summaryrefslogtreecommitdiff
path: root/ReferenceCode/Chipset/LynxPoint/PchInit/Dxe/PchUsbPrecondition.c
diff options
context:
space:
mode:
Diffstat (limited to 'ReferenceCode/Chipset/LynxPoint/PchInit/Dxe/PchUsbPrecondition.c')
-rw-r--r--ReferenceCode/Chipset/LynxPoint/PchInit/Dxe/PchUsbPrecondition.c522
1 files changed, 522 insertions, 0 deletions
diff --git a/ReferenceCode/Chipset/LynxPoint/PchInit/Dxe/PchUsbPrecondition.c b/ReferenceCode/Chipset/LynxPoint/PchInit/Dxe/PchUsbPrecondition.c
new file mode 100644
index 0000000..ea8f794
--- /dev/null
+++ b/ReferenceCode/Chipset/LynxPoint/PchInit/Dxe/PchUsbPrecondition.c
@@ -0,0 +1,522 @@
+/** @file
+ PCH USB precondition feature support in DXE phase
+
+@copyright
+ Copyright (c) 2012 - 2013 Intel Corporation. All rights reserved
+ This software and associated documentation (if any) is furnished
+ under a license and may only be used or copied in accordance
+ with the terms of the license. Except as permitted by such
+ license, no part of this software or documentation may be
+ reproduced, stored in a retrieval system, or transmitted in any
+ form or by any means without the express written consent of
+ Intel Corporation.
+
+ This file contains an 'Intel Peripheral Driver' and uniquely
+ identified as "Intel Reference Module" and is
+ licensed for Intel CPUs and chipsets under the terms of your
+ license agreement with Intel or your vendor. This file may
+ be modified by the user, subject to additional terms of the
+ license agreement
+
+**/
+#include "PchInit.h"
+#include "PchUsbPrecondition.h"
+#include "PchUsbCommon.h"
+
+#ifdef USB_PRECONDITION_ENABLE_FLAG
+//
+// Data referred by EHCI
+//
+extern USB_CONTROLLER EhciControllersMap[];
+
+//
+// Data referred by XHCI
+//
+UINTN *PORTSCxUSB2Ptr;
+UINTN *PORTSCxUSB3Ptr;
+
+//
+// Data referred by USB Precondition feature
+//
+EFI_USB_HC_PORT_PRECONDITION *mPrivatePreConditionList = NULL;
+
+//
+// This flag set when 50ms root port reset duration is satisified (Tdrstr). It is countered from
+// last root port reset.
+//
+BOOLEAN PchUsbRPortsRstDoneFlag = FALSE;
+
+//
+// All root ports reset continuously, so the reset starting time between first root port and last
+// root port should not exceed PCH ACPI timer High-to-Low transition frequency - 2.3435 seconds.
+//
+UINTN LastRPortResetTicks = 0;
+
+//
+// Tdrstr for all root portis satisfied as the following scenarios:
+//
+// |
+// |-> Reset all root ports of 1st HC, save tick_1 to LastRPortResetTicks
+// |
+// |-> Reset all root ports of 2nd HC, save tick_2 to LastRPortResetTicks
+// |
+// |-> IsRootPortReset () for is invoked by first call, wait until if delay for tick_2 is enough
+// | Set PchUsbRPortsRstDoneFlag = TRUE, return TRUE if the port is in the list
+// |
+// |-> IsRootPortReset () is invoked for the other HC, and PchUsbRPortsRstDoneFlag is set
+// | Return TRUE if the port is in the list
+//
+
+/**
+ Return current PCH PM1 timer value
+
+ @param[in] None
+
+ @retval PM1 timer value in 32 bit
+**/
+UINTN
+PchGetPchTimerTick (
+ VOID
+ )
+{
+ UINT16 AcpiBaseAddr;
+
+ AcpiBaseAddr = PciRead16 (
+ PCI_LIB_ADDRESS (DEFAULT_PCI_BUS_NUMBER_PCH,
+ PCI_DEVICE_NUMBER_PCH_LPC,
+ 0,
+ R_PCH_LPC_ACPI_BASE)
+ ) & B_PCH_LPC_ACPI_BASE_BAR;
+
+ return IoRead32 ((UINTN) (AcpiBaseAddr + R_PCH_ACPI_PM1_TMR)) & B_PCH_ACPI_PM1_TMR_VAL;
+
+}
+
+/**
+ Check if the required delay condition is satisified
+ Note: The delay can't be larger than PCH ACPI timer High-to-Low
+ transition frequency - 2.3435 seconds.
+
+ @param[in] InitialTicks Initial PM1 tick value
+ @param[in] RequiredStallInUs Required delay in us
+
+ @retval TRUE The required delay is satisified
+ @retval FALSE The required delay is not satisified
+**/
+BOOLEAN
+UsbTimeout (
+ IN UINTN InitialTicks,
+ IN UINTN RequiredStallInUs
+ )
+{
+ UINTN CurrentTick;
+ UINTN ExpiredTick;
+
+ //
+ // The timer frequency is 3.579545 MHz, so 1 us corresponds 3.58 clocks
+ //
+ ExpiredTick = RequiredStallInUs * 358 / 100 + InitialTicks + 1;
+ CurrentTick = PchGetPchTimerTick ();
+
+ //
+ // The High-to-Low transition will occur every 2.3435 seconds.
+ //
+ if (CurrentTick < InitialTicks) {
+ CurrentTick += V_PCH_ACPI_PM1_TMR_MAX_VAL;
+ }
+
+ if (CurrentTick > ExpiredTick){
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ Initialize usb global data and flag for reference
+
+ @param[in] None
+
+ @retval None
+**/
+VOID
+UsbInitGlobalData (
+ VOID
+ )
+{
+
+ ///
+ /// Set the flag to false and start to count time.
+ ///
+ PchUsbRPortsRstDoneFlag = FALSE;
+
+ ///
+ /// This is the latest root port reset, record it to ensure the Tdrstr is satisified.
+ ///
+ LastRPortResetTicks = PchGetPchTimerTick();
+ return;
+}
+
+/**
+ Check if the delay is enough since last root port reset
+
+ @param[in] None
+
+ @retval None
+**/
+VOID
+UsbTdrstrDelayCheck (
+ VOID
+ )
+{
+ UINTN i;
+
+ ///
+ /// If the latest root port reset done, and then for all root ports reset by
+ /// this protocol is ready. If we are in scenario#3, wait until delay time is enough. The flag
+ /// is set either by timer event or the waitting loop.
+ ///
+ for (i = 0; (PchUsbRPortsRstDoneFlag != TRUE) && (i < USB_ROOT_PORT_RESET_STALL_US/ USB_TDRSTR_CHECK_INTERVAL_US); i++) {
+ if (UsbTimeout (LastRPortResetTicks, USB_ROOT_PORT_RESET_STALL_US)) {
+ PchUsbRPortsRstDoneFlag = TRUE;
+ LastRPortResetTicks = 0;
+ break;
+ }
+ PchPmTimerStall (USB_TDRSTR_CHECK_INTERVAL_US);
+ }
+
+ return ;
+}
+
+/**
+ Check if the queried port is reset by USB precondition feature or not
+
+ @param[in] This EFI_USB_HC_PORT_PRECONDITION instance
+ @param[in] PortNumber The root port number (started by zero) to be queried
+
+ @retval TRUE The root port is reset done
+ @retval FALSE The root port is not reset
+**/
+BOOLEAN
+EFIAPI
+IsEhcRootPortReset (
+ IN EFI_USB_HC_PORT_PRECONDITION *This,
+ IN UINT8 PortNumber
+ )
+{
+ USB_EHCI_PRECONDITION_DEV *EhcPreCondition;
+
+ EhcPreCondition = EHC_PRECONDITION_FROM_THIS (This);
+
+ ///
+ /// For the EHCI on PCH, the root port 0 is always RMH and existing.
+ /// PCH USB precondition feature resets the root port 0 on PCH EHCI only
+ /// If the signature, PortNumber, or PortResetBitMap is invalid, return
+ /// FALSE directly. Otherwise, return TRUE when required reset signal delay
+ /// is satisified.
+ ///
+ if ((EhcPreCondition->Signature != EHCI_PRECONDITION_DEV_SIGN) ||
+ (PortNumber != 0) ||
+ (EhcPreCondition->PortResetBitMap == 0)) {
+ return FALSE;
+ }
+
+ //
+ // Drive the reset signal on root port for at least 50ms(Tdrstr). Check USB 2.0 Spec
+ // section 7.1.7.5 for timing requirements.
+ //
+ if (!PchUsbRPortsRstDoneFlag) {
+ UsbTdrstrDelayCheck ();
+ }
+ return TRUE;
+}
+
+/**
+ Perform USB precondition on EHCI, it is the root port reset on
+ installed USB device in DXE phase
+
+ @param[in] Device The device number of the EHCI
+ @param[in] EhciMmioBase Memory base address of EHCI Controller
+
+ @retval None
+**/
+VOID
+EhciPrecondition (
+ IN UINT8 Device,
+ IN UINT32 EhciMmioBase
+ )
+{
+ UINTN i;
+ UINT32 Data32;
+ USB_EHCI_PRECONDITION_DEV *EhcPreCondition;
+ EFI_USB_HC_LOCATION EhcLocation = {0, 0, 0, 0};
+
+ //
+ // Check if all ports routed to this EHCI successfully, if not, exit directly
+ //
+ if ((MmioRead32 (EhciMmioBase + R_PCH_EHCI_CONFIGFLAG) & BIT0) == 0) {
+ return;
+ }
+
+ EhcPreCondition = AllocateZeroPool (sizeof (USB_EHCI_PRECONDITION_DEV));
+ if (EhcPreCondition == NULL) {
+ return;
+ }
+
+ ///
+ /// This is Intel RMH behind EHCI, and it is on root hub port 0. Reset the root hub port0.
+ ///
+ for (i = 0; i < (USB_HC_RESET_STALL_US/ 10); i++) {
+ if ((MmioRead32 (EhciMmioBase + R_PCH_EHCI_PORTSC0) & BIT0) == 0) {
+ //
+ // Root port 0 on EHCI is RMH, check the CCS bit before reset port.
+ // If the CCS bit is not true, wait and poll until timeout
+ //
+ PchPmTimerStall (10);
+ } else {
+ break;
+ }
+ }
+
+ Data32 = MmioRead32 (EhciMmioBase + R_PCH_EHCI_PORTSC0);
+
+ //
+ // Mask of the port change bits, they are WC (write clean).
+ // Set one to PortReset bit and must also set zero to PortEnable bit
+ //
+ Data32 &= ~B_PCH_EHCI_PORTSC0_CHANGE_ENABLE_MASK;
+ Data32 |= B_PCH_EHCI_PORTSC0_RESET;
+ MmioWrite32 ((EhciMmioBase + R_PCH_EHCI_PORTSC0), Data32);
+
+ UsbInitGlobalData ();
+ EhcPreCondition->Signature = EHCI_PRECONDITION_DEV_SIGN;
+
+ //
+ // RMH is at root hub port 0
+ //
+ EhcPreCondition->PortResetBitMap = BIT0;
+
+ //
+ // Suggest required delay time defined by specification per RMH implementation. Reserved so far
+ //
+ ZeroMem (&(EhcPreCondition->Protocol.Timing), sizeof (EFI_USB_PORT_ENUM_TIMING_TABLE));
+
+ EhcLocation.DeviceNumber = (UINTN) Device;
+ CopyMem (&(EhcPreCondition->Protocol.Location), &EhcLocation, sizeof (EFI_USB_HC_LOCATION));
+
+ EhcPreCondition->Protocol.IsRootPortReset = IsEhcRootPortReset;
+ EhcPreCondition->Protocol.Next = mPrivatePreConditionList;
+ mPrivatePreConditionList = &(EhcPreCondition->Protocol);
+}
+
+/**
+ Check if the queried port is reset by USB precondition feature or not. This service must be called when
+ XHC is in Run(R/S = '1') mode per XHCI specification requirement.
+
+ @param[in] This EFI_USB_HC_PORT_PRECONDITION instance
+ @param[in] PortNumber The root port number (started by zero) to be queried
+
+ @retval TRUE The root port is reset done
+ @retval FALSE The root port is not reset
+**/
+BOOLEAN
+EFIAPI
+IsXhcRootPortReset (
+ IN EFI_USB_HC_PORT_PRECONDITION *This,
+ IN UINT8 PortNumber
+ )
+{
+ UINT32 UsbPort;
+ UINT32 Data32;
+ UINT32 XhciMmioBase;
+ UINT32 XhciPciMmBase;
+ USB_XHCI_PRECONDITION_DEV *XhcPreCondition;
+ BOOLEAN ResumeFlag;
+
+ XhcPreCondition = XHC_PRECONDITION_FROM_THIS (This);
+
+ ///
+ /// If the signature, PortNumber, or PortResetBitMap is invalid, return
+ /// FALSE directly. Otherwise, return TRUE when required reset signal delay
+ /// is satisified.
+ ///
+ if ((XhcPreCondition->PortResetBitMap == 0) || (XhcPreCondition->Signature != XHCI_PRECONDITION_DEV_SIGN)) {
+ return FALSE;
+ }
+
+ ///
+ /// Resume all USB2 protocol ports by first call
+ ///
+ if (XhcPreCondition->PORTSCxResumeDoneFlag != TRUE) {
+ //
+ // Drive the reset signal on root port for at least 50ms(Tdrstr). Check USB 2.0 Spec
+ // section 7.1.7.5 for timing requirements.
+ //
+ if (!PchUsbRPortsRstDoneFlag) {
+ UsbTdrstrDelayCheck ();
+ }
+
+ XhciPciMmBase = (UINT32) MmPciAddress (
+ 0,
+ (UINT8) XhcPreCondition->Protocol.Location.BusNumber,
+ (UINT8) XhcPreCondition->Protocol.Location.DeviceNumber,
+ (UINT8) XhcPreCondition->Protocol.Location.FunctionNumber,
+ 0
+ );
+
+ XhciMmioBase = MmioRead32 (XhciPciMmBase + R_PCH_XHCI_MEM_BASE) & (~0xF);
+ ResumeFlag = FALSE;
+
+ ///
+ /// For USB2 protocol port on XHCI, the reset done port will enter U3 state once the HC is halted
+ /// To recovery the USB2 protocol port from U3 to U0, SW should:
+ /// 1. SW shall ensure that the XHC is in Run mode prior to transitioning a root hub port from Resume to
+ /// the U0 state.
+ /// 2. Write a "15" (Resume) to the PLS, XHC shall transmit the resume signaling within 1ms (Tursm)
+ /// 3. SW shall ensure that resume is signaled for at least 20 ms (Tdrsmdn) from the write of Resume
+ /// 4. After Tdrsmdn is complete, SW shall write a "0"(U0) to the PLS field
+ ///
+ for (UsbPort = 0; UsbPort < XhcPreCondition->HsPortCount; UsbPort++) {
+ if (((UINT32)(1 << UsbPort) & XhcPreCondition->PortResetBitMap) != 0) {
+ Data32 = MmioRead32 (XhciMmioBase + PORTSCxUSB2Ptr[UsbPort]);
+ if ((Data32 & B_PCH_XHCI_PORTSCXUSB2_CCS) != 0) {
+ Data32 &= ~B_PCH_XHCI_PORT_CHANGE_ENABLE_MASK;
+ Data32 |= (B_PCH_XHCI_USB2_U3_EXIT + B_PCH_XHCI_PORTSCXUSB2_PP + B_PCH_XHCI_PORTSCXUSB2_LWS);
+ MmioWrite32 (
+ XhciMmioBase + PORTSCxUSB2Ptr[UsbPort],
+ Data32
+ );
+ ResumeFlag = TRUE;
+ } else {
+ //
+ // The CCS bit of this port disappears, it may be caused by the following reasons:
+ // 1. Link training is successfully now, the CCS shows on correct USB speed port, i.e. USB3
+ // speed if it is USB3 device.
+ // 2. The device is removed.
+ // Ignore this port due to there is no device on it now.
+ //
+ XhcPreCondition->PortResetBitMap &= ~(UINT32) (1 << UsbPort);
+ }
+ }
+ }
+
+ if (ResumeFlag) {
+ //
+ // There is one root port resuming from U3 at least.
+ //
+ PchPmTimerStall (20 * 1000);
+ for (UsbPort = 0; UsbPort < XhcPreCondition->HsPortCount; UsbPort++) {
+ if (((UINT32)(1 << UsbPort) & XhcPreCondition->PortResetBitMap) != 0 ) {
+ Data32 = (B_PCH_XHCI_PORTSCXUSB2_PP + B_PCH_XHCI_PORTSCXUSB2_LWS + B_PCH_XHCI_PORTSCXUSB2_CCS);
+ MmioWrite32 (
+ XhciMmioBase + PORTSCxUSB2Ptr[UsbPort],
+ Data32
+ );
+ }
+ }
+ }
+ XhcPreCondition->PORTSCxResumeDoneFlag = TRUE;
+ }
+
+ if (XhcPreCondition->PORTSCxResumeDoneFlag == TRUE) {
+ //
+ // If the signature, PortNumber, or PortResetBitMap is invalid, return
+ // FALSE directly. Otherwise, return TRUE when required reset signal delay
+ // is satisified.
+ //
+ if (((UINT32)(1 << PortNumber) & XhcPreCondition->PortResetBitMap) != 0) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ Perform USB precondition on XHCI, it is the root port reset on
+ installed USB device in DXE phase
+
+ @param[in] BusNumber The Bus number of the XHCI
+ @param[in] Device The device number of the XHCI
+ @param[in] Function The function number of the XHCI
+ @param[in] XhciMmioBase Memory base address of XHCI Controller
+ @param[in] XhciUSB2Ptr Pointer to USB2 protocol port register
+ @param[in] HsPortCount The number of USB2 protocol port supported by this XHCI
+
+ @retval None
+**/
+VOID
+XhciPrecondition (
+ IN UINT8 BusNumber,
+ IN UINT8 Device,
+ IN UINT8 Function,
+ IN UINT32 XhciMmioBase,
+ IN UINTN *XhciUSB2Ptr,
+ IN UINTN HsPortCount,
+ IN UINTN *XhciUSB3Ptr,
+ IN UINTN SsPortCount
+ )
+{
+ UINT32 UsbPort;
+ UINT32 Data32;
+ USB_XHCI_PRECONDITION_DEV *XhcPreCondition;
+ EFI_USB_HC_LOCATION XhcLocation = {0, 0, 0, 0};
+
+ XhcPreCondition = AllocateZeroPool (sizeof (USB_XHCI_PRECONDITION_DEV));
+ if (XhcPreCondition == NULL) {
+ return;
+ }
+
+ PORTSCxUSB2Ptr = XhciUSB2Ptr;
+ PORTSCxUSB3Ptr = XhciUSB3Ptr;
+ XhcPreCondition->Signature = XHCI_PRECONDITION_DEV_SIGN;
+
+ XhcPreCondition->HsPortCount = HsPortCount;
+
+ for (UsbPort = 0; UsbPort < HsPortCount; UsbPort++) {
+ Data32 = MmioRead32 (XhciMmioBase + PORTSCxUSB2Ptr[UsbPort]);
+ if ((Data32 & B_PCH_XHCI_PORTSCXUSB2_CCS) != 0) {
+ Data32 &= ~B_PCH_XHCI_PORTSCXUSB2_PED;
+ Data32 |= B_PCH_XHCI_PORTSCXUSB2_PR | B_PCH_XHCI_PORTSCXUSB2_PP;
+ MmioWrite32 (
+ XhciMmioBase + PORTSCxUSB2Ptr[UsbPort],
+ Data32
+ );
+ //
+ // PortSC registers in PCH XHCI is counted from HS ports
+ //
+ XhcPreCondition->PortResetBitMap |= (UINT32) (1 << UsbPort);
+ }
+ }
+#ifdef EFI_DEBUG
+ DEBUG ((EFI_D_INFO, "XhciPreconditionDxe - USB3PORTSC Start\n"));
+ for (UsbPort = 0; UsbPort < SsPortCount; UsbPort++) {
+ DEBUG ((EFI_D_INFO, "USB3Port %x - %x\n", UsbPort, MmioRead32 (XhciMmioBase + XhciUSB3Ptr[UsbPort])));
+ }
+#endif
+
+ //
+ // Clear WRC bit for all USB3 PORTSC
+ //
+ for (UsbPort = 0; UsbPort < SsPortCount; UsbPort++) {
+ MmioAndThenOr32 (
+ XhciMmioBase + PORTSCxUSB3Ptr[UsbPort],
+ (UINT32)~ (B_PCH_XHCI_PORTSCXUSB3_CHANGE_ENABLE_MASK),
+ B_PCH_XHCI_PORTSCXUSB3_WRC
+ );
+ }
+#ifdef EFI_DEBUG
+ DEBUG ((EFI_D_INFO, "XhciPreconditionDxe - USB3PORTSC Done\n"));
+ for (UsbPort = 0; UsbPort < SsPortCount; UsbPort++) {
+ DEBUG ((EFI_D_INFO, "USB3Port %x - %x\n", UsbPort, MmioRead32 (XhciMmioBase + XhciUSB3Ptr[UsbPort])));
+ }
+#endif
+ UsbInitGlobalData ();
+ XhcLocation.DeviceNumber = (UINTN) Device;
+ XhcLocation.FunctionNumber = (UINTN) Function;
+ CopyMem (&(XhcPreCondition->Protocol.Location), &XhcLocation, sizeof (EFI_USB_HC_LOCATION));
+ XhcPreCondition->Protocol.IsRootPortReset = IsXhcRootPortReset;
+ XhcPreCondition->PORTSCxResumeDoneFlag = FALSE;
+ XhcPreCondition->Protocol.Next = mPrivatePreConditionList;
+ mPrivatePreConditionList = &(XhcPreCondition->Protocol);
+}
+
+#endif // USB_PRECONDITION_ENABLE_FLAG