diff options
Diffstat (limited to 'Core/EM/UsbRecovery/XhciPEI/XhciPei.c')
-rw-r--r-- | Core/EM/UsbRecovery/XhciPEI/XhciPei.c | 2314 |
1 files changed, 2314 insertions, 0 deletions
diff --git a/Core/EM/UsbRecovery/XhciPEI/XhciPei.c b/Core/EM/UsbRecovery/XhciPEI/XhciPei.c new file mode 100644 index 0000000..ae926ca --- /dev/null +++ b/Core/EM/UsbRecovery/XhciPEI/XhciPei.c @@ -0,0 +1,2314 @@ +//********************************************************************** +//********************************************************************** +//** ** +//** (C)Copyright 1985-2010, American Megatrends, Inc. ** +//** ** +//** All Rights Reserved. ** +//** ** +//** 5555 Oakbrook Pkwy, Suite 200, Norcross, GA 30093 ** +//** ** +//** Phone: (770)-246-8600 ** +//** ** +//********************************************************************** +//********************************************************************** +//********************************************************************** +// +// $Header: /Alaska/SOURCE/Modules/USBRecovery/XhciPei.c 10 11/24/12 5:45a Ryanchou $ +// +// $Revision: 10 $ +// +// $Date: 11/24/12 5:45a $ +// +//********************************************************************** +// Revision History +// ---------------- +// $Log: /Alaska/SOURCE/Modules/USBRecovery/XhciPei.c $ +// +// 10 11/24/12 5:45a Ryanchou +// [TAG] EIP103990 +// [Category] Improvement +// [Description] Synchronized with USB PEI module 4.6.3_USB_08.10.24. +// [Files] EhciPei.c, EhciPei.h, OhciPei.c, OhciPei.h, UhcPeim.c, +// BotPeim.c, BotPeim.h, PeiAtapi.c, UsbBotPeim.c, UsbBotPeim.h, +// HubPeim.c, UsbPeim.c, XhciPei.c, XhciPei.h, HubPeim.h, PeiUsbLib.c, +// PeiUsbLib.h, UsbPeim.h +// +// 9 4/24/12 10:16p Wilsonlee +// [TAG] EIP75547 +// [Category] Bug Fix +// [Severity] Normal +// [Symptom] Xhci recovery funtion failed when SS devices on USB 3.0 +// port. +// [RootCause] The SS device connected to USB2 port. +// [Solution] Reset the USB2 port when initial xhci controller, then the +// SS device reconnect to USB3 port. +// [Files] XhciPei.c, XhciPei.h +// +// 8 4/12/11 12:00a Rameshr +// [TAG]- EIP 57444 +// [Category]-IMPROVEMENT +// [Description]- PI1.1 Support. +// [Files]- OhciPeiboard.c,Xhcipei.c, Peiusblib.c +// +// 7 1/18/11 1:08a Ryanchou +// [TAG] EIP47931 +// [Category] Improvement +// [Description] Added USB 3.0 hub support. +// [Files] EhciPei.c, EhciPei.h, HubPeim.c, HubPeim.h, OhciPei.c, +// OhciPei.h, UhcPeim.c, UhcPeim.h, usb.h, UsbHostController.h, +// UsbIoPeim.c, UsbPeim.c, UsbPeim.h, XhciPei.c, XhciPei.h +// +// 6 1/17/11 7:08a Ryanchou +// [TAG] EIP48013 +// [Category] Improvement +// [Description] Use 32 or 64 byte Context data structures dynamically. +// [Files] XhciPei.c, XhciPei.h +// +// 5 10/27/10 11:27a Olegi +// [TAG] EIP46147 +// [Category] Bug Fix +// +// 4 10/20/10 10:44a Olegi +// [TAG] EIP46492 +// [Category] Bug Fix +// [Severity] Important +// [Symptom] TdSize variable should be declared as a 32bit integer +// instead of a 8bit one. +// +// 3 10/14/10 3:57p Olegi +// Added code that check for PCI device presence. +// +// 2 10/12/10 5:57p Olegi +// Added (UINTN) typecast when converting pointers to UINT64. Without it +// pointers that have BIT31 will have bits 32..63 set. +// +// 1 10/11/10 4:53p Olegi +// +//********************************************************************** + +//<AMI_FHDR_START> +//---------------------------------------------------------------------------- +// +// Name: XhciPei.c +// +// Description: +// This file is the main source file for the xHCI PEI USB recovery module. +// Its entry point at XhciPeiUsbEntryPoint will be executed from the +// UsbRecoveryInitialize INIT_LIST. +// +//---------------------------------------------------------------------------- +//<AMI_FHDR_END> + +#include <AmiPeiLib.h> +#include "XhciPei.h" +#include <PPI\stall.h> +#include "token.h" +#include "pci.h" +#include "usbpeim.h" + +PCI_BUS_DEV_FUNCTION gXhciControllerPciTable[] = {PEI_XHCI_CONTROLLER_PCI_ADDRESS {0,0,0}}; +UINT16 gXhciControllerCount = \ + sizeof(gXhciControllerPciTable) / sizeof(PCI_BUS_DEV_FUNCTION) - 1; + +PCI_DEV_REGISTER_VALUE gPeiXhciInitPciTable[] = {PEI_XHCI_CONTROLLER_PCI_REGISTER_VALUES {0,0,0,0,0,0,0}}; +UINT16 gPeiXhciInitPciTableCount = \ + sizeof(gPeiXhciInitPciTable) / sizeof(PCI_DEV_REGISTER_VALUE) - 1; + +UINT8 gSlotBeingConfigured; + +#ifndef PI_SPECIFICATION_VERSION //old Core +extern EFI_STATUS PciCfgModify( +IN CONST EFI_PEI_SERVICES **PeiServices, +IN EFI_PEI_PCI_CFG_PPI_WIDTH Width, +IN UINT64 Address, +IN UINTN SetBits, +IN UINTN ClearBits +); +#endif + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: XhciPeiUsbEntryPoint +// +// Description: +// This is the entry point into the XHCI initialization. +// +// Input: +// IN EFI_FFS_FILE_HEADER *FfsHeader +// -- EFI_FFS_FILE_HEADER pointer +// IN EFI_PEI_SERVICES **PeiServices +// -- EFI_PEI_SERVICES pointer +// +// Output: +// EFI_STATUS (Return Value) +// = EFI_SUCCESS on successful completion or valid EFI error code +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS XhciPeiUsbEntryPoint ( + IN EFI_FFS_FILE_HEADER *FfsHeader, + IN EFI_PEI_SERVICES **PeiServices ) +{ + + EFI_STATUS Status; + EFI_PEI_STALL_PPI *StallPpi; + UINT32 XhciBaseAddress = PEI_XHCI_MMIOBASE; + EFI_PHYSICAL_ADDRESS TempPtr; + USB3_CONTROLLER *Usb3Hc; + UINTN MemPages; + UINT8 ControllerIndex; + UINT8 PciDevIndex; + UINT8 CmdRegisterValue = 6; + + //------------------------------------------- + // Initialize the EFI_PEI_STALL_PPI interface + //------------------------------------------- + Status = (**PeiServices).LocatePpi( PeiServices, &gPeiStallPpiGuid, + 0, NULL, &StallPpi ); + if ( EFI_ERROR( Status ) ) { + return EFI_UNSUPPORTED; + } + + // Program PCI registers of the host controller and other relevant PCI + // devices (e.g. bridges that enable this host). + + for (PciDevIndex = 0; PciDevIndex < gPeiXhciInitPciTableCount; PciDevIndex++) { + + EFI_PEI_PCI_CFG_PPI_WIDTH Width; + + switch (gPeiXhciInitPciTable[PciDevIndex].Size) { + case 8: Width = EfiPeiPciCfgWidthUint8; break; + case 16: Width = EfiPeiPciCfgWidthUint16; break; + case 32: Width = EfiPeiPciCfgWidthUint32; break; + case 64: Width = EfiPeiPciCfgWidthUint64; break; + default: continue; + } + + Status = PciCfgModify( + PeiServices, + Width, + PEI_PCI_CFG_ADDRESS( + gPeiXhciInitPciTable[PciDevIndex].Bus, + gPeiXhciInitPciTable[PciDevIndex].Device, + gPeiXhciInitPciTable[PciDevIndex].Function, + gPeiXhciInitPciTable[PciDevIndex].Register + ), + gPeiXhciInitPciTable[PciDevIndex].SetBits, + gPeiXhciInitPciTable[PciDevIndex].ClearBits); + } + + for (ControllerIndex = 0; ControllerIndex < gXhciControllerCount; + ControllerIndex++, XhciBaseAddress+=0x10000) + { + UINT16 Vid; + UINT16 Did; + + // Get VID/DID, see if controller is visible on PCI + (*PeiServices)->PciCfg->Read(PeiServices,(*PeiServices)->PciCfg, + EfiPeiPciCfgWidthUint16, XHCI_PCI_ADDRESS( ControllerIndex, PCI_VID ),&Vid); + if (Vid == 0xffff) continue; // Controller not present + + (*PeiServices)->PciCfg->Read(PeiServices,(*PeiServices)->PciCfg, + EfiPeiPciCfgWidthUint16, XHCI_PCI_ADDRESS( ControllerIndex, PCI_DID ), &Did); + + //---------------------------------------------------------- + // Allocate USB3_CONTROLLER object that holds all necessary + // information for the Host Controller operational registers + // for each controller. Initialze the controller and setup + // data structures, get it ready for operation. + //---------------------------------------------------------- + MemPages = sizeof(USB3_CONTROLLER) / 0x1000 + 1; + Status = (**PeiServices).AllocatePages(PeiServices, + EfiConventionalMemory, + MemPages, + &TempPtr); + if (EFI_ERROR(Status)) return EFI_OUT_OF_RESOURCES; + + (**PeiServices).SetMem((VOID*)TempPtr, MemPages*4096, 0); // Clear allocated memory + + Usb3Hc = (USB3_CONTROLLER*)(UINTN)TempPtr; + + //---------------------------------------------------------- + // USB3 controller data area is allocated, start stuff it in + // with the useful filling in with the useful data. + //---------------------------------------------------------- + + // Program BAR + (*PeiServices)->PciCfg->Write(PeiServices,(*PeiServices)->PciCfg, + EfiPeiPciCfgWidthUint32, + XHCI_PCI_ADDRESS( ControllerIndex, PCI_BAR0 ), + &XhciBaseAddress + ); + Usb3Hc->CapRegs = (XHCI_HC_CAP_REGS*)(UINTN)XhciBaseAddress; + + // Enable MMIO access and BM + (*PeiServices)->PciCfg->Write(PeiServices,(*PeiServices)->PciCfg, + EfiPeiPciCfgWidthUint8, + XHCI_PCI_ADDRESS( ControllerIndex, PCI_CMD ), + &CmdRegisterValue + ); + + (UINT8)(UINTN)Usb3Hc->CapRegs &= ~(0x7F); // Clear attributes + + Usb3Hc->Did = Did; + Usb3Hc->Vid = Vid; + Usb3Hc->Access64 = Usb3Hc->CapRegs->HcParams.Ac64; + Usb3Hc->HciVersion = Usb3Hc->CapRegs->HciVersion; + Usb3Hc->MaxPorts = Usb3Hc->CapRegs->HcParams1.MaxPorts; + Usb3Hc->OpRegs = (XHCI_HC_OP_REGS*)((UINTN)Usb3Hc->CapRegs + Usb3Hc->CapRegs->CapLength); + Usb3Hc->PageSize4K = Usb3Hc->OpRegs->PageSize; + Usb3Hc->ContextSize = 0x20 << Usb3Hc->CapRegs->HcParams.Csz; + Usb3Hc->MaxIntrs = Usb3Hc->CapRegs->HcParams1.MaxIntrs; // Get maximum number of interrupters + Usb3Hc->DbOffset = Usb3Hc->CapRegs->DbOff; // Doorbell offset + Usb3Hc->MaxSlots = PEI_XHCI_MAX_SLOTS; + + Usb3Hc->InputContext = (VOID*)&Usb3Hc->InpCtx; + Usb3Hc->XfrRings = Usb3Hc->XfrRing; + Usb3Hc->XfrTrbs = (UINTN)Usb3Hc->XfrTrb; + Usb3Hc->DeviceContext = (VOID*)Usb3Hc->DevCtx; + + XhciExtCapParser(PeiServices, Usb3Hc); //(EIP75547+) + + PEI_TRACE ((-1, PeiServices, "USB recovery xHCI[%d] controller initialization details:\n", ControllerIndex)); + PEI_TRACE ((-1, PeiServices, " PCI location: B%x/D%x/F%x, VID:DID = %x:%x, BAR0 = %x\n", + gXhciControllerPciTable[ControllerIndex].Bus, + gXhciControllerPciTable[ControllerIndex].Device, + gXhciControllerPciTable[ControllerIndex].Function, + Usb3Hc->Vid, + Usb3Hc->Did, + Usb3Hc->CapRegs + )); + PEI_TRACE((-1, PeiServices, " MaxSlots = %x, InputCtx %x, Device Ctx %x\n", + Usb3Hc->MaxSlots, (UINT8*)Usb3Hc->InputContext, (UINT8*)Usb3Hc->DeviceContext)); + + Usb3Hc->PeiServices = PeiServices; + Usb3Hc->CpuIoPpi = (*PeiServices)->CpuIo; + Usb3Hc->StallPpi = StallPpi; + Usb3Hc->PciCfgPpi = (*PeiServices)->PciCfg; + + // Initialize the xHCI Controller for operation + Status = XhciInitHC(PeiServices, Usb3Hc, ControllerIndex); + PEI_ASSERT(PeiServices, Status == EFI_SUCCESS); + if (EFI_ERROR(Status)) return Status; + + // Setup PPI entry point + Usb3Hc->UsbHostControllerPpi.ControlTransfer = XhciHcControlTransfer; + Usb3Hc->UsbHostControllerPpi.BulkTransfer = XhciHcBulkTransfer; + Usb3Hc->UsbHostControllerPpi.GetRootHubPortNumber = XhciHcGetRootHubPortNumber; + Usb3Hc->UsbHostControllerPpi.GetRootHubPortStatus = XhciHcGetRootHubPortStatus; + Usb3Hc->UsbHostControllerPpi.SetRootHubPortFeature = XhciHcSetRootHubPortFeature; + Usb3Hc->UsbHostControllerPpi.ClearRootHubPortFeature = XhciHcClearRootHubPortFeature; + Usb3Hc->UsbHostControllerPpi.PreConfigureDevice = XhciHcPreConfigureDevice; + Usb3Hc->UsbHostControllerPpi.EnableEndpoints = XhciEnableEndpoints; + + Usb3Hc->PpiDescriptor.Flags =(EFI_PEI_PPI_DESCRIPTOR_PPI |EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST); + Usb3Hc->PpiDescriptor.Guid = &gPeiUsbHostControllerPpiGuid; + Usb3Hc->PpiDescriptor.Ppi = &Usb3Hc->UsbHostControllerPpi; + + Status = (**PeiServices).InstallPpi(PeiServices, &Usb3Hc->PpiDescriptor); + if (EFI_ERROR(Status)) return EFI_NOT_FOUND; + + } + + return EFI_SUCCESS; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: XhciInitHC +// +// Description: +// This function initializes xHCI controller registers and starts it. +// +// Input: +// EFI_PEI_SERVICES **PeiServices +// -- PEI_SERVICES pointer +// USB3_CONTROLLER *Usb3Hc +// -- XHCI controller data structure pointer +// UINT8 ControllerIndex +// -- 0-based index of the controller to be initialized +// +// Output: +// EFI_STATUS (Return Value) +// = EFI_SUCCESS on successful initialization completion +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS +XhciInitHC( + EFI_PEI_SERVICES **PeiServices, + USB3_CONTROLLER *Usb3Hc, + UINT8 ControllerIndex +) +{ + XHCI_INTERRUPTER_REGS *Interrupter; + XHCI_ER_SEGMENT_ENTRY *Erst0Entry; + BOOLEAN PpSet = FALSE; + UINT8 PortNumber; + volatile XHCI_PORTSC *PortSC; + UINT32 CurrentPortOffset = 0; + UINT32 i; + + // Wait controller ready + for (i = 0; i < 100; i++) { + if (Usb3Hc->OpRegs->UsbSts.Field.Cnr == 0) break; + XHCI_FIXED_DELAY_MS(Usb3Hc, 1); + } + PEI_ASSERT(PeiServices, Usb3Hc->OpRegs->UsbSts.Field.Cnr == 0); + if (Usb3Hc->OpRegs->UsbSts.Field.Cnr) return EFI_DEVICE_ERROR; + + // Check if the xHC is halted + if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted == 0) + { + Usb3Hc->OpRegs->UsbCmd.RunStop = 0; + // The xHC should halt within 16 ms. Section 5.4.1.1 + for (i = 0; i < 16; i++) { + XHCI_FIXED_DELAY_MS(Usb3Hc, 1); + if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted) break; + } + PEI_ASSERT(PeiServices, Usb3Hc->OpRegs->UsbSts.Field.HcHalted); + if (Usb3Hc->OpRegs->UsbSts.Field.HcHalted == 0) return EFI_DEVICE_ERROR; + } + + // Reset controller + Usb3Hc->OpRegs->UsbCmd.HcRst = 1; + for (i = 0; i < 400; i++) { + XHCI_FIXED_DELAY_MS(Usb3Hc, 1); + if (Usb3Hc->OpRegs->UsbCmd.HcRst == 0) break; + } + + PEI_ASSERT(PeiServices, Usb3Hc->OpRegs->UsbCmd.HcRst == 0); + if (Usb3Hc->OpRegs->UsbCmd.HcRst) return EFI_DEVICE_ERROR; // Controller can not be reset + + // Wait controller ready + for (i = 0; i < 100; i++) { + if (Usb3Hc->OpRegs->UsbSts.Field.Cnr == 0) break; + XHCI_FIXED_DELAY_MS(Usb3Hc, 1); + } + PEI_ASSERT(PeiServices, Usb3Hc->OpRegs->UsbSts.Field.Cnr == 0); + if (Usb3Hc->OpRegs->UsbSts.Field.Cnr) return EFI_DEVICE_ERROR; + + Usb3Hc->RtRegs = (XHCI_HC_RT_REGS*)((UINTN)Usb3Hc->CapRegs + Usb3Hc->CapRegs->RtsOff); + PEI_TRACE ((-1, PeiServices, "PEI_XHCI: RT registers are at %x\n", Usb3Hc->RtRegs)); + + Usb3Hc->OpRegs->Config = Usb3Hc->MaxSlots; // Max device slots enabled + Usb3Hc->DcbaaPtr = (XHCI_DCBAA*)Usb3Hc->Dcbaa; + + XhciMmio64Write(Usb3Hc, (UINTN)&Usb3Hc->OpRegs->DcbAap, (UINT64)(UINTN)Usb3Hc->DcbaaPtr); + + // Define the Command Ring Dequeue Pointer by programming the Command Ring + // Control Register (5.4.5) with a 64-bit address pointing to the starting + // address of the first TRB of the Command Ring. + + // Initialize Command Ring Segment: Size TRBS_PER_SEGMENT*16, 64 Bytes aligned + XhciInitRing(&Usb3Hc->CmdRing, (UINTN)Usb3Hc->CommandRing, TRBS_PER_SEGMENT, TRUE); + PEI_TRACE ((-1, PeiServices, "CMD Ring is at %x\n", (UINTN)&Usb3Hc->CmdRing)); + + // Write CRCR HC register with the allocated address. Set Ring Cycle State to 1. + XhciMmio64Write(Usb3Hc, (UINTN)&Usb3Hc->OpRegs->Crcr, + (UINT64)(UINTN)Usb3Hc->CmdRing.Base + CRCR_RING_CYCLE_STATE); + + // Initialize and assign Event Ring + XhciInitRing(&Usb3Hc->EvtRing, (UINTN)Usb3Hc->EventRing, TRBS_PER_SEGMENT, FALSE); + PEI_TRACE ((-1, PeiServices, "EVT Ring is at %x\n", (UINTN)&Usb3Hc->EvtRing)); + + // NOTE: This driver supports one Interrupter, hence it uses + // one Event Ring segment with TRBS_PER_SEGMENT TRBs in it. + + // Initialize ERST[0] + Erst0Entry = &Usb3Hc->Erst; + Erst0Entry->RsBase = (UINT64)(UINTN)Usb3Hc->EvtRing.Base; + Erst0Entry->RsSize = TRBS_PER_SEGMENT; + + Interrupter = Usb3Hc->RtRegs->IntRegs; + + // Initialize Interrupter fields + Interrupter->Erstz = 1; // # of segments + // ER dequeue pointer + XhciMmio64Write(Usb3Hc, (UINTN)&Interrupter->Erdp, (UINT64)(UINTN)Usb3Hc->EvtRing.QueuePtr); + // Seg Table location + XhciMmio64Write(Usb3Hc, (UINTN)&Interrupter->Erstba, (UINT64)(UINTN)Erst0Entry); + Interrupter->IMod = XHCI_IMODI; // Max interrupt rate + Interrupter->IMan |= 2; // Enable interrupt + + PEI_TRACE((-1, PeiServices, "Transfer Rings structures start at %x\n", Usb3Hc->XfrRings)); + + // Set PortPower unless PowerPortControl indicates otherwise + if (Usb3Hc->CapRegs->HcParams.Ppc != 0) { + for (PortNumber = 0; PortNumber<Usb3Hc->MaxPorts; + PortNumber++, CurrentPortOffset+=0x10) { + PortSC = (XHCI_PORTSC*)((UINTN)Usb3Hc->OpRegs + + XHCI_PORTSC_OFFSET + CurrentPortOffset); + if (PortSC->Field.Pp == 0) { + PortSC->Field.Pp = 1; // Set port power + PpSet = TRUE; + } + } + if (PpSet) XHCI_FIXED_DELAY_MS(Usb3Hc, 20); // Wait for 20 ms, Section 5.4.8 + } + + Usb3Hc->OpRegs->UsbCmd.Inte = 1; + Usb3Hc->OpRegs->UsbCmd.RunStop = 1; + + XHCI_FIXED_DELAY_MS(Usb3Hc, 100); + + XhciResetUsb2Port(PeiServices, Usb3Hc); //(EIP75547+) + + return EFI_SUCCESS; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: XhciHcPreConfigureDevice +// +// Description: +// This function does preliminary device initialization: enables slot and +// sets the address. +// +// Output: +// EFI_STATUS (Return Value) +// = EFI_SUCCESS on successful pre-configuration completion +// = EFI_DEVICE_ERROR on error +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS +XhciHcPreConfigureDevice( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *This, + IN UINT8 Port, + IN UINT8 DeviceSpeed, + IN UINT16 TransactionTranslator +) +{ + USB3_CONTROLLER *Usb3Hc = PEI_RECOVERY_USB_XHCI_DEV_FROM_THIS(This); + UINT8 *Device; + UINT8 *ParentHubDevice; + TRB_RING *XfrRing; + UINT8 Speed; + static UINT16 aMaxPacketSize[5] = {0, 8, 8, 64, 512}; + EFI_STATUS Status; + UINT8 SlotId; + XHCI_INPUT_CONTROL_CONTEXT *CtlCtx; + XHCI_SLOT_CONTEXT *SlotCtx; + XHCI_SLOT_CONTEXT *ParentHubSlotCtx; + XHCI_EP_CONTEXT *Ep0Ctx; + UINT8 ParentHubSlotId; + UINT8 i; + +// XHCI_FIXED_DELAY_MS(Usb3Hc, 1000); + + // Obtain device slot using Enable Slot command, 4.3.2, 4.6.3 + Status = XhciExecuteCommand(PeiServices, Usb3Hc, XhciTEnableSlotCmd, &SlotId); + PEI_ASSERT(PeiServices, Status == EFI_SUCCESS); + PEI_ASSERT(PeiServices, SlotId != 0); + if (Status != EFI_SUCCESS) return Status; + + Device = (UINT8*)XhciGetDeviceContext(Usb3Hc, SlotId); + + // Update DCBAA with the new device pointer (index = SlotId) + Usb3Hc->DcbaaPtr->DevCntxtAddr[SlotId-1] = (UINT64)(UINTN)Device; + PEI_TRACE((-1, PeiServices, "PEI_XHCI: Slot[%d] enabled, device context at %x\n", SlotId, Device)); + + // Initialize data structures associated with the slot 4.3.3 + + // Zero the InputContext and DeviceContext + (**PeiServices).SetMem((UINT8*)Usb3Hc->InputContext, XHCI_INPUT_CONTEXT_ENTRIES * Usb3Hc->ContextSize, 0); + (**PeiServices).SetMem(Device, XHCI_DEVICE_CONTEXT_ENTRIES * Usb3Hc->ContextSize, 0); + + // Initialize the Input Control Context of the Input Context + // by setting the A0 and A1 flags to 1 + + CtlCtx = (XHCI_INPUT_CONTROL_CONTEXT*)XhciGetContextEntry(Usb3Hc, (UINT8*)Usb3Hc->InputContext, 0); + CtlCtx->AddContextFlags = BIT0 + BIT1; + + // Initialize the Input Slot Context data structure + SlotCtx = (XHCI_SLOT_CONTEXT*)XhciGetContextEntry(Usb3Hc, (UINT8*)Usb3Hc->InputContext, 1); + SlotCtx->RouteString = 0; + SlotCtx->ContextEntries = 1; + if (TransactionTranslator == 0) { + SlotCtx->RootHubPort = Port; + } else { + Status = GetSlotId(Usb3Hc->DeviceMap, &ParentHubSlotId, (UINT8)(TransactionTranslator & 0xF)); + PEI_ASSERT(PeiServices, Status == EFI_SUCCESS); + ParentHubDevice = (UINT8*)XhciGetDeviceContext(Usb3Hc, ParentHubSlotId); + ParentHubSlotCtx = (XHCI_SLOT_CONTEXT*)XhciGetContextEntry(Usb3Hc, ParentHubDevice, 0); + SlotCtx->RootHubPort = ParentHubSlotCtx->RootHubPort; + } + + switch (DeviceSpeed) { + case USB_HIGH_SPEED_DEVICE: Speed = XHCI_DEVSPEED_HIGH; break; + case USB_SLOW_SPEED_DEVICE: Speed = XHCI_DEVSPEED_LOW; break; + case USB_FULL_SPEED_DEVICE: Speed = XHCI_DEVSPEED_FULL; break; + case USB_SUPER_SPEED_DEVICE: Speed = XHCI_DEVSPEED_SUPER; + } + + SlotCtx->Speed = Speed; + + XfrRing = XhciInitXfrRing(Usb3Hc, SlotId, 0); + + // Initialize the Input default control Endpoint 0 Context + Ep0Ctx = (XHCI_EP_CONTEXT*)XhciGetContextEntry(Usb3Hc, (UINT8*)Usb3Hc->InputContext, 2); + Ep0Ctx->EpType = XHCI_EPTYPE_CTL; + Ep0Ctx->MaxPacketSize = aMaxPacketSize[Speed]; + Ep0Ctx->TrDequeuePtr = (UINT64)(UINTN)XfrRing->Base + 1; + Ep0Ctx->AvgTrbLength = 8; + Ep0Ctx->ErrorCount = 3; + + // Initialize Route String and TT fields + if (TransactionTranslator != 0) { + if (ParentHubSlotCtx->Speed == XHCI_DEVSPEED_SUPER) { + for (i = 0; i < 5; i++) { + if (((ParentHubSlotCtx->RouteString >> (i << 2)) & 0xF) == 0) { + break; + } + } + SlotCtx->RouteString = ParentHubSlotCtx->RouteString | (Port << (i << 2)); + } else { + // Update TT fields in the Slot context for LS/FS device connected to HS hub + if (SlotCtx->Speed == XHCI_DEVSPEED_FULL || SlotCtx->Speed == XHCI_DEVSPEED_LOW) { + if(ParentHubSlotCtx->Speed == XHCI_DEVSPEED_HIGH) { + SlotCtx->TtHubSlotId = ParentHubSlotId; + SlotCtx->TtPortNumber = (UINT8)(TransactionTranslator >> 7); + SlotCtx->MultiTT = ParentHubSlotCtx->MultiTT; + } else { + SlotCtx->TtHubSlotId = ParentHubSlotCtx->TtHubSlotId; + SlotCtx->TtPortNumber = ParentHubSlotCtx->TtPortNumber; + SlotCtx->MultiTT = ParentHubSlotCtx->MultiTT; + } + } + } + } + + // Assign a new address 4.3.4, 4.6.5 + Status = XhciExecuteCommand(PeiServices, Usb3Hc, XhciTAddressDeviceCmd, &SlotId); + PEI_ASSERT(PeiServices, Status == EFI_SUCCESS); + if (Status != EFI_SUCCESS) { + XhciExecuteCommand(PeiServices, Usb3Hc, XhciTDisableSlotCmd, &SlotId); + Usb3Hc->DcbaaPtr->DevCntxtAddr[SlotId-1] = 0; + return Status; + } + PEI_TRACE((-1, PeiServices, "PEI_XHCI: new device address %d\n", ((XHCI_SLOT_CONTEXT*)Device)->DevAddr)); + + gSlotBeingConfigured = SlotId; // Valid from now til SetAddress + + return EFI_SUCCESS; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: UpdateDeviceMap +// +// Description: +// This function updates SlotId<->Address mapping table. +// +// Output: +// EFI_STATUS (Return Value) +// = EFI_SUCCESS if update is successful +// = EFI_NOT_FOUND if there is no room for a new entry in the map +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS +UpdateDeviceMap( + PEI_XHCI_SLOTADDR_MAP *DeviceMap, + UINT8 SlotId, + UINT8 DevAddr +) +{ + UINT8 i; + PEI_XHCI_SLOTADDR_MAP *Map = DeviceMap; + + for (i = 0; i < PEI_XHCI_MAX_SLOTS; i++, Map++) { + if (Map->SlotId == 0) { + Map->SlotId = SlotId; + Map->DevAddr = DevAddr; + return EFI_SUCCESS; + } + } + return EFI_NOT_FOUND; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: GetSlotId +// +// Description: +// This function retrieves SlotId from the Slot<->Address mapping table. +// +// Output: +// SlotId variable is updated +// EFI_STATUS (Return Value) +// = EFI_SUCCESS if update is successful +// = EFI_NOT_FOUND if the requested Slot is not found in the mapping table +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS +GetSlotId( + PEI_XHCI_SLOTADDR_MAP *DeviceMap, + UINT8 *SlotId, + UINT8 DevAddr +) +{ + UINT8 i; + PEI_XHCI_SLOTADDR_MAP *Map = DeviceMap; + + for (i = 0; i < PEI_XHCI_MAX_SLOTS; i++, Map++) { + if (Map->DevAddr == DevAddr) { + *SlotId = Map->SlotId; + return EFI_SUCCESS; + } + } + return EFI_NOT_FOUND; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XhciClearStalledEp +// +// Description: +// This function is called to restart endpoint. After Endpoint STALLs, it +// transitions from Halted to Stopped state. It is restored back to Running +// state by moving the endpoint ring dequeue pointer past the failed control +// transfer with a Set TR Dequeue Pointer. Then it is restarted by ringing the +// doorbell. Alternatively endpint is restarted using Configure Endpoint command. +// +// Input: +// Stalled EP data - SlotId and DCI +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS +XhciClearStalledEp( + EFI_PEI_SERVICES **PeiServices, + USB3_CONTROLLER *Usb3Hc, + UINT8 SlotId, + UINT8 Dci +) +{ + UINT16 EpInfo; + TRB_RING *XfrRing; + EFI_STATUS Status; + XHCI_SET_TRPTR_CMD_TRB Trb; + + // Reset stalled endpoint + EpInfo = (Dci << 8) + SlotId; + Status = XhciExecuteCommand(PeiServices, Usb3Hc, XhciTResetEndpointCmd, &EpInfo); + PEI_ASSERT(PeiServices, Status == EFI_SUCCESS); + + XfrRing = XhciGetXfrRing(Usb3Hc, SlotId, Dci-1); + + Trb.TrPointer = (UINT64)((UINTN)XfrRing->QueuePtr + XfrRing->CycleBit); // Set up DCS + Trb.EndpointId = Dci; + Trb.SlotId = SlotId; + + Status = XhciExecuteCommand(PeiServices, Usb3Hc, XhciTSetTRDequeuePointerCmd, &Trb); + PEI_ASSERT(PeiServices, Status == EFI_SUCCESS); + + return Status; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XhciUpdateEp0MaxPacket +// +// Description: +// This function verifies the MaxPacket size of the control pipe. If it does +// not match the one received as a part of GET_DESCRIPTOR, then this function +// updates the MaxPacket data in DeviceContext and HC is notified via +// EvaluateContext command. +// +// Input: +// Usb3Hc Pointer to the HC structure +// Device Evaluated device context pointer +// SlotId Device context index in DCBAA +// Endp0MaxPacket Max packet size obtained from the device +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +VOID +XhciUpdateEp0MaxPacket( + EFI_PEI_SERVICES **PeiServices, + USB3_CONTROLLER *Usb3Hc, + UINT8 SlotId, + UINT8 Endp0MaxPacket +) +{ + UINT8 Status; + UINT8 *DevCtx; + XHCI_INPUT_CONTROL_CONTEXT *CtlCtx; + XHCI_SLOT_CONTEXT *SlotCtx; + XHCI_EP_CONTEXT *EpCtx; + + DevCtx = (UINT8*)XhciGetDeviceContext(Usb3Hc, SlotId); + + SlotCtx = (XHCI_SLOT_CONTEXT*)XhciGetContextEntry(Usb3Hc, DevCtx, 0); + if (SlotCtx->Speed != XHCI_DEVSPEED_FULL) return; + + EpCtx = (XHCI_EP_CONTEXT*)XhciGetContextEntry(Usb3Hc, DevCtx, 1); + if (EpCtx->MaxPacketSize == Endp0MaxPacket) return; + + // Prepare input context for EvaluateContext comand + (**PeiServices).SetMem((UINT8*)Usb3Hc->InputContext, XHCI_INPUT_CONTEXT_ENTRIES * Usb3Hc->ContextSize, 0); + + CtlCtx = (XHCI_INPUT_CONTROL_CONTEXT*)XhciGetContextEntry(Usb3Hc, (UINT8*)Usb3Hc->InputContext, 0); + CtlCtx->AddContextFlags = BIT1; + + EpCtx = (XHCI_EP_CONTEXT*)XhciGetContextEntry(Usb3Hc, (UINT8*)Usb3Hc->InputContext, 2); + EpCtx->MaxPacketSize = Endp0MaxPacket; + + Status = XhciExecuteCommand(PeiServices, Usb3Hc, XhciTEvaluateContextCmd, &SlotId); + PEI_ASSERT(PeiServices, Status == EFI_SUCCESS); +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: XhciHcControlTransfer +// +// Description: +// This function intiates a USB control transfer and waits on it to +// complete. +// +// Input: +// IN EFI_PEI_SERVICES **PeiServices +// -- EFI_PEI_SERVICES pointer +// IN PEI_USB_HOST_CONTROLLER_PPI *This +// -- PEI_USB_HOST_CONTROLLER_PPI pointer +// IN UINT8 bDeviceAddress +// -- USB address of the device for which the control +// transfer is to be issued +// IN UINT8 DeviceType +// -- Not used +// IN UINT8 MaximumPacketLength +// -- Maximum number of bytes that can be sent to or +// received from the endpoint in a single data packet +// IN EFI_USB_DEVICE_REQUEST *Request +// -- EFI_USB_DEVICE_REQUEST pointer +// IN EFI_USB_DATA_DIRECTION TransferDirection +// -- Direction of transfer +// OPTIONAL IN OUT VOID *DataBuffer +// -- Pointer to source or destination buffer +// OPTIONAL IN OUT UINTN *DataLength +// -- Length of buffer +// IN UINTN TimeOut +// -- Not used +// OUT UINT32 *TransferResult +// -- Not used +// +// Output: +// EFI_STATUS (Return Value) +// = EFI_SUCCESS on successful completion +// or valid EFI error code +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS XhciHcControlTransfer ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *This, + IN UINT8 DeviceAddress, + IN UINT8 DeviceSpeed, + IN UINT8 MaximumPacketLength, + IN UINT16 TransactionTranslator OPTIONAL, + IN EFI_USB_DEVICE_REQUEST *Request, + IN EFI_USB_DATA_DIRECTION TransferDirection, + IN OUT VOID *Data OPTIONAL, + IN OUT UINTN *DataLength OPTIONAL, + IN UINTN TimeOut, + OUT UINT32 *TransferResult ) +{ + USB3_CONTROLLER *Usb3Hc = PEI_RECOVERY_USB_XHCI_DEV_FROM_THIS(This); + XHCI_TRB *Trb; + volatile UINT32 *Doorbell; + UINT8 SlotId; + UINT8 CompletionCode; + UINT8 Status; + TRB_RING *XfrRing; + UINT16 Rq = ((UINT16)Request->Request << 8) + Request->RequestType; + + // Skip SET_ADDRESS request + if (Request->RequestType == USB_DEV_SET_ADDRESS_REQ_TYPE && + Request->Request == USB_DEV_SET_ADDRESS) + { + Status = UpdateDeviceMap(Usb3Hc->DeviceMap, gSlotBeingConfigured, (UINT8)Request->Value); + PEI_ASSERT(PeiServices, Status == EFI_SUCCESS); + return EFI_SUCCESS; + } + + if (DeviceAddress == 0) { + SlotId = gSlotBeingConfigured; + } else { + Status = GetSlotId(Usb3Hc->DeviceMap, &SlotId, DeviceAddress); + PEI_ASSERT(PeiServices, Status == EFI_SUCCESS); + } + + // Insert Setup, Data(if needed), and Status TRBs into the transfer ring + XfrRing = XhciGetXfrRing(Usb3Hc, SlotId, 0); + + // Setup TRB + Trb = XhciAdvanceEnqueuePtr(XfrRing); + Trb->TrbType = XhciTSetupStage; + ((XHCI_SETUP_XFR_TRB*)Trb)->Idt = 1; + *(UINT16*)&((XHCI_SETUP_XFR_TRB*)Trb)->bmRequestType = Rq; + ((XHCI_SETUP_XFR_TRB*)Trb)->wValue = Request->Value; + ((XHCI_SETUP_XFR_TRB*)Trb)->wIndex = Request->Index; + ((XHCI_SETUP_XFR_TRB*)Trb)->wLength = (UINT16)(*DataLength); + ((XHCI_SETUP_XFR_TRB*)Trb)->XferLength = 8; + + if (Usb3Hc->HciVersion == 0x100) { + if (*DataLength != 0) { + if ((Rq & BIT7) != 0) { + ((XHCI_SETUP_XFR_TRB*)Trb)->Trt = XHCI_XFER_TYPE_DATA_IN; + } else { + ((XHCI_SETUP_XFR_TRB*)Trb)->Trt = XHCI_XFER_TYPE_DATA_OUT; + } + } else { + ((XHCI_SETUP_XFR_TRB*)Trb)->Trt = XHCI_XFER_TYPE_NO_DATA; + } + } + + // Data TRB + if (*DataLength != 0) { + Trb = XhciAdvanceEnqueuePtr(XfrRing); + Trb->TrbType = XhciTDataStage; + ((XHCI_DATA_XFR_TRB*)Trb)->Dir = ((Rq & BIT7) != 0)? 1 : 0; + ((XHCI_DATA_XFR_TRB*)Trb)->XferLength = *DataLength; + ((XHCI_DATA_XFR_TRB*)Trb)->DataBuffer = (UINT64)(UINTN)Data; + } + + // Status TRB + Trb = XhciAdvanceEnqueuePtr(XfrRing); + Trb->TrbType = XhciTStatusStage; + ((XHCI_STATUS_XFR_TRB*)Trb)->Ioc = 1; + if ((Rq & BIT7) == 0) { + ((XHCI_STATUS_XFR_TRB*)Trb)->Dir = 1; // Status is IN + } + + // Ring the doorbell and see Event Ring update + Doorbell = XhciGetTheDoorbell(Usb3Hc, SlotId); + *Doorbell = 1; // Control EP0 Enqueue Pointer Update + + Status = XhciWaitForEvent( + PeiServices, Usb3Hc, Trb, XhciTTransferEvt, + &CompletionCode, XHCI_CTL_COMPLETE_TIMEOUT_MS, NULL); + + + if (CompletionCode == XHCI_TRB_STALL_ERROR) { + Status = XhciClearStalledEp(PeiServices, + Usb3Hc, SlotId, 1); // Dci = 1 for control endpoint + return 0; + } + + if (Request->Request == USB_DEV_GET_DESCRIPTOR && *DataLength == 8) { + // Full speed device requires the update of MaxPacket size + XhciUpdateEp0MaxPacket(PeiServices, Usb3Hc, SlotId, + ((EFI_USB_DEVICE_DESCRIPTOR*)Data)->MaxPacketSize0); + } + + return EFI_SUCCESS; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: XhciHcBulkTransfer +// +// Description: +// This function intiates a USB bulk transfer and waits on it to +// complete. +// +// Input: +// IN EFI_PEI_SERVICES **PeiServices +// -- EFI_PEI_SERVICES pointer +// IN PEI_USB_HOST_CONTROLLER_PPI *This +// -- PEI_USB_HOST_CONTROLLER_PPI pointer +// IN UINT8 DeviceAddress +// -- USB address of the device for which the control +// transfer is to be issued +// IN UINT8 EndPointAddress +// -- Particular endpoint for the device +// IN UINT8 MaximumPacketLength +// -- Maximum number of bytes that can be sent to or +// received from the endpoint in a single data packet +// OPTIONAL IN OUT VOID *DataBuffer +// -- Pointer to source or destination buffer +// OPTIONAL IN OUT UINTN *DataLength +// -- Length of buffer +// IN OUT UINT8 *DataToggle +// -- Used to update the control/status DataToggle field +// of the Transfer Descriptor +// IN UINTN TimeOut +// -- Not used +// OUT UINT32 *TransferResult +// -- Not used +// +// Output: +// EFI_STATUS (Return Value) +// = EFI_SUCCESS on successful completion +// or valid EFI error code +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS XhciHcBulkTransfer ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *This, + IN UINT8 DeviceAddress, + IN UINT8 EndPointAddress, + IN UINT8 DeviceSpeed, + IN UINT16 MaximumPacketLength, + IN UINT16 TransactionTranslator OPTIONAL, + IN OUT VOID *Data, + IN OUT UINTN *DataLength, + IN OUT UINT8 *DataToggle, + IN UINTN TimeOut, + OUT UINT32 *TransferResult ) +{ + USB3_CONTROLLER *Usb3Hc = PEI_RECOVERY_USB_XHCI_DEV_FROM_THIS(This); + XHCI_TRB *Trb; + UINTN FirstTrb; + volatile UINT32 *Doorbell; + UINT8 SlotId; + UINT8 CompletionCode; + EFI_STATUS Status; + TRB_RING *XfrRing; + UINT8 Endpoint; + UINT8 Dci; + UINT64 DataPointer; + UINT32 ResidualData; // Transferred amount return by Transfer Event + UINT32 TransferredSize; // Total transfer amount + UINT32 RingDataSize; // One TRB ring transfer amount + UINT32 RemainingXfrSize; + UINT32 RemainingDataSize; + UINT32 XfrSize; + UINT32 XfrTdSize; + UINT32 TdPktCnt; + UINT32 TdSize; + + Endpoint = EndPointAddress; + Dci = (Endpoint & 0xf)* 2; + if (Endpoint & BIT7) Dci++; + + GetSlotId(Usb3Hc->DeviceMap, &SlotId, DeviceAddress); + + Doorbell = XhciGetTheDoorbell(Usb3Hc, SlotId); + + XfrRing = XhciGetXfrRing(Usb3Hc, SlotId, Dci-1); + + // Make a chain of TDs to transfer the requested amount of data. If necessary, + // make multiple transfers in a loop. + + DataPointer = (UINTN)Data; + RemainingDataSize = *DataLength; + + // Two loops are executing the transfer: + // The inner loop creates a transfer ring of chained TDs, XHCI_BOT_TD_MAXSIZE + // bytes each. This makes a ring capable of transferring + // XHCI_BOT_TD_MAXSIZE * (TRBS_PER_SEGMENT-1) bytes. + // The outter loop repeats the transfer if the requested transfer size exceeds + // XHCI_BOT_TD_MAXSIZE * (TRBS_PER_SEGMENT-1). + + for (TransferredSize = 0; TransferredSize < *DataLength;) { + // Calculate the amount of data to transfer in the ring + RingDataSize = (RemainingDataSize > XHCI_BOT_MAX_XFR_SIZE)? + XHCI_BOT_MAX_XFR_SIZE : RemainingDataSize; + + RemainingXfrSize = RingDataSize; + TdPktCnt = RingDataSize / MaximumPacketLength; + + for (Trb = NULL, XfrSize = 0, FirstTrb = 0; XfrSize < RingDataSize;) + { + Trb = XhciAdvanceEnqueuePtr(XfrRing); + if (FirstTrb == 0) FirstTrb = (UINTN)Trb; + + Trb->TrbType = XhciTNormal; + ((XHCI_NORMAL_XFR_TRB*)Trb)->Isp = 1; + ((XHCI_NORMAL_XFR_TRB*)Trb)->DataBuffer = DataPointer; + + // See if we need a TD chain. Note that we do not need to + // place the chained TRB into Event Ring, since we will not be + // looking for it anyway. Set IOC only for the last-in-chain TRB. + if (RemainingXfrSize > XHCI_BOT_TD_MAXSIZE) { + XfrTdSize = XHCI_BOT_TD_MAXSIZE; + ((XHCI_NORMAL_XFR_TRB*)Trb)->Chain = 1; + } else { + ((XHCI_NORMAL_XFR_TRB*)Trb)->Ioc = 1; + XfrTdSize = RemainingXfrSize; + } + // Data buffers referenced by Transfer TRBs shall not span 64KB boundaries. + // If a physical data buffer spans a 64KB boundary, software shall chain + // multiple TRBs to describe the buffer. + if (XfrTdSize > (UINT32)(0x10000 - (DataPointer & (0x10000 - 1)))) + { + XfrTdSize = (UINT32)(0x10000 - (DataPointer & (0x10000 - 1))); + ((XHCI_NORMAL_XFR_TRB*)Trb)->Chain = 1; + ((XHCI_NORMAL_XFR_TRB*)Trb)->Ioc = 0; + } + + ((XHCI_NORMAL_XFR_TRB*)Trb)->XferLength = XfrTdSize; + if(Usb3Hc->HciVersion == 0x100) { + TdSize = TdPktCnt - ((XfrSize + XfrTdSize)/MaximumPacketLength); + ((XHCI_NORMAL_XFR_TRB*)Trb)->TdSize = (TdSize > 31)? 31 : TdSize; + } else { + TdSize = RemainingXfrSize - XfrTdSize; + ((XHCI_NORMAL_XFR_TRB*)Trb)->TdSize = (TdSize < 32768)? (TdSize >> 10) : 31; + } + + XfrSize += XfrTdSize; + DataPointer += XfrTdSize; + RemainingXfrSize -= XfrTdSize; + } + + // If transfer ring crossed Link TRB, set its Chain flag + if ((UINTN)Trb < FirstTrb) { + ((XHCI_NORMAL_XFR_TRB*)XfrRing->LastTrb)->Chain = 1; + } + + // Ring the door bell and see Event Ring update + *Doorbell = Dci; + + Status = XhciWaitForEvent( + PeiServices, Usb3Hc, Trb, XhciTTransferEvt, + &CompletionCode, XHCI_BULK_COMPLETE_TIMEOUT_MS, &ResidualData); + + // Clear Link TRB chain flag + ((XHCI_NORMAL_XFR_TRB*)XfrRing->LastTrb)->Chain = 0; + + if (CompletionCode == XHCI_TRB_STALL_ERROR) { + XhciClearStalledEp(PeiServices, Usb3Hc, SlotId, Dci); + break; + } + TransferredSize += (RingDataSize - ResidualData); + if (ResidualData != 0) break; // Short packet detected, no more transfers + RemainingDataSize -= RingDataSize; + } + + *DataLength = TransferredSize; + + return Status; + +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XhciHcGetRootHubPortNumber +// +// Description: +// This function returns number of root ports supported by the controller. +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS XhciHcGetRootHubPortNumber ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *This, + OUT UINT8 *PortNumber ) +{ + USB3_CONTROLLER *Usb3Hc = PEI_RECOVERY_USB_XHCI_DEV_FROM_THIS(This); + + *PortNumber = Usb3Hc->MaxPorts; + return EFI_SUCCESS; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: UpdatePortStatusSpeed +// +// Description: +// This function converts XHCI speed definition into the terms +// of PEI_USB_HOST_CONTROLLER_PPI (namely XHCI_DEVSPEED_xyz is converted +// into USB_PORT_STAT_xyz). +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +VOID +UpdatePortStatusSpeed( + EFI_PEI_SERVICES **PeiServices, + UINT8 Speed, + UINT16 *PortStatus +) +{ + switch (Speed) { + case XHCI_DEVSPEED_UNDEFINED: + case XHCI_DEVSPEED_FULL: + break; + case XHCI_DEVSPEED_LOW: + *PortStatus |= USB_PORT_STAT_LOW_SPEED; + break; + case XHCI_DEVSPEED_HIGH: + *PortStatus |= USB_PORT_STAT_HIGH_SPEED; + break; + case XHCI_DEVSPEED_SUPER: + *PortStatus |= USB_PORT_STAT_SUPER_SPEED; + break; + default: + PEI_TRACE((-1, PeiServices, "XHCI ERROR: unknown device speed.\n")); + } +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XhciHcGetRootHubPortStatus +// +// Description: +// Host controller API function; returns root hub port status in terms of +// PEI_USB_HOST_CONTROLLER_PPI definition. +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS XhciHcGetRootHubPortStatus ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *This, + IN UINT8 PortNumber, + OUT EFI_USB_PORT_STATUS *PortStatus ) +{ + USB3_CONTROLLER *Usb3Hc = PEI_RECOVERY_USB_XHCI_DEV_FROM_THIS(This); + volatile XHCI_PORTSC *PortSC; + + // Find the proper MMIO access offset for a given port + PortSC = (XHCI_PORTSC*)((UINTN)Usb3Hc->OpRegs + + XHCI_PORTSC_OFFSET + (0x10 * (PortNumber - 1))); + + PEI_TRACE((-1, PeiServices, "XHCI port[%d] status: %08x\n", PortNumber, PortSC->AllBits)); + + *(UINT32*)PortStatus = 0; + + if (PortSC->Field.Ccs != 0) { + PortStatus->PortStatus |= USB_PORT_STAT_CONNECTION; + } + if (PortSC->Field.Ped != 0) { + PortStatus->PortStatus |= USB_PORT_STAT_ENABLE; + } + if (PortSC->Field.Oca != 0) { + PortStatus->PortStatus |= USB_PORT_STAT_OVERCURRENT; + } + if (PortSC->Field.Pr != 0) { + PortStatus->PortStatus |= USB_PORT_STAT_RESET; + } + if (PortSC->Field.Pp != 0) { + PortStatus->PortStatus |= USB_PORT_STAT_POWER; + } + if (PortSC->Field.Csc != 0) { + PortStatus->PortChangeStatus |= USB_PORT_STAT_C_CONNECTION; + } + if (PortSC->Field.Pec != 0) { + PortStatus->PortChangeStatus |= USB_PORT_STAT_C_ENABLE; + } + if (PortSC->Field.Occ != 0) { + PortStatus->PortChangeStatus |= USB_PORT_STAT_C_OVERCURRENT; + } + if (PortSC->Field.Prc != 0) { + PortStatus->PortChangeStatus |= USB_PORT_STAT_C_RESET; + } + + UpdatePortStatusSpeed(PeiServices, PortSC->Field.PortSpeed, &PortStatus->PortStatus); + + return EFI_SUCCESS; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XhciHcSetRootHubPortFeature +// +// Description: +// Host controller PEI_USB_HOST_CONTROLLER_PPI API function; sets a requested +// feature of a root hub port. +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS XhciHcSetRootHubPortFeature ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature ) +{ + USB3_CONTROLLER *Usb3Hc = PEI_RECOVERY_USB_XHCI_DEV_FROM_THIS(This); + volatile XHCI_PORTSC *PortSC; + + if (PortNumber > Usb3Hc->MaxPorts) { + return EFI_INVALID_PARAMETER; + } + + PortSC = (XHCI_PORTSC*)((UINTN)Usb3Hc->OpRegs + + XHCI_PORTSC_OFFSET + (0x10 * (PortNumber - 1))); + switch (PortFeature) { + case EfiUsbPortEnable: + case EfiUsbPortSuspend: + break; + + case EfiUsbPortReset: + PortSC->AllBits = (PortSC->AllBits & XHCI_PCS_PP) | XHCI_PCS_PR; + break; + + case EfiUsbPortPower: + PortSC->AllBits = XHCI_PCS_PP; + break; + + default: + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XhciHcClearRootHubPortFeature +// +// Description: +// Host controller PEI_USB_HOST_CONTROLLER_PPI API function; clears a requested +// feature of a root hub port. +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS XhciHcClearRootHubPortFeature ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *This, + IN UINT8 PortNumber, + IN EFI_USB_PORT_FEATURE PortFeature ) +{ + USB3_CONTROLLER *Usb3Hc = PEI_RECOVERY_USB_XHCI_DEV_FROM_THIS(This); + volatile XHCI_PORTSC *PortSC; + + if (PortNumber > Usb3Hc->MaxPorts) { + return EFI_INVALID_PARAMETER; + } + + PortSC = (XHCI_PORTSC*)((UINTN)Usb3Hc->OpRegs + + XHCI_PORTSC_OFFSET + (0x10 * (PortNumber - 1))); + + switch (PortFeature) { + case EfiUsbPortEnable: + PortSC->AllBits = (PortSC->AllBits & XHCI_PCS_PP) | XHCI_PCS_PED; + break; + + case EfiUsbPortSuspend: + case EfiUsbPortReset: + break; + + case EfiUsbPortPower: + PortSC->AllBits = (PortSC->AllBits & XHCI_PCS_PP) & ~(XHCI_PCS_PP); + break; + + case EfiUsbPortOwner: + break; + + case EfiUsbPortConnectChange: + PortSC->AllBits = (PortSC->AllBits & XHCI_PCS_PP) | XHCI_PCS_CSC; + break; + + case EfiUsbPortEnableChange: + PortSC->AllBits = (PortSC->AllBits & XHCI_PCS_PP) | XHCI_PCS_PEC; + break; + + case EfiUsbPortSuspendChange: + break; + + case EfiUsbPortOverCurrentChange: + PortSC->AllBits = (PortSC->AllBits & XHCI_PCS_PP) | XHCI_PCS_OCC; + break; + + case EfiUsbPortResetChange: + PortSC->AllBits = (PortSC->AllBits & XHCI_PCS_PP) | XHCI_PCS_PRC; + break; + + default: + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XhciMmio64Write +// +// Description: +// MMIO write; depending on 64-bit access availability executes either one +// 64-bit write or two 32-bit writes. +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +VOID +XhciMmio64Write( + USB3_CONTROLLER *Usb3Hc, + UINTN Address, + UINT64 Data +) +{ + if (Usb3Hc->Access64) { + *(UINT64*)Address = Data; + } + else { + *(UINT32*)Address = (UINT32)Data; + *(UINT32*)(Address + sizeof(UINT32)) = (UINT32)(Shr64(Data, 32)); + } +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XhciInitRing +// +// Description: +// Transfer ring initialization. There is an option to create a Link TRB in +// the end of the ring. +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS +XhciInitRing ( + IN OUT TRB_RING *Ring, + IN UINTN RingBase, + IN UINT32 RingSize, + IN BOOLEAN PlaceLinkTrb +) +{ + XHCI_LINK_TRB *LinkTrb; + + Ring->Base = (XHCI_TRB*)RingBase; + Ring->Size = RingSize; + Ring->LastTrb = Ring->Base + RingSize - 1; + Ring->CycleBit = 1; + Ring->QueuePtr = (XHCI_TRB*)RingBase; + + // Initialize ring with zeroes + { + UINT8 *p = (UINT8*)RingBase; + UINTN i; + for (i = 0; i < RingSize*sizeof(XHCI_TRB); i++, p++) *p = 0; + } + + if (PlaceLinkTrb) { + // Place a Link TRB in the end of the ring pointing to the beginning + LinkTrb = (XHCI_LINK_TRB*)Ring->LastTrb; + LinkTrb->NextSegPtr = (UINT64)(UINTN)RingBase; + LinkTrb->ToggleCycle = 1; + LinkTrb->TrbType = XhciTLink; + } + + return EFI_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XhciAdvanceEnqueuePtr +// +// Description: +// This function advances returns the pointer to the current TRB and anvances +// dequeue pointer. If the advance pointer is Link TRB, then it: 1) activates +// Link TRB by updating its cycle bit, 2) updates dequeue pointer to the value +// pointed by Link TRB. +// +// Input: +// Ring - TRB ring to be updated +// +// Output: +// TRB that can be used for command, transfer, etc. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +XHCI_TRB* +XhciAdvanceEnqueuePtr( + TRB_RING *Ring +) +{ + XHCI_TRB* Trb = Ring->QueuePtr; + + if (Trb->TrbType == XhciTLink) { + Trb->CycleBit = Ring->CycleBit; + Ring->CycleBit ^= 1; + Ring->QueuePtr = Ring->Base; + + Trb = Ring->QueuePtr; + } + // Clear the TRB + { + UINT32 *p = (UINT32*)Trb; + UINT8 i = 0; + for (i=0; i<(sizeof(XHCI_TRB)/sizeof(UINT32)); i++) { + *p++ = 0; + } + } + + Trb->CycleBit = Ring->CycleBit; + Ring->QueuePtr++; + + return Trb; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: XHCI_ProcessInterrupt +// +// Description: This is the XHCI controller event handler. It walks through +// the Event Ring and executes the event associated code if needed. Updates +// the Event Ring Data Pointer in the xHC to let it know which events are +// completed. +// +// Output: +// EFI_NOT_READY - Need more Interrupt processing +// EFI_SUCCESS - No interrupts pending +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS +XhciProcessInterrupt( + EFI_PEI_SERVICES **PeiServices, + USB3_CONTROLLER *Usb3Hc +) +{ + XHCI_TRB *Trb; + XHCI_EVENT_TRB *EvTrb; + + if ((UINT32)Usb3Hc->OpRegs->DcbAap != (UINT32)Usb3Hc->DcbaaPtr) return EFI_SUCCESS; +/* + if (Usb3Hc->OpRegs->UsbSts.Field.Pcd) { + XHCI_EnumeratePorts(HcStruc); + Usb3Hc->OpRegs->UsbSts.AllBits = XHCI_STS_PCD; // Clear PortChangeDetect + } +*/ + if (Usb3Hc->OpRegs->UsbSts.Field.Eint) + { + Usb3Hc->OpRegs->UsbSts.AllBits = XHCI_STS_EVT_INTERRUPT; + Usb3Hc->RtRegs->IntRegs[0].IMan |= BIT0; + } + + // Check for pending interrupts: + // check the USBSTS[3] and IMAN [0] to determine if any interrupt generated + if (Usb3Hc->EvtRing.QueuePtr->CycleBit != Usb3Hc->EvtRing.CycleBit) return EFI_SUCCESS; + + // See if there are any TRBs waiting in the event ring + //for (Count = 0; Count < Usb3Hc->EvtRing.Size; Count++) { + for (;;) { + Trb = Usb3Hc->EvtRing.QueuePtr; + + if (Trb->CycleBit != Usb3Hc->EvtRing.CycleBit) break; // past the last + + if (Usb3Hc->EvtRing.QueuePtr == Usb3Hc->EvtRing.LastTrb) { + // Reached the end of the ring, wrap around + Usb3Hc->EvtRing.QueuePtr = Usb3Hc->EvtRing.Base; + Usb3Hc->EvtRing.CycleBit ^= 1; + } else { + Usb3Hc->EvtRing.QueuePtr++; + } + // TODO:: error manager + if (Trb->CompletionCode == XHCI_TRB_SHORTPACKET) { + PEI_TRACE((-1, PeiServices, "PEI_XHCI: short packet detected.")); + } + + if (Trb->CompletionCode == XHCI_TRB_STALL_ERROR) { + PEI_TRACE((-1, PeiServices, "PEI_XHCI: device STALLs.")); + } + + if (Trb->CompletionCode != XHCI_TRB_SUCCESS + && Trb->CompletionCode != XHCI_TRB_STALL_ERROR + && Trb->CompletionCode != XHCI_TRB_SHORTPACKET) { + PEI_TRACE((-1, PeiServices, "Trb completion code: %d\n", Trb->CompletionCode)); + PEI_ASSERT(PeiServices, FALSE); + } + + // Process TRB pointed by Usb3Hc->EvtRing->QueuePtr + EvTrb = (XHCI_EVENT_TRB*)Trb; + + switch (Trb->TrbType) { + case XhciTTransferEvt: +// very frequent, debug message here might affect timings, +// uncomment only when needed +// PEI_TRACE((-1, PeiServices, "TransferEvt\n")); + +// DEBUG +/* XhciProcessXferEvt( + PeiServices, + Usb3Hc, + EvTrb->TransferEvt.TrbPtr, + EvTrb->TransferEvt.SlotId, + EvTrb->TransferEvt.EndpointId);*/ + break; + case XhciTCmdCompleteEvt: + PEI_TRACE((-1, PeiServices, "CmdCompleteEvt\n")); + break; + case XhciTPortStatusChgEvt: + PEI_TRACE((-1, PeiServices, "PortStatusChgEvt, port #%d\n", EvTrb->PortStsChgEvt.PortId)); + break; + case XhciTDoorbellEvt: + PEI_TRACE((-1, PeiServices, "DoorbellEvt\n")); + break; + case XhciTHostControllerEvt: + PEI_TRACE((-1, PeiServices, "HostControllerEvt\n")); + break; + case XhciTDevNotificationEvt: + PEI_TRACE((-1, PeiServices, "DevNotificationEvt\n")); + break; + case XhciTMfIndexWrapEvt: + PEI_TRACE((-1, PeiServices, "MfIndexWrapEvt\n")); + break; + default: + PEI_TRACE((-1, PeiServices, "UNKNOWN EVENT\n")); + } + } + //PEI_ASSERT(PeiServices, Count<Usb3Hc->EvtRing.Size); // Event ring is full + + // Update ERDP to inform xHC that we have processed another TRB + XhciMmio64Write(Usb3Hc, + (UINTN)&Usb3Hc->RtRegs->IntRegs->Erdp, (UINT64)(UINTN)Usb3Hc->EvtRing.QueuePtr | BIT3); + + return EFI_SUCCESS; // Set as interrupt processed +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XhciWaitForEvent +// +// Description: +// This function walks through the active TRBs in the event ring and looks for +// the command TRB to be complete. If found, returns SlotId and CompletionCode +// from the completed event TRB. In the end it processes the event ring, +// adjusting its Dequeue Pointer. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS +XhciWaitForEvent( + EFI_PEI_SERVICES **PeiServices, + USB3_CONTROLLER *Usb3Hc, + XHCI_TRB *TrbToCheck, + TRB_TYPE EventType, + UINT8 *CompletionCode, + UINT16 TimeOutMs, + VOID *Data +) +{ + XHCI_TRB *Trb; + UINT32 TimeOut; + EFI_STATUS Status; + UINT8 CycleBit; +// UINT32 TimeOutValue = TimeOutMs << 6; // *64, 15 us unit + UINT32 TimeOutValue = TimeOutMs + 1; + + for (TimeOut = 0; TimeOut < TimeOutValue; TimeOut++) { + for (Trb = Usb3Hc->EvtRing.QueuePtr, + CycleBit = Usb3Hc->EvtRing.CycleBit;;) { + if (Trb->CycleBit != CycleBit) { + // Command is not complete, break and retry + break; + } + + *CompletionCode = Trb->CompletionCode; + if (Trb->CompletionCode == XHCI_TRB_STALL_ERROR || + Trb->CompletionCode == XHCI_TRB_TRANSACTION_ERROR) { + Status = EFI_DEVICE_ERROR; + goto DoneWaiting; + } + + // Active TRB found + if (Trb->TrbType == EventType) { + if ((*(UINTN*)&Trb->Param1) == (UINTN)TrbToCheck) { + + if (Trb->CompletionCode != XHCI_TRB_SUCCESS && Trb->CompletionCode != XHCI_TRB_SHORTPACKET) { + PEI_TRACE((-1, PeiServices, "TRB Completion Error: %d\n", Trb->CompletionCode)); + PEI_ASSERT(PeiServices, FALSE); + } + + if (EventType == XhciTCmdCompleteEvt) { + *(UINT8*)Data = ((XHCI_CMDCOMPLETE_EVT_TRB*)Trb)->SlotId; + } + if (EventType == XhciTTransferEvt) { + if (Data != NULL) { + *(UINT32*)Data = ((XHCI_TRANSFER_EVT_TRB*)Trb)->TransferLength; + } + } + + Status = (Trb->CompletionCode == XHCI_TRB_SUCCESS || + Trb->CompletionCode == XHCI_TRB_SHORTPACKET)? EFI_SUCCESS:EFI_DEVICE_ERROR; + goto DoneWaiting; + } + } + // Advance TRB pointer + if (Trb == Usb3Hc->EvtRing.LastTrb) { + Trb = Usb3Hc->EvtRing.Base; + CycleBit ^= 1; + } else { + Trb++; + } + if (Trb == Usb3Hc->EvtRing.QueuePtr) { + // Event ring is full, return error + PEI_TRACE((-1, PeiServices, "PEI_XHCI: Event Ring is full...\n")); + PEI_ASSERT(PeiServices, FALSE); + *CompletionCode = XHCI_TRB_EVENTRINGFULL_ERROR; + Status = EFI_DEVICE_ERROR; + break; + } + } +// XHCI_FIXED_DELAY_15MCS(Usb3Hc, 1); // 15 us out of TimeOutMs + XHCI_FIXED_DELAY_MS(Usb3Hc, 1); // 1 ms out of TimeOutMs + } + + PEI_TRACE((-1, PeiServices, "PEI_XHCI: execution time-out.\n")); + + *CompletionCode = XHCI_TRB_EXECUTION_TIMEOUT_ERROR; + Status = EFI_DEVICE_ERROR; + +DoneWaiting: + XhciProcessInterrupt(PeiServices, Usb3Hc); + + return Status; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XhciExecuteCommand +// +// Description: +// This function places a given command in the Command Ring, rings HC doorbell, +// and waits for the command completion. +// +// Output: +// EFI_DEVICE_ERROR on execution failure, otherwise EFI_SUCCESS +// Params - pointer to the command specific data. +// +// Notes: +// Caller is responsible for a data placeholder. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS +XhciExecuteCommand( + EFI_PEI_SERVICES **PeiServices, + USB3_CONTROLLER *Usb3Hc, + TRB_TYPE Cmd, + VOID *Params +) +{ + volatile UINT32 *Doorbell; + UINT8 CompletionCode; + UINT8 SlotId; + EFI_STATUS Status; + XHCI_TRB *Trb = XhciAdvanceEnqueuePtr(&Usb3Hc->CmdRing); + + Trb->TrbType = Cmd; // Set TRB type + + // Fill in the command TRB fields + switch (Cmd) { + case XhciTAddressDeviceCmd: + case XhciTEvaluateContextCmd: + case XhciTConfigureEndpointCmd: + ((XHCI_ADDRESSDEV_CMD_TRB*)Trb)->InpCtxAddress = (UINT64)(UINTN)Usb3Hc->InputContext; + ((XHCI_ADDRESSDEV_CMD_TRB*)Trb)->SlotId = *((UINT8*)Params); + ((XHCI_ADDRESSDEV_CMD_TRB*)Trb)->Bsr = 0; + break; + case XhciTResetEndpointCmd: + ((XHCI_RESET_EP_CMD_TRB*)Trb)->Tsp = 0; + ((XHCI_RESET_EP_CMD_TRB*)Trb)->SlotId = *((UINT8*)Params); + ((XHCI_RESET_EP_CMD_TRB*)Trb)->EndpointId = *((UINT8*)Params+1); + break; + case XhciTSetTRDequeuePointerCmd: + ((XHCI_SET_TRPTR_CMD_TRB*)Trb)->TrPointer = ((XHCI_SET_TRPTR_CMD_TRB*)Params)->TrPointer; + ((XHCI_SET_TRPTR_CMD_TRB*)Trb)->EndpointId = ((XHCI_SET_TRPTR_CMD_TRB*)Params)->EndpointId; + ((XHCI_SET_TRPTR_CMD_TRB*)Trb)->SlotId = ((XHCI_SET_TRPTR_CMD_TRB*)Params)->SlotId; + break; + case XhciTDisableSlotCmd: + ((XHCI_DISABLESLOT_CMD_TRB*)Trb)->SlotId = *((UINT8*)Params); + break; + } + + // Ring the door bell and see Event Ring update + Doorbell = (UINT32*)((UINTN)Usb3Hc->CapRegs + Usb3Hc->DbOffset); + *Doorbell = 0; // HC doorbell is #0 + + Status = XhciWaitForEvent( + PeiServices, Usb3Hc, Trb, XhciTCmdCompleteEvt, + &CompletionCode, XHCI_CMD_COMPLETE_TIMEOUT_MS, &SlotId); + + if (Status == EFI_DEVICE_ERROR) { + PEI_TRACE((-1, PeiServices, "XHCI command completion error code: %d\n", CompletionCode)); + PEI_ASSERT(PeiServices, Status != EFI_DEVICE_ERROR); + return Status; + } + + switch (Cmd) { + case XhciTEnableSlotCmd: + PEI_TRACE((-1, PeiServices, "PEI_XHCI: Enable Slot command complete, SlotID %d\n", SlotId)); + *((UINT8*)Params) = SlotId; + break; + case XhciTEvaluateContextCmd: + PEI_TRACE((-1, PeiServices, "PEI_XHCI: Evaluate Context command complete.\n")); + break; + case XhciTConfigureEndpointCmd: + PEI_TRACE((-1, PeiServices, "PEI_XHCI: Configure Endpoint command complete.\n")); + break; + case XhciTResetEndpointCmd: + PEI_TRACE((-1, PeiServices, "PEI_XHCI: Reset Endpoint command complete (slot#%x dci#%x).\n", + *((UINT8*)Params), *((UINT8*)Params+1))); + break; + case XhciTSetTRDequeuePointerCmd: + PEI_TRACE((-1, PeiServices, "PEI_XHCI: Set TR pointer command complete.\n")); + break; + case XhciTDisableSlotCmd: + PEI_TRACE((-1, PeiServices, "PEI_XHCI: DisableSlot command complete.\n")); + break; + } + + return EFI_SUCCESS; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XhciInitXfrRing +// +// Description: +// This function initializes transfer ring of given endpoint +// +// Output: +// Pointer to the transfer ring +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +TRB_RING* +XhciInitXfrRing( + USB3_CONTROLLER* Usb3Hc, + UINT8 Slot, + UINT8 Ep +) +{ + TRB_RING *XfrRing = Usb3Hc->XfrRings + (Slot-1)*32 + Ep; + UINTN Base = Usb3Hc->XfrTrbs + ((Slot-1)*32+Ep)*RING_SIZE; + + XhciInitRing(XfrRing, Base, TRBS_PER_SEGMENT, TRUE); + + return XfrRing; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XhciTranslateInterval +// +// Description: +// This routine calculates the Interval field to be used in device's endpoint +// context. Interval is calculated using the following rules (Section 6.2.3.6): +// +// For SuperSpeed bulk and control endpoints, the Interval field shall not be +// used by the xHC. For all other endpoint types and speeds, system software +// shall translate the bInterval field in the USB Endpoint Descriptor to the +// appropriate value for this field. +// +// For high-speed and SuperSpeed Interrupt and Isoch endpoints the bInterval +// field the Endpoint Descriptor is computed as 125ęs * 2^(bInterval-1), where +// bInterval = 1 to 16, therefore Interval = bInterval - 1. +// +// For low-speed Interrupt and full-speed Interrupt and Isoch endpoints the +// bInterval field declared by a Full or Low-speed device is computed as +// bInterval * 1ms., where bInterval = 1 to 255. +// +// For Full- and Low-speed devices software shall round the value of Endpoint +// Context Interval field down to the nearest base 2 multiple of bInterval * 8. +// +// Input: +// EpType Endpoint type, see XHCI_EP_CONTEXT.DW1.EpType field definitions +// Speed Endpoint speed, 1..4 for XHCI_DEVSPEED_FULL, _LOW, _HIGH, _SUPER +// Interval Poll interval value from endpoint descriptor +// +// Output: +// Interval value to be written to the endpoint context +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +XhciTranslateInterval( + UINT8 EpType, + UINT8 Speed, + UINT8 Interval +) +{ + UINT8 TempData; + UINT8 BitCount; + + if (EpType == XHCI_EPTYPE_CTL || + EpType == XHCI_EPTYPE_BULK_OUT || + EpType == XHCI_EPTYPE_BULK_IN) { + + if (Speed == XHCI_DEVSPEED_HIGH) { + return Interval; + } else { + return 0; // Interval field will not be used for LS, FS and SS + } + } + + // Control and Bulk endpoints are processed; translate intervals for Isoc and Interrupt + // endpoints + + // Translate SS and HS endpoints + if (Speed == XHCI_DEVSPEED_SUPER || Speed == XHCI_DEVSPEED_HIGH) { + return (Interval - 1); + } + + for (TempData = Interval, BitCount = 0; TempData != 0; BitCount++) { + TempData >>= 1; + } + return (BitCount + 2); // return value, where Interval = 0.125*2^value +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XhciEnableEndpoints +// +// Description: +// This function parses the device descriptor data and enables the endpoints +// by 1)assigning the Transfer TRB and 2)executing ConfigureEndpoint command +// for the slot. Section 4.3.5. +// +// Input: +// Desc - Device Configuration Descriptor data pointer +// +// Output: +// EFI_DEVICE_ERROR on error, EFI_SUCCESS on success +// +// Notes: +// 1) This call is executed before SET_CONFIGURATION control transfer +// 2) Device slot is addressed by gSlotBeingConfigured +// 3) EP0 information is valid in the Device +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS +XhciEnableEndpoints ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_USB_HOST_CONTROLLER_PPI *This, + IN UINT8 *Desc +) +{ + UINT16 TotalLength; + UINT16 CurPos; + UINT8 Dci; + EFI_USB_INTERFACE_DESCRIPTOR *IntrfDesc; + EFI_USB_ENDPOINT_DESCRIPTOR *EpDesc; + TRB_RING *XfrRing; + UINT8 EpType; + UINT8 Status; + UINT8 IsHub; + UINT8 DevSpeed; + XHCI_INPUT_CONTROL_CONTEXT *CtlCtx; + XHCI_SLOT_CONTEXT *SlotCtx; + XHCI_EP_CONTEXT *EpCtx; + + USB3_CONTROLLER *Usb3Hc = PEI_RECOVERY_USB_XHCI_DEV_FROM_THIS(This); + UINT8 SlotId = gSlotBeingConfigured; + UINT8 *DevCtx = (UINT8*)XhciGetDeviceContext(Usb3Hc, SlotId); + + if (((EFI_USB_CONFIG_DESCRIPTOR*)Desc)->DescriptorType != USB_DT_CONFIG) return EFI_DEVICE_ERROR; + + SlotCtx = (XHCI_SLOT_CONTEXT*)XhciGetContextEntry(Usb3Hc, DevCtx, 0); + DevSpeed = SlotCtx->Speed; + + // Note (From 4.6.6): The Add Context flag A1 and Drop Context flags D0 and D1 + // of the Input Control Context (in the Input Context) shall be cleared to 0. + // Endpoint 0 Context does not apply to the Configure Endpoint Command and + // shall be ignored by the xHC. A0 shall be set to 1. + + // Note (From 6.2.2.2): If Hub = 1 and Speed = High-Speed (3), then the + // TT Think Time and Multi-TT (MTT) fields shall be initialized. + // If Hub = 1, then the Number of Ports field shall be initialized, else + // Number of Ports = 0. + + // Prepare input context for EvaluateContext comand + (**PeiServices).SetMem((UINT8*)Usb3Hc->InputContext, XHCI_INPUT_CONTEXT_ENTRIES * Usb3Hc->ContextSize, 0); + + CtlCtx = (XHCI_INPUT_CONTROL_CONTEXT*)XhciGetContextEntry(Usb3Hc, (UINT8*)Usb3Hc->InputContext, 0); + CtlCtx->AddContextFlags = BIT0; // EP0 + + SlotCtx = (XHCI_SLOT_CONTEXT*)XhciGetContextEntry(Usb3Hc, (UINT8*)Usb3Hc->InputContext, 1); + + // Collect the endpoint information and update the Device Input Context + TotalLength = ((EFI_USB_CONFIG_DESCRIPTOR*)Desc)->TotalLength; + + if (TotalLength > (MAX_CONTROL_DATA_SIZE - 1)) { + TotalLength = MAX_CONTROL_DATA_SIZE - 1; + } + + for (CurPos = 0; CurPos < TotalLength; CurPos += EpDesc->Length) { + EpDesc = (EFI_USB_ENDPOINT_DESCRIPTOR*)(IntrfDesc = (EFI_USB_INTERFACE_DESCRIPTOR*)(Desc + CurPos)); + + if (IntrfDesc->DescriptorType == USB_DT_INTERFACE) { + IsHub = IntrfDesc->InterfaceClass == BASE_CLASS_HUB; + continue; + } + + if (EpDesc->DescriptorType != USB_DT_ENDPOINT) continue; + + // Found Endpoint, fill up the information in the InputContext + + // Calculate Device Context Index (DCI), Section 4.5.1. + // 1) For Isoch, Interrupt, or Bulk type endpoints the DCI is calculated + // from the Endpoint Number and Direction with the following formula: + // DCI = (Endpoint Number * 2) + Direction, where Direction = 0 for OUT + // endpoints and 1 for IN endpoints. + // 2) For Control type endpoints: + // DCI = (Endpoint Number * 2) + 1 + // + // Also calculate XHCI EP type out of EpDesc->bEndpointFlags + + if ((EpDesc->Attributes & EP_DESC_FLAG_TYPE_BITS) == EP_DESC_FLAG_TYPE_CONT) { + Dci = (EpDesc->EndpointAddress & 0xf) * 2 + 1; + EpType = XHCI_EPTYPE_CTL; + } else { + // Isoc, Bulk or Interrupt endpoint + Dci = (EpDesc->EndpointAddress & 0xf) * 2; + EpType = EpDesc->Attributes & EP_DESC_FLAG_TYPE_BITS; // 1, 2, or 3 + + if (EpDesc->EndpointAddress & BIT7) { + Dci++; // IN + EpType += 4; // 5, 6, or 7 + } + } + + // Update ContextEntries in the Slot context + if (Dci > SlotCtx->ContextEntries) { + SlotCtx->ContextEntries = Dci; + } + + EpCtx = (XHCI_EP_CONTEXT*)XhciGetContextEntry(Usb3Hc, (UINT8*)Usb3Hc->InputContext, Dci + 1); + + EpCtx->EpType = EpType; + EpCtx->MaxPacketSize = EpDesc->MaxPacketSize; + EpCtx->ErrorCount = 3; + + // Set Interval + EpCtx->Interval = XhciTranslateInterval(EpType, DevSpeed, EpDesc->Interval); + + XfrRing = XhciInitXfrRing(Usb3Hc, SlotId, Dci - 1); + EpCtx->TrDequeuePtr = (UINT64)(UINTN)XfrRing->Base + 1; + + CtlCtx->AddContextFlags |= (1 << Dci); + } + + // For a HUB update NumberOfPorts and TTT fields in the Slot context. For that get hub descriptor + // and use bNbrPorts and TT Think time fields (11.23.2.1 of USB2 specification) + // Notes: + // - Slot.Hub field is already updated + // - Do not set NumberOfPorts and TTT fields for 0.95 controllers + + if (IsHub) { + + EFI_STATUS Status; + EFI_USB_HUB_DESCRIPTOR HubDesc; + UINT8 Speed; + EFI_USB_DEVICE_REQUEST DevReq; + UINT32 Timeout; + UINTN DataLength = sizeof(EFI_USB_HUB_DESCRIPTOR); + UINT32 TransferResult; + + // + // Fill Device request packet + // + (**PeiServices).SetMem((VOID*)&DevReq, sizeof(EFI_USB_DEVICE_REQUEST), 0); + DevReq.RequestType = USB_RT_HUB | 0x80; + DevReq.Request = USB_DEV_GET_DESCRIPTOR; + DevReq.Value = DevSpeed == XHCI_DEVSPEED_SUPER ? USB_DT_SS_HUB << 8 : USB_DT_HUB << 8; + DevReq.Index = 0; + DevReq.Length = sizeof(EFI_USB_HUB_DESCRIPTOR); + + Timeout = 3000; + + switch (DevSpeed) { + case XHCI_DEVSPEED_HIGH: Speed = USB_HIGH_SPEED_DEVICE; break; + case XHCI_DEVSPEED_LOW: Speed = USB_SLOW_SPEED_DEVICE; break; + case XHCI_DEVSPEED_FULL: Speed = USB_FULL_SPEED_DEVICE; break; + case XHCI_DEVSPEED_SUPER: Speed = USB_SUPER_SPEED_DEVICE; + } + + EpCtx = (XHCI_EP_CONTEXT*)XhciGetContextEntry(Usb3Hc, DevCtx, 1); + + Status = XhciHcControlTransfer(PeiServices, This, + 0, // Current address + Speed, + EpCtx->MaxPacketSize, + 0, // Transaction translator + &DevReq, + EfiUsbDataIn, + &HubDesc, + &DataLength, + Timeout, + &TransferResult); + + if (!EFI_ERROR(Status)) { + SlotCtx->Hub = 1; + SlotCtx->PortsNum = HubDesc.NbrPorts; + + if (DevSpeed == XHCI_DEVSPEED_HIGH) { + SlotCtx->TThinkTime = (HubDesc.HubCharacteristics[0] >> 5) & 0x3; + } + } + } + +// check route string here + // Input context is updated with the endpoint information. Execute ConfigureEndpoint command. + Status = XhciExecuteCommand(PeiServices, Usb3Hc, XhciTConfigureEndpointCmd, &SlotId); + PEI_ASSERT(PeiServices, Status == EFI_SUCCESS); + + return Status; +} + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XhciGetXfrRing +// +// Description: +// This routine calculates the address of the address ring of a particular +// Slot/Endpoint. +// +// Output: +// Pointer to the transfer ring +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +TRB_RING* +XhciGetXfrRing( + USB3_CONTROLLER* Usb3Hc, + UINT8 Slot, + UINT8 Ep +) +{ + return Usb3Hc->XfrRings + (Slot-1)*32 + Ep; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XhciGetTheDoorbell +// +// Description: +// This function calculates and returns the pointer to a doorbell for a +// given Slot. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT32* +XhciGetTheDoorbell( + USB3_CONTROLLER *Usb3Hc, + UINT8 SlotId +) +{ + return (UINT32*)((UINTN)Usb3Hc->CapRegs + Usb3Hc->DbOffset + sizeof(UINT32)*SlotId); +} + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XhciGetDeviceContext +// +// Description: +// This function calculates and returns the pointer to a device context for +// a given Slot. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8* +XhciGetDeviceContext( + USB3_CONTROLLER *Usb3Hc, + UINT8 SlotId +) +{ + UINT32 DevCtxSize = XHCI_DEVICE_CONTEXT_ENTRIES * Usb3Hc->ContextSize; + return (UINT8*)((UINTN)Usb3Hc->DeviceContext + (SlotId - 1) * DevCtxSize); +} + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: XhciGetContextEntry +// +// Description: +// This function calculates and returns the pointer to a context entry for +// a given index. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8* +XhciGetContextEntry( + USB3_CONTROLLER *Usb3Hc, + VOID *Context, + UINT8 Index +) +{ + return (UINT8*)((UINTN)Context + Index * Usb3Hc->ContextSize); +} + //(EIP75547+)> +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XhciEnableUsb2Port +// +// Description: +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +VOID +XhciResetUsb2Port( + EFI_PEI_SERVICES **PeiServices, + USB3_CONTROLLER *Usb3Hc +) +{ + UINT8 Count; + UINT8 PortNumber; + volatile XHCI_PORTSC *PortSC; + UINT32 i; + + if (Usb3Hc->Usb2Protocol) { + for(Count = 0; Count < Usb3Hc->Usb2Protocol->PortCount; Count++) { + PortNumber = Count + Usb3Hc->Usb2Protocol->PortOffset; + PortSC =(XHCI_PORTSC*)((UINTN)Usb3Hc->OpRegs + + XHCI_PORTSC_OFFSET + (0x10 * (PortNumber - 1))); + if (PortSC->Field.Ccs) { + if(!(PortSC->Field.Ped)) { + PortSC->AllBits = XHCI_PCS_PR | XHCI_PCS_PP; + for (i = 0; i < 200; i++) { + XHCI_FIXED_DELAY_MS(Usb3Hc, 1); + if (PortSC->Field.Prc) break; + } + PortSC->AllBits = XHCI_PCS_WRC | XHCI_PCS_PRC | XHCI_PCS_PP; + } + } + } + } + + } + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Name: XhciExtCapParser +// +// Description: +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +EFI_STATUS +XhciExtCapParser( + EFI_PEI_SERVICES **PeiServices, + USB3_CONTROLLER *Usb3Hc +) +{ + XHCI_EXT_CAP *CurPtr; + + if (Usb3Hc->CapRegs->HcParams.Xecp == 0) return EFI_SUCCESS; + + // Starts from first capability + CurPtr = (XHCI_EXT_CAP *)((UINTN)Usb3Hc->CapRegs + (Usb3Hc->CapRegs->HcParams.Xecp << 2)); + + // Traverse all capability structures + for(;;) { + switch (CurPtr->CapId) { + case XHCI_EXT_CAP_USB_LEGACY: + break; + case XHCI_EXT_CAP_SUPPORTED_PROTOCOL: + if (((XHCI_EXT_PROTOCOL*)CurPtr)->MajorRev == 0x02) { + Usb3Hc->Usb2Protocol = (XHCI_EXT_PROTOCOL*)CurPtr; + PEI_TRACE((-1, PeiServices, "XHCI: USB2 Support Protocol %x, PortOffset %x PortCount %x\n", + Usb3Hc->Usb2Protocol, Usb3Hc->Usb2Protocol->PortOffset, Usb3Hc->Usb2Protocol->PortCount)); + } else if (((XHCI_EXT_PROTOCOL*)CurPtr)->MajorRev == 0x03) { + Usb3Hc->Usb3Protocol = (XHCI_EXT_PROTOCOL*)CurPtr; + PEI_TRACE((-1, PeiServices, "XHCI: USB3 Support Protocol %x, PortOffset %x PortCount %x\n", + Usb3Hc->Usb3Protocol, Usb3Hc->Usb3Protocol->PortOffset, Usb3Hc->Usb3Protocol->PortCount)); + } + break; + + case XHCI_EXT_CAP_POWERMANAGEMENT: + case XHCI_EXT_CAP_IO_VIRTUALIZATION: + case XHCI_EXT_CAP_USB_DEBUG_PORT: + break; + } + if(CurPtr->NextCapPtr == 0) break; + // Point to next capability + CurPtr=(XHCI_EXT_CAP *)((UINTN)CurPtr+ (((UINTN)CurPtr->NextCapPtr) << 2)); + } + + return EFI_SUCCESS; +} + //(EIP75547+) + +//********************************************************************** +//********************************************************************** +//** ** +//** (C)Copyright 1985-2010, American Megatrends, Inc. ** +//** ** +//** All Rights Reserved. ** +//** ** +//** 5555 Oakbrook Pkwy, Suite 200, Norcross, GA 30093 ** +//** ** +//** Phone: (770)-246-8600 ** +//** ** +//********************************************************************** +//********************************************************************** +//********************************************************************** |