diff options
Diffstat (limited to 'Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Common.c')
-rw-r--r-- | Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Common.c | 1066 |
1 files changed, 1066 insertions, 0 deletions
diff --git a/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Common.c b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Common.c new file mode 100644 index 0000000000..04e25e3ec2 --- /dev/null +++ b/Core/SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Common.c @@ -0,0 +1,1066 @@ +/** @file
+ Debug Port Library implementation based on usb3 debug port.
+
+ Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "DebugCommunicationLibUsb3Internal.h"
+
+//
+// The global variable which can be used after memory is ready.
+//
+USB3_DEBUG_PORT_HANDLE mDebugCommunicationLibUsb3DebugPortHandle;
+
+UINT16 mString0Desc[] = {
+ // String Descriptor Type + Length
+ ( USB_DESC_TYPE_STRING << 8 ) + STRING0_DESC_LEN,
+ 0x0409
+};
+
+UINT16 mManufacturerStrDesc[] = {
+ // String Descriptor Type + Length
+ ( USB_DESC_TYPE_STRING << 8 ) + MANU_DESC_LEN,
+ 'I', 'n', 't', 'e', 'l'
+};
+
+UINT16 mProductStrDesc[] = {
+ // String Descriptor Type + Length
+ ( USB_DESC_TYPE_STRING << 8 ) + PRODUCT_DESC_LEN,
+ 'U', 'S', 'B', ' ', '3', '.', '0', ' ', 'D', 'e', 'b', 'u', 'g', ' ', 'C', 'a', 'b', 'l', 'e'
+};
+
+UINT16 mSerialNumberStrDesc[] = {
+ // String Descriptor Type + Length
+ ( USB_DESC_TYPE_STRING << 8 ) + SERIAL_DESC_LEN,
+ '1'
+};
+
+/**
+ Sets bits as per the enabled bit positions in the mask.
+
+ @param[in, out] Register UINTN register
+ @param[in] BitMask 32-bit mask
+**/
+VOID
+XhcSetR32Bit(
+ IN OUT UINTN Register,
+ IN UINT32 BitMask
+ )
+{
+ UINT32 RegisterValue;
+
+ RegisterValue = MmioRead32 (Register);
+ RegisterValue |= (UINT32)(BitMask);
+ MmioWrite32 (Register, RegisterValue);
+}
+
+/**
+ Clears bits as per the enabled bit positions in the mask.
+
+ @param[in, out] Register UINTN register
+ @param[in] BitMask 32-bit mask
+**/
+VOID
+XhcClearR32Bit(
+ IN OUT UINTN Register,
+ IN UINT32 BitMask
+ )
+{
+ UINT32 RegisterValue;
+
+ RegisterValue = MmioRead32 (Register);
+ RegisterValue &= ~BitMask;
+ MmioWrite32 (Register, RegisterValue);
+}
+
+/**
+ Write the data to the XHCI debug register.
+
+ @param Handle Debug port handle.
+ @param Offset The offset of the runtime register.
+ @param Data The data to write.
+
+**/
+VOID
+XhcWriteDebugReg (
+ IN USB3_DEBUG_PORT_HANDLE *Handle,
+ IN UINT32 Offset,
+ IN UINT32 Data
+ )
+{
+ EFI_PHYSICAL_ADDRESS DebugCapabilityBase;
+
+ DebugCapabilityBase = Handle->DebugCapabilityBase;
+ MmioWrite32 ((UINTN)(DebugCapabilityBase + Offset), Data);
+
+ return;
+}
+
+/**
+ Read XHCI debug register.
+
+ @param Handle Debug port handle.
+ @param Offset The offset of the runtime register.
+
+ @return The register content read
+
+**/
+UINT32
+XhcReadDebugReg (
+ IN USB3_DEBUG_PORT_HANDLE *Handle,
+ IN UINT32 Offset
+ )
+{
+ UINT32 Data;
+ EFI_PHYSICAL_ADDRESS DebugCapabilityBase;
+
+ DebugCapabilityBase = Handle->DebugCapabilityBase;
+ Data = MmioRead32 ((UINTN)(DebugCapabilityBase + Offset));
+
+ return Data;
+}
+
+/**
+ Set one bit of the runtime register while keeping other bits.
+
+ @param Handle Debug port handle.
+ @param Offset The offset of the runtime register.
+ @param Bit The bit mask of the register to set.
+
+**/
+VOID
+XhcSetDebugRegBit (
+ IN USB3_DEBUG_PORT_HANDLE *Handle,
+ IN UINT32 Offset,
+ IN UINT32 Bit
+ )
+{
+ UINT32 Data;
+
+ Data = XhcReadDebugReg (Handle, Offset);
+ Data |= Bit;
+ XhcWriteDebugReg (Handle, Offset, Data);
+}
+
+/**
+ Program and eanble XHCI MMIO base address.
+
+ @return XHCI MMIO base address.
+
+**/
+EFI_PHYSICAL_ADDRESS
+ProgramXhciBaseAddress (
+ VOID
+ )
+{
+ UINT16 PciCmd;
+ UINT32 Low;
+ UINT32 High;
+ EFI_PHYSICAL_ADDRESS XhciMmioBase;
+
+ Low = PciRead32 (PcdGet32(PcdUsbXhciPciAddress) + PCI_BASE_ADDRESSREG_OFFSET);
+ High = PciRead32 (PcdGet32(PcdUsbXhciPciAddress) + PCI_BASE_ADDRESSREG_OFFSET + 4);
+ XhciMmioBase = (EFI_PHYSICAL_ADDRESS) (LShiftU64 ((UINT64) High, 32) | Low);
+ XhciMmioBase &= XHCI_BASE_ADDRESS_64_BIT_MASK;
+
+ if ((XhciMmioBase == 0) || (XhciMmioBase == XHCI_BASE_ADDRESS_64_BIT_MASK)) {
+ XhciMmioBase = PcdGet64(PcdUsbXhciMemorySpaceBase);
+ PciWrite32(PcdGet32(PcdUsbXhciPciAddress) + PCI_BASE_ADDRESSREG_OFFSET, XhciMmioBase & 0xFFFFFFFF);
+ PciWrite32(PcdGet32(PcdUsbXhciPciAddress) + PCI_BASE_ADDRESSREG_OFFSET + 4, (RShiftU64 (XhciMmioBase, 32) & 0xFFFFFFFF));
+ }
+
+ PciCmd = PciRead16 (PcdGet32(PcdUsbXhciPciAddress) + PCI_COMMAND_OFFSET);
+ if (((PciCmd & EFI_PCI_COMMAND_MEMORY_SPACE) == 0) || ((PciCmd & EFI_PCI_COMMAND_BUS_MASTER) == 0)) {
+ PciCmd |= EFI_PCI_COMMAND_MEMORY_SPACE | EFI_PCI_COMMAND_BUS_MASTER;
+ PciWrite16(PcdGet32(PcdUsbXhciPciAddress) + PCI_COMMAND_OFFSET, PciCmd);
+ }
+
+ return XhciMmioBase;
+}
+
+/**
+ Update XHC MMIO base address when MMIO base address is changed.
+
+ @param Handle Debug port handle.
+ @param XhciMmioBase XHCI MMIO base address.
+
+**/
+VOID
+UpdateXhcResource (
+ IN OUT USB3_DEBUG_PORT_HANDLE *Handle,
+ IN EFI_PHYSICAL_ADDRESS XhciMmioBase
+ )
+{
+ if ((Handle == NULL) || (Handle->XhciMmioBase == XhciMmioBase)) {
+ return;
+ }
+
+ //
+ // Need fix Handle data according to new XHCI MMIO base address.
+ //
+ Handle->XhciMmioBase = XhciMmioBase;
+ Handle->DebugCapabilityBase = XhciMmioBase + Handle->DebugCapabilityOffset;
+ Handle->XhciOpRegister = XhciMmioBase + MmioRead8 ((UINTN)XhciMmioBase);
+}
+
+/**
+ Calculate the usb debug port bar address.
+
+ @param Handle Debug port handle.
+
+ @retval RETURN_UNSUPPORTED The usb host controller does not supported usb debug port capability.
+ @retval RETURN_SUCCESS Get bar and offset successfully.
+
+**/
+RETURN_STATUS
+EFIAPI
+CalculateUsbDebugPortMmioBase (
+ USB3_DEBUG_PORT_HANDLE *Handle
+ )
+{
+ UINT16 VendorId;
+ UINT16 DeviceId;
+ UINT8 ProgInterface;
+ UINT8 SubClassCode;
+ UINT8 BaseCode;
+ BOOLEAN Flag;
+ UINT32 Capability;
+ EFI_PHYSICAL_ADDRESS CapabilityPointer;
+ UINT8 CapLength;
+
+ VendorId = PciRead16 (PcdGet32(PcdUsbXhciPciAddress) + PCI_VENDOR_ID_OFFSET);
+ DeviceId = PciRead16 (PcdGet32(PcdUsbXhciPciAddress) + PCI_DEVICE_ID_OFFSET);
+
+ if ((VendorId == 0xFFFF) || (DeviceId == 0xFFFF)) {
+ goto Done;
+ }
+
+ ProgInterface = PciRead8 (PcdGet32(PcdUsbXhciPciAddress) + PCI_CLASSCODE_OFFSET);
+ SubClassCode = PciRead8 (PcdGet32(PcdUsbXhciPciAddress) + PCI_CLASSCODE_OFFSET + 1);
+ BaseCode = PciRead8 (PcdGet32(PcdUsbXhciPciAddress) + PCI_CLASSCODE_OFFSET + 2);
+
+ if ((ProgInterface != PCI_IF_XHCI) || (SubClassCode != PCI_CLASS_SERIAL_USB) || (BaseCode != PCI_CLASS_SERIAL)) {
+ goto Done;
+ }
+
+ CapLength = MmioRead8 ((UINTN) Handle->XhciMmioBase);
+
+ //
+ // Get capability pointer from HCCPARAMS at offset 0x10
+ //
+ CapabilityPointer = Handle->XhciMmioBase + (MmioRead32 ((UINTN)(Handle->XhciMmioBase + XHC_HCCPARAMS_OFFSET)) >> 16) * 4;
+
+ //
+ // Search XHCI debug capability
+ //
+ Flag = FALSE;
+ Capability = MmioRead32 ((UINTN)CapabilityPointer);
+ while (TRUE) {
+ if ((Capability & XHC_CAPABILITY_ID_MASK) == PCI_CAPABILITY_ID_DEBUG_PORT) {
+ Flag = TRUE;
+ break;
+ }
+ if ((((Capability & XHC_NEXT_CAPABILITY_MASK) >> 8) & XHC_CAPABILITY_ID_MASK) == 0) {
+ //
+ // Reach the end of capability list, quit
+ //
+ break;
+ }
+ CapabilityPointer += ((Capability & XHC_NEXT_CAPABILITY_MASK) >> 8) * 4;
+ Capability = MmioRead32 ((UINTN)CapabilityPointer);
+ }
+
+ if (!Flag) {
+ goto Done;
+ }
+
+ //
+ // USB3 debug capability is supported.
+ //
+ Handle->DebugCapabilityBase = CapabilityPointer;
+ Handle->DebugCapabilityOffset = CapabilityPointer - Handle->XhciMmioBase;
+ Handle->XhciOpRegister = Handle->XhciMmioBase + CapLength;
+ Handle->Initialized = USB3DBG_DBG_CAB;
+ return RETURN_SUCCESS;
+
+Done:
+ Handle->Initialized = USB3DBG_NO_DBG_CAB;
+ return RETURN_UNSUPPORTED;
+}
+
+/**
+ Check if it needs to re-initialize usb debug port hardware.
+
+ During different phases switch, such as SEC to PEI or PEI to DXE or DXE to SMM, we should check
+ whether the usb debug port hardware configuration is changed. Such case can be triggered by
+ Pci bus resource allocation and so on.
+
+ @param Handle Debug port handle.
+
+ @retval TRUE The usb debug port hardware configuration is changed.
+ @retval FALSE The usb debug port hardware configuration is not changed.
+
+**/
+BOOLEAN
+EFIAPI
+NeedReinitializeHardware(
+ IN USB3_DEBUG_PORT_HANDLE *Handle
+ )
+{
+ BOOLEAN Result;
+ volatile UINT32 Dcctrl;
+
+ Result = FALSE;
+
+ //
+ // If DCE bit, it means USB3 debug is not enabled.
+ //
+ Dcctrl = XhcReadDebugReg (Handle, XHC_DC_DCCTRL);
+ if ((Dcctrl & BIT0) == 0) {
+ Result = TRUE;
+ }
+
+ return Result;
+}
+
+/**
+ Create XHCI event ring.
+
+ @param Handle Debug port handle.
+ @param EventRing The created event ring.
+
+**/
+EFI_STATUS
+CreateEventRing (
+ IN USB3_DEBUG_PORT_HANDLE *Handle,
+ OUT EVENT_RING *EventRing
+ )
+{
+ VOID *Buf;
+ EVENT_RING_SEG_TABLE_ENTRY *ERSTBase;
+
+ ASSERT (EventRing != NULL);
+
+ //
+ // Allocate Event Ring
+ //
+ Buf = AllocateAlignBuffer (sizeof (TRB_TEMPLATE) * EVENT_RING_TRB_NUMBER);
+ ASSERT (Buf != NULL);
+ ASSERT (((UINTN) Buf & 0x3F) == 0);
+ ZeroMem (Buf, sizeof (TRB_TEMPLATE) * EVENT_RING_TRB_NUMBER);
+
+ EventRing->EventRingSeg0 = (EFI_PHYSICAL_ADDRESS)(UINTN) Buf;
+ EventRing->TrbNumber = EVENT_RING_TRB_NUMBER;
+ EventRing->EventRingDequeue = (EFI_PHYSICAL_ADDRESS)(UINTN) EventRing->EventRingSeg0;
+ EventRing->EventRingEnqueue = (EFI_PHYSICAL_ADDRESS)(UINTN) EventRing->EventRingSeg0;
+
+ //
+ // Software maintains an Event Ring Consumer Cycle State (CCS) bit, initializing it to '1'
+ // and toggling it every time the Event Ring Dequeue Pointer wraps back to the beginning of the Event Ring.
+ //
+ EventRing->EventRingCCS = 1;
+
+ //
+ // Allocate Event Ring Segment Table Entry 0 in Event Ring Segment Table
+ //
+ Buf = AllocateAlignBuffer (sizeof (EVENT_RING_SEG_TABLE_ENTRY) * ERST_NUMBER);
+ ASSERT (Buf != NULL);
+ ASSERT (((UINTN) Buf & 0x3F) == 0);
+ ZeroMem (Buf, sizeof (EVENT_RING_SEG_TABLE_ENTRY) * ERST_NUMBER);
+
+ ERSTBase = (EVENT_RING_SEG_TABLE_ENTRY *) Buf;
+ EventRing->ERSTBase = (EFI_PHYSICAL_ADDRESS)(UINTN) ERSTBase;
+
+ //
+ // Fill Event Segment address
+ //
+ ERSTBase->PtrLo = XHC_LOW_32BIT (EventRing->EventRingSeg0);
+ ERSTBase->PtrHi = XHC_HIGH_32BIT (EventRing->EventRingSeg0);
+ ERSTBase->RingTrbSize = EVENT_RING_TRB_NUMBER;
+
+ //
+ // Program the Interrupter Event Ring Dequeue Pointer (DCERDP) register (7.6.4.1)
+ //
+ XhcWriteDebugReg (
+ Handle,
+ XHC_DC_DCERDP,
+ XHC_LOW_32BIT((UINT64)(UINTN)EventRing->EventRingDequeue)
+ );
+
+ XhcWriteDebugReg (
+ Handle,
+ XHC_DC_DCERDP + 4,
+ XHC_HIGH_32BIT((UINT64)(UINTN)EventRing->EventRingDequeue)
+ );
+
+ //
+ // Program the Debug Capability Event Ring Segment Table Base Address (DCERSTBA) register(7.6.4.1)
+ //
+ XhcWriteDebugReg (
+ Handle,
+ XHC_DC_DCERSTBA,
+ XHC_LOW_32BIT((UINT64)(UINTN)ERSTBase)
+ );
+
+ XhcWriteDebugReg (
+ Handle,
+ XHC_DC_DCERSTBA + 4,
+ XHC_HIGH_32BIT((UINT64)(UINTN)ERSTBase)
+ );
+
+ //
+ // Program the Debug Capability Event Ring Segment Table Size (DCERSTSZ) register(7.6.4.1)
+ //
+ XhcWriteDebugReg (
+ Handle,
+ XHC_DC_DCERSTSZ,
+ ERST_NUMBER
+ );
+ return EFI_SUCCESS;
+}
+
+/**
+ Create XHCI transfer ring.
+
+ @param Handle Debug port handle.
+ @param TrbNum The number of TRB in the ring.
+ @param TransferRing The created transfer ring.
+
+**/
+VOID
+CreateTransferRing (
+ IN USB3_DEBUG_PORT_HANDLE *Handle,
+ IN UINT32 TrbNum,
+ OUT TRANSFER_RING *TransferRing
+ )
+{
+ VOID *Buf;
+ LINK_TRB *EndTrb;
+
+ Buf = AllocateAlignBuffer (sizeof (TRB_TEMPLATE) * TrbNum);
+ ASSERT (Buf != NULL);
+ ASSERT (((UINTN) Buf & 0xF) == 0);
+ ZeroMem (Buf, sizeof (TRB_TEMPLATE) * TrbNum);
+
+ TransferRing->RingSeg0 = (EFI_PHYSICAL_ADDRESS)(UINTN) Buf;
+ TransferRing->TrbNumber = TrbNum;
+ TransferRing->RingEnqueue = TransferRing->RingSeg0;
+ TransferRing->RingDequeue = TransferRing->RingSeg0;
+ TransferRing->RingPCS = 1;
+ //
+ // 4.9.2 Transfer Ring Management
+ // To form a ring (or circular queue) a Link TRB may be inserted at the end of a ring to
+ // point to the first TRB in the ring.
+ //
+ EndTrb = (LINK_TRB *) ((UINTN)Buf + sizeof (TRB_TEMPLATE) * (TrbNum - 1));
+ EndTrb->Type = TRB_TYPE_LINK;
+ EndTrb->PtrLo = XHC_LOW_32BIT (Buf);
+ EndTrb->PtrHi = XHC_HIGH_32BIT (Buf);
+ //
+ // Toggle Cycle (TC). When set to '1', the xHC shall toggle its interpretation of the Cycle bit.
+ //
+ EndTrb->TC = 1;
+ //
+ // Set Cycle bit as other TRB PCS init value
+ //
+ EndTrb->CycleBit = 0;
+}
+
+/**
+ Create debug capability context for XHC debug device.
+
+ @param Handle Debug port handle.
+
+ @retval EFI_SUCCESS The bit successfully changed by host controller.
+ @retval EFI_TIMEOUT The time out occurred.
+
+**/
+EFI_STATUS
+CreateDebugCapabilityContext (
+ IN USB3_DEBUG_PORT_HANDLE *Handle
+ )
+{
+ VOID *Buf;
+ XHC_DC_CONTEXT *DebugCapabilityContext;
+ UINT8 *String0Desc;
+ UINT8 *ManufacturerStrDesc;
+ UINT8 *ProductStrDesc;
+ UINT8 *SerialNumberStrDesc;
+
+ //
+ // Allocate debug device context
+ //
+ Buf = AllocateAlignBuffer (sizeof (XHC_DC_CONTEXT));
+ ASSERT (Buf != NULL);
+ ASSERT (((UINTN) Buf & 0xF) == 0);
+ ZeroMem (Buf, sizeof (XHC_DC_CONTEXT));
+
+ DebugCapabilityContext = (XHC_DC_CONTEXT *)(UINTN) Buf;
+ Handle->DebugCapabilityContext = (EFI_PHYSICAL_ADDRESS)(UINTN) DebugCapabilityContext;
+
+ //
+ // Initialize DbcInfoContext.
+ //
+ DebugCapabilityContext->DbcInfoContext.String0Length = STRING0_DESC_LEN;
+ DebugCapabilityContext->DbcInfoContext.ManufacturerStrLength = MANU_DESC_LEN;
+ DebugCapabilityContext->DbcInfoContext.ProductStrLength = PRODUCT_DESC_LEN;
+ DebugCapabilityContext->DbcInfoContext.SerialNumberStrLength = SERIAL_DESC_LEN;
+
+ //
+ // Initialize EpOutContext.
+ //
+ DebugCapabilityContext->EpOutContext.CErr = 0x3;
+ DebugCapabilityContext->EpOutContext.EPType = ED_BULK_OUT;
+ DebugCapabilityContext->EpOutContext.MaxPacketSize = XHCI_DEBUG_DEVICE_MAX_PACKET_SIZE;
+ DebugCapabilityContext->EpOutContext.AverageTRBLength = 0x1000;
+
+ //
+ // Initialize EpInContext.
+ //
+ DebugCapabilityContext->EpInContext.CErr = 0x3;
+ DebugCapabilityContext->EpInContext.EPType = ED_BULK_IN;
+ DebugCapabilityContext->EpInContext.MaxPacketSize = XHCI_DEBUG_DEVICE_MAX_PACKET_SIZE;
+ DebugCapabilityContext->EpInContext.AverageTRBLength = 0x1000;
+
+ //
+ // Update string descriptor address
+ //
+ String0Desc = (UINT8 *) AllocateAlignBuffer (STRING0_DESC_LEN + MANU_DESC_LEN + PRODUCT_DESC_LEN + SERIAL_DESC_LEN);
+ ASSERT (String0Desc != NULL);
+ ZeroMem (String0Desc, STRING0_DESC_LEN + MANU_DESC_LEN + PRODUCT_DESC_LEN + SERIAL_DESC_LEN);
+ CopyMem (String0Desc, mString0Desc, STRING0_DESC_LEN);
+ DebugCapabilityContext->DbcInfoContext.String0DescAddress = (UINT64)(UINTN)String0Desc;
+
+ ManufacturerStrDesc = String0Desc + STRING0_DESC_LEN;
+ CopyMem (ManufacturerStrDesc, mManufacturerStrDesc, MANU_DESC_LEN);
+ DebugCapabilityContext->DbcInfoContext.ManufacturerStrDescAddress = (UINT64)(UINTN)ManufacturerStrDesc;
+
+ ProductStrDesc = ManufacturerStrDesc + MANU_DESC_LEN;
+ CopyMem (ProductStrDesc, mProductStrDesc, PRODUCT_DESC_LEN);
+ DebugCapabilityContext->DbcInfoContext.ProductStrDescAddress = (UINT64)(UINTN)ProductStrDesc;
+
+ SerialNumberStrDesc = ProductStrDesc + PRODUCT_DESC_LEN;
+ CopyMem (SerialNumberStrDesc, mSerialNumberStrDesc, SERIAL_DESC_LEN);
+ DebugCapabilityContext->DbcInfoContext.SerialNumberStrDescAddress = (UINT64)(UINTN)SerialNumberStrDesc;
+
+ //
+ // Allocate and initialize the Transfer Ring for the Input Endpoint Context.
+ //
+ ZeroMem (&Handle->TransferRingIn, sizeof (TRANSFER_RING));
+ CreateTransferRing (Handle, TR_RING_TRB_NUMBER, &Handle->TransferRingIn);
+ DebugCapabilityContext->EpInContext.PtrLo = XHC_LOW_32BIT (Handle->TransferRingIn.RingSeg0) | BIT0;
+ DebugCapabilityContext->EpInContext.PtrHi = XHC_HIGH_32BIT (Handle->TransferRingIn.RingSeg0);
+
+ //
+ // Allocate and initialize the Transfer Ring for the Output Endpoint Context.
+ //
+ ZeroMem (&Handle->TransferRingOut, sizeof (TRANSFER_RING));
+ CreateTransferRing (Handle, TR_RING_TRB_NUMBER, &Handle->TransferRingOut);
+ DebugCapabilityContext->EpOutContext.PtrLo = XHC_LOW_32BIT (Handle->TransferRingOut.RingSeg0) | BIT0;
+ DebugCapabilityContext->EpOutContext.PtrHi = XHC_HIGH_32BIT (Handle->TransferRingOut.RingSeg0);
+
+ //
+ // Program the Debug Capability Context Pointer (DCCP) register(7.6.8.7)
+ //
+ XhcWriteDebugReg (
+ Handle,
+ XHC_DC_DCCP,
+ XHC_LOW_32BIT((UINT64)(UINTN)DebugCapabilityContext)
+ );
+ XhcWriteDebugReg (
+ Handle,
+ XHC_DC_DCCP + 4,
+ XHC_HIGH_32BIT((UINT64)(UINTN)DebugCapabilityContext)
+ );
+ return EFI_SUCCESS;
+}
+
+/**
+ Check if debug device is running.
+
+ @param Handle Debug port handle.
+
+**/
+VOID
+XhcDetectDebugCapabilityReady (
+ IN USB3_DEBUG_PORT_HANDLE *Handle
+ )
+{
+ UINT64 TimeOut;
+ volatile UINT32 Dcctrl;
+
+ TimeOut = 1;
+ if (Handle->Initialized == USB3DBG_DBG_CAB) {
+ //
+ // As detection is slow in seconds, wait for longer timeout for the first time.
+ // If first initialization is failed, we will try to enable debug device in the
+ // Poll function invoked by timer.
+ //
+ TimeOut = DivU64x32 (PcdGet64 (PcdUsbXhciDebugDetectTimeout), XHC_POLL_DELAY) + 1;
+ }
+
+ do {
+ //
+ // Check if debug device is in configured state
+ //
+ Dcctrl = XhcReadDebugReg (Handle, XHC_DC_DCCTRL);
+ if ((Dcctrl & BIT0) != 0) {
+ //
+ // Set the flag to indicate debug device is in configured state
+ //
+ Handle->Ready = TRUE;
+ break;
+ }
+ MicroSecondDelay (XHC_POLL_DELAY);
+ TimeOut--;
+ } while (TimeOut != 0);
+}
+
+/**
+ Initialize usb debug port hardware.
+
+ @param Handle Debug port handle.
+
+ @retval TRUE The usb debug port hardware configuration is changed.
+ @retval FALSE The usb debug port hardware configuration is not changed.
+
+**/
+RETURN_STATUS
+EFIAPI
+InitializeUsbDebugHardware (
+ IN USB3_DEBUG_PORT_HANDLE *Handle
+ )
+{
+ RETURN_STATUS Status;
+ UINT8 *Buffer;
+ UINTN Index;
+ UINT8 TotalUsb3Port;
+ EFI_PHYSICAL_ADDRESS XhciOpRegister;
+
+ XhciOpRegister = Handle->XhciOpRegister;
+ TotalUsb3Port = MmioRead32 (((UINTN) Handle->XhciMmioBase + XHC_HCSPARAMS1_OFFSET)) >> 24;
+
+ if (Handle->Initialized == USB3DBG_NOT_ENABLED) {
+ //
+ // If XHCI supports debug capability, hardware resource has been allocated,
+ // but it has not been enabled, try to enable again.
+ //
+ goto Enable;
+ }
+
+ //
+ // Initialize for PEI phase when AllocatePages can work.
+ // Allocate data buffer with max packet size for data read and data poll.
+ // Allocate data buffer for data write.
+ //
+ Buffer = AllocateAlignBuffer (XHCI_DEBUG_DEVICE_MAX_PACKET_SIZE * 2 + USB3_DEBUG_PORT_WRITE_MAX_PACKET_SIZE);
+ if (Buffer == NULL) {
+ //
+ // AllocatePages can not still work now, return fail and do not initialize now.
+ //
+ return RETURN_NOT_READY;
+ }
+
+ //
+ // Reset port to get debug device discovered
+ //
+ for (Index = 0; Index < TotalUsb3Port; Index++) {
+ XhcSetR32Bit ((UINTN)XhciOpRegister + XHC_PORTSC_OFFSET + Index * 0x10, BIT4);
+ MicroSecondDelay (10 * 1000);
+ }
+
+ //
+ // Construct the buffer for read, poll and write.
+ //
+ Handle->UrbIn.Data = (EFI_PHYSICAL_ADDRESS)(UINTN) Buffer;
+ Handle->Data = (EFI_PHYSICAL_ADDRESS)(UINTN) Buffer + XHCI_DEBUG_DEVICE_MAX_PACKET_SIZE;
+ Handle->UrbOut.Data = Handle->UrbIn.Data + XHCI_DEBUG_DEVICE_MAX_PACKET_SIZE * 2;
+
+ //
+ // Initialize event ring
+ //
+ ZeroMem (&Handle->EventRing, sizeof (EVENT_RING));
+ Status = CreateEventRing (Handle, &Handle->EventRing);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Init IN and OUT endpoint context
+ //
+ Status = CreateDebugCapabilityContext (Handle);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Init DCDDI1 and DCDDI2
+ //
+ XhcWriteDebugReg (
+ Handle,
+ XHC_DC_DCDDI1,
+ (UINT32)((XHCI_DEBUG_DEVICE_VENDOR_ID << 16) | XHCI_DEBUG_DEVICE_PROTOCOL)
+ );
+
+ XhcWriteDebugReg (
+ Handle,
+ XHC_DC_DCDDI2,
+ (UINT32)((XHCI_DEBUG_DEVICE_REVISION << 16) | XHCI_DEBUG_DEVICE_PRODUCT_ID)
+ );
+
+Enable:
+ if ((Handle->Initialized == USB3DBG_NOT_ENABLED) && (!Handle->ChangePortPower)) {
+ //
+ // If the first time detection is failed, turn port power off and on in order to
+ // reset port status this time, then try to check if debug device is ready again.
+ //
+ for (Index = 0; Index < TotalUsb3Port; Index++) {
+ XhcClearR32Bit ((UINTN)XhciOpRegister + XHC_PORTSC_OFFSET + Index * 0x10, BIT9);
+ MicroSecondDelay (XHC_DEBUG_PORT_ON_OFF_DELAY);
+ XhcSetR32Bit ((UINTN)XhciOpRegister + XHC_PORTSC_OFFSET + Index * 0x10, BIT9);
+ MicroSecondDelay (XHC_DEBUG_PORT_ON_OFF_DELAY);
+ Handle->ChangePortPower = TRUE;
+ }
+ }
+
+ //
+ // Set DCE bit and LSE bit to "1" in DCCTRL in first initialization
+ //
+ XhcSetDebugRegBit (Handle, XHC_DC_DCCTRL, BIT1|BIT31);
+
+ XhcDetectDebugCapabilityReady (Handle);
+
+ Status = RETURN_SUCCESS;
+ if (!Handle->Ready) {
+ Handle->Initialized = USB3DBG_NOT_ENABLED;
+ Status = RETURN_NOT_READY;
+ } else {
+ Handle->Initialized = USB3DBG_ENABLED;
+ }
+
+ return Status;
+}
+
+/**
+ Read data from debug device and save the data in buffer.
+
+ Reads NumberOfBytes data bytes from a debug device into the buffer
+ specified by Buffer. The number of bytes actually read is returned.
+ If the return value is less than NumberOfBytes, then the rest operation failed.
+ If NumberOfBytes is zero, then return 0.
+
+ @param Handle Debug port handle.
+ @param Buffer Pointer to the data buffer to store the data read from the debug device.
+ @param NumberOfBytes Number of bytes which will be read.
+ @param Timeout Timeout value for reading from debug device. It unit is Microsecond.
+
+ @retval 0 Read data failed, no data is to be read.
+ @retval >0 Actual number of bytes read from debug device.
+
+**/
+UINTN
+EFIAPI
+DebugPortReadBuffer (
+ IN DEBUG_PORT_HANDLE Handle,
+ IN UINT8 *Buffer,
+ IN UINTN NumberOfBytes,
+ IN UINTN Timeout
+ )
+{
+ USB3_DEBUG_PORT_HANDLE *UsbDebugPortHandle;
+ RETURN_STATUS Status;
+ UINT8 Index;
+ UINT8 *Data;
+
+ if (NumberOfBytes != 1 || Buffer == NULL || Timeout != 0) {
+ return 0;
+ }
+
+ //
+ // If Handle is NULL, it means memory is ready for use.
+ // Use global variable to store handle value.
+ //
+ if (Handle == NULL) {
+ UsbDebugPortHandle = &mDebugCommunicationLibUsb3DebugPortHandle;
+ } else {
+ UsbDebugPortHandle = (USB3_DEBUG_PORT_HANDLE *)Handle;
+ }
+
+ if (UsbDebugPortHandle->Initialized == USB3DBG_NO_DBG_CAB) {
+ return 0;
+ }
+
+ if (NeedReinitializeHardware(UsbDebugPortHandle)) {
+ Status = InitializeUsbDebugHardware (UsbDebugPortHandle);
+ if (RETURN_ERROR(Status)) {
+ return 0;
+ }
+ }
+
+ Data = (UINT8 *)(UINTN)UsbDebugPortHandle->Data;
+
+ //
+ // Read data from buffer
+ //
+ if (UsbDebugPortHandle->DataCount < 1) {
+ return 0;
+ } else {
+ *Buffer = Data[0];
+
+ for (Index = 0; Index < UsbDebugPortHandle->DataCount - 1; Index++) {
+ if ((Index + 1) >= XHCI_DEBUG_DEVICE_MAX_PACKET_SIZE) {
+ return 0;
+ }
+ Data[Index] = Data[Index + 1];
+ }
+ UsbDebugPortHandle->DataCount = (UINT8)(UsbDebugPortHandle->DataCount - 1);
+ return 1;
+ }
+}
+
+/**
+ Write data from buffer to debug device.
+
+ Writes NumberOfBytes data bytes from Buffer to the debug device.
+ The number of bytes actually written to the debug device is returned.
+ If the return value is less than NumberOfBytes, then the write operation failed.
+ If NumberOfBytes is zero, then return 0.
+
+ @param Handle Debug port handle.
+ @param Buffer Pointer to the data buffer to be written.
+ @param NumberOfBytes Number of bytes to written to the debug device.
+
+ @retval 0 NumberOfBytes is 0.
+ @retval >0 The number of bytes written to the debug device.
+ If this value is less than NumberOfBytes, then the read operation failed.
+
+**/
+UINTN
+EFIAPI
+DebugPortWriteBuffer (
+ IN DEBUG_PORT_HANDLE Handle,
+ IN UINT8 *Buffer,
+ IN UINTN NumberOfBytes
+ )
+{
+ USB3_DEBUG_PORT_HANDLE *UsbDebugPortHandle;
+ RETURN_STATUS Status;
+ UINTN Sent;
+ UINTN Total;
+ EFI_PHYSICAL_ADDRESS XhciMmioBase;
+ UINTN Index;
+
+ if (NumberOfBytes == 0 || Buffer == NULL) {
+ return 0;
+ }
+
+ Sent = 0;
+ Total = 0;
+
+ //
+ // If Handle is NULL, it means memory is ready for use.
+ // Use global variable to store handle value.
+ //
+ if (Handle == NULL) {
+ UsbDebugPortHandle = &mDebugCommunicationLibUsb3DebugPortHandle;
+ } else {
+ UsbDebugPortHandle = (USB3_DEBUG_PORT_HANDLE *)Handle;
+ }
+
+ if (UsbDebugPortHandle->Initialized == USB3DBG_NO_DBG_CAB) {
+ return 0;
+ }
+
+ //
+ // MMIO base address is possible to clear, set it if it is cleared. (XhciMemorySpaceClose in PchUsbCommon.c)
+ //
+ XhciMmioBase = ProgramXhciBaseAddress ();
+
+ UpdateXhcResource (UsbDebugPortHandle, XhciMmioBase);
+
+ if (NeedReinitializeHardware(UsbDebugPortHandle)) {
+ Status = InitializeUsbDebugHardware (UsbDebugPortHandle);
+ if (RETURN_ERROR(Status)) {
+ return 0;
+ }
+ }
+
+ //
+ // When host is trying to send data, write will be blocked.
+ // Poll to see if there is any data sent by host at first.
+ //
+ DebugPortPollBuffer (Handle);
+
+ Index = 0;
+ while ((Total < NumberOfBytes)) {
+ if (NumberOfBytes - Total > USB3_DEBUG_PORT_WRITE_MAX_PACKET_SIZE) {
+ Sent = USB3_DEBUG_PORT_WRITE_MAX_PACKET_SIZE;
+ } else {
+ Sent = (UINT8)(NumberOfBytes - Total);
+ }
+ Status = XhcDataTransfer (UsbDebugPortHandle, EfiUsbDataOut, Buffer + Total, &Sent, DATA_TRANSFER_WRITE_TIMEOUT);
+ Total += Sent;
+ }
+
+ return Total;
+}
+
+/**
+ Polls a debug device to see if there is any data waiting to be read.
+
+ Polls a debug device to see if there is any data waiting to be read.
+ If there is data waiting to be read from the debug device, then TRUE is returned.
+ If there is no data waiting to be read from the debug device, then FALSE is returned.
+
+ @param Handle Debug port handle.
+
+ @retval TRUE Data is waiting to be read from the debug device.
+ @retval FALSE There is no data waiting to be read from the serial device.
+
+**/
+BOOLEAN
+EFIAPI
+DebugPortPollBuffer (
+ IN DEBUG_PORT_HANDLE Handle
+ )
+{
+ USB3_DEBUG_PORT_HANDLE *UsbDebugPortHandle;
+ UINTN Length;
+ RETURN_STATUS Status;
+ EFI_PHYSICAL_ADDRESS XhciMmioBase;
+
+ //
+ // If Handle is NULL, it means memory is ready for use.
+ // Use global variable to store handle value.
+ //
+ if (Handle == NULL) {
+ UsbDebugPortHandle = &mDebugCommunicationLibUsb3DebugPortHandle;
+ } else {
+ UsbDebugPortHandle = (USB3_DEBUG_PORT_HANDLE *)Handle;
+ }
+
+ if (UsbDebugPortHandle->Initialized == USB3DBG_NO_DBG_CAB) {
+ return 0;
+ }
+
+ XhciMmioBase = ProgramXhciBaseAddress ();
+ UpdateXhcResource (UsbDebugPortHandle, XhciMmioBase);
+
+ if (NeedReinitializeHardware(UsbDebugPortHandle)) {
+ Status = InitializeUsbDebugHardware(UsbDebugPortHandle);
+ if (RETURN_ERROR(Status)) {
+ return FALSE;
+ }
+ }
+
+ //
+ // If the data buffer is not empty, then return TRUE directly.
+ // Otherwise initialize a usb read transaction and read data to internal data buffer.
+ //
+ if (UsbDebugPortHandle->DataCount != 0) {
+ return TRUE;
+ }
+
+ //
+ // Read data as much as we can
+ //
+ Length = XHCI_DEBUG_DEVICE_MAX_PACKET_SIZE;
+ XhcDataTransfer (Handle, EfiUsbDataIn, (VOID *)(UINTN)UsbDebugPortHandle->Data, &Length, DATA_TRANSFER_POLL_TIMEOUT);
+
+ if (Length > XHCI_DEBUG_DEVICE_MAX_PACKET_SIZE) {
+ return FALSE;
+ }
+
+ if (Length == 0) {
+ return FALSE;
+ }
+
+ //
+ // Store data into internal buffer for use later
+ //
+ UsbDebugPortHandle->DataCount = (UINT8) Length;
+ return TRUE;
+}
+
+/**
+ Initialize the debug port.
+
+ If Function is not NULL, Debug Communication Library will call this function
+ by passing in the Context to be the first parameter. If needed, Debug Communication
+ Library will create one debug port handle to be the second argument passing in
+ calling the Function, otherwise it will pass NULL to be the second argument of
+ Function.
+
+ If Function is NULL, and Context is not NULL, the Debug Communication Library could
+ a) Return the same handle as passed in (as Context parameter).
+ b) Ignore the input Context parameter and create new handle to be returned.
+
+ If parameter Function is NULL and Context is NULL, Debug Communication Library could
+ created a new handle if needed and return it, otherwise it will return NULL.
+
+ @param[in] Context Context needed by callback function; it was optional.
+ @param[in] Function Continue function called by Debug Communication library;
+ it was optional.
+
+ @return The debug port handle created by Debug Communication Library if Function
+ is not NULL.
+
+**/
+DEBUG_PORT_HANDLE
+EFIAPI
+DebugPortInitialize (
+ IN VOID *Context,
+ IN DEBUG_PORT_CONTINUE Function
+ )
+{
+ RETURN_STATUS Status;
+ USB3_DEBUG_PORT_HANDLE Handle;
+ USB3_DEBUG_PORT_HANDLE *UsbDebugPortHandle;
+
+ //
+ // Validate the PCD PcdDebugPortHandleBufferSize value
+ //
+ ASSERT (PcdGet16 (PcdDebugPortHandleBufferSize) == sizeof (USB3_DEBUG_PORT_HANDLE));
+
+ if (Function == NULL && Context != NULL) {
+ UsbDebugPortHandle = (USB3_DEBUG_PORT_HANDLE *)Context;
+ } else {
+ ZeroMem(&Handle, sizeof (USB3_DEBUG_PORT_HANDLE));
+ UsbDebugPortHandle = &Handle;
+ }
+
+ if (Function == NULL && Context != NULL) {
+ return (DEBUG_PORT_HANDLE *) Context;
+ }
+
+ //
+ // Read 64-bit MMIO base address
+ //
+ UsbDebugPortHandle->XhciMmioBase = ProgramXhciBaseAddress ();
+
+ Status = CalculateUsbDebugPortMmioBase (UsbDebugPortHandle);
+ if (RETURN_ERROR (Status)) {
+ goto Exit;
+ }
+
+ if (NeedReinitializeHardware(&Handle)) {
+ Status = InitializeUsbDebugHardware (&Handle);
+ if (RETURN_ERROR(Status)) {
+ goto Exit;
+ }
+ }
+
+Exit:
+
+ if (Function != NULL) {
+ Function (Context, &Handle);
+ } else {
+ CopyMem(&mDebugCommunicationLibUsb3DebugPortHandle, &Handle, sizeof (USB3_DEBUG_PORT_HANDLE));
+ }
+
+ return (DEBUG_PORT_HANDLE)(UINTN)&mDebugCommunicationLibUsb3DebugPortHandle;
+}
|