/** @file Thunderbolt(TM) Pei Library Copyright (c) 2020, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** Is host router (For dTBT) or End Point (For iTBT) present before sleep @param[in] ControllerType - DTBT_CONTROLLER or ITBT_CONTROLLER @param[in] Controller - Controller begin offset of CMOS @Retval TRUE There is a TBT HostRouter presented before sleep @Retval FALSE There is no TBT HostRouter presented before sleep BOOLEAN IsHostRouterPresentBeforeSleep( IN UINT8 ControllerType, IN UINT8 Controller ) { UINT8 SavedState; SavedState = (UINT8)GetTbtHostRouterStatus(); if (ControllerType == DTBT_CONTROLLER){ return ((SavedState & (DTBT_SAVE_STATE_OFFSET << Controller)) == (DTBT_SAVE_STATE_OFFSET << Controller)); } else { if (ControllerType == ITBT_CONTROLLER) { return ((SavedState & (ITBT_SAVE_STATE_OFFSET << Controller)) == (ITBT_SAVE_STATE_OFFSET << Controller)); } } return 0; } **/ /** Execute TBT PCIE2TBT_SX_EXIT_TBT_CONNECTED Mail Box Command for S4 mode with PreBootAclEnable @param[in] Bus Bus number for Host Router (DTBT) @param[in] Device Device number for Host Router (DTBT) @param[in] Function Function number for Host Router (DTBT) @param[in] Timeout Time out with 100 ms garnularity @Retval true if command executes succesfully **/ BOOLEAN TbtSetPcie2TbtSxExitCommandWithPreBootAclEnable( IN UINT8 Bus, IN UINT8 Device, IN UINT8 Function, IN UINT32 Timeout ) { UINT64 Pcie2Tbt; UINT64 Tbt2Pcie; UINT32 RegisterValue; BOOLEAN ReturnFlag; UINT32 Command; GET_TBT2PCIE_REGISTER_ADDRESS(0, Bus, Device, Function, Tbt2Pcie) GET_PCIE2TBT_REGISTER_ADDRESS(0, Bus, Device, Function, Pcie2Tbt) // If PreBootAcl is Enable, we need to enable DATA bit while sending SX EXIT MAIL BOX Command Command = (1 << 8) | PCIE2TBT_SX_EXIT_TBT_CONNECTED; PciSegmentWrite32 (Pcie2Tbt, Command | PCIE2TBT_VLD_B); ReturnFlag = InternalWaitforCommandCompletion(Tbt2Pcie, Timeout, TRUE, &RegisterValue); PciSegmentWrite32(Pcie2Tbt, 0); return ReturnFlag; } /** Set the Sleep Mode if the HR is up. @param[in] Bus Bus number for Host Router (DTBT) @param[in] Device Device number for Host Router (DTBT) @param[in] Function Function number for Host Router (DTBT) **/ VOID TbtSetSxMode( IN UINT8 Bus, IN UINT8 Device, IN UINT8 Function, IN UINT8 TbtBootOn ) { UINT64 TbtUsDevId; UINT64 Tbt2Pcie; UINT32 RegVal; UINT32 MaxLoopCount; UINTN Delay; UINT8 RetCode; EFI_BOOT_MODE BootMode; EFI_STATUS Status; TbtUsDevId = PCI_SEGMENT_LIB_ADDRESS(0, Bus, Device, Function, 0); GET_TBT2PCIE_REGISTER_ADDRESS(0, Bus, Device, Function, Tbt2Pcie) MaxLoopCount = TBT_5S_TIMEOUT; // Wait 5 sec Delay = 100 * 1000; RetCode = 0x62; Status = PeiServicesGetBootMode(&BootMode); ASSERT_EFI_ERROR(Status); if ((BootMode == BOOT_ON_S4_RESUME) && (TbtBootOn == 2)) { MaxLoopCount = TBT_3S_TIMEOUT; if (!TbtSetPcie2TbtSxExitCommandWithPreBootAclEnable(Bus, Device, Function, MaxLoopCount)) { // // Nothing to wait, HR is not responsive // return; } } else { if (!TbtSetPcie2TbtCommand(PCIE2TBT_SX_EXIT_TBT_CONNECTED, Bus, Device, Function, MaxLoopCount)) { // // Nothing to wait, HR is not responsive // return; } } DEBUG((DEBUG_INFO, "Wait for Dev ID != 0xFF\n")); while (MaxLoopCount-- > 0) { // // Check what HR still here // RegVal = PciSegmentRead32(Tbt2Pcie); if (0xFFFFFFFF == RegVal) { RetCode = 0x6F; break; } // // Check completion of TBT link // RegVal = PciSegmentRead32(TbtUsDevId); if (0xFFFFFFFF != RegVal) { RetCode = 0x61; break; } MicroSecondDelay(Delay); } DEBUG((DEBUG_INFO, "Return code = 0x%x\n", RetCode)); } /** set tPCH25 Timing to 10 ms for DTBT. @param[in] PEI_TBT_POLICY PeiTbtConfig @retval EFI_SUCCESS The function completes successfully @retval EFI_UNSUPPORTED dTBT is not supported. **/ EFI_STATUS EFIAPI DTbtSetTPch25Timing ( IN PEI_TBT_POLICY *PeiTbtConfig ) { DEBUG ((DEBUG_INFO, "DTbtSetTPch25Timing call Inside\n")); UINT32 PchPwrmBase; // //During boot, reboot and wake tPCH25 Timing should be set to 10 ms // MmioOr32 ( (UINTN) (PchPwrmBase + R_PCH_PWRM_CFG), (BIT0 | BIT1) ); DEBUG((DEBUG_INFO, "DTbtSetTPch25Timing call Return\n")); return EFI_SUCCESS; } /** Do ForcePower for DTBT Controller @param[in] PEI_TBT_POLICY PeiTbtConfig @retval EFI_SUCCESS The function completes successfully @retval EFI_UNSUPPORTED dTBT is not supported. **/ EFI_STATUS EFIAPI DTbtForcePower ( IN PEI_TBT_POLICY *PeiTbtConfig ) { DEBUG ((DEBUG_INFO, "DTbtForcePower call Inside\n")); if (PeiTbtConfig->DTbtCommonConfig.Gpio3ForcePwr) { DEBUG((DEBUG_INFO, "ForcePwrGpio.GpioPad = %x \n", PeiTbtConfig-> DTbtControllerConfig.ForcePwrGpio.GpioPad)); ForceDtbtPower(PeiTbtConfig-> DTbtControllerConfig.ForcePwrGpio.GpioAccessType,PeiTbtConfig-> DTbtControllerConfig.ForcePwrGpio.Expander, PeiTbtConfig-> DTbtControllerConfig.ForcePwrGpio.GpioPad, PeiTbtConfig-> DTbtControllerConfig.ForcePwrGpio.GpioLevel); DEBUG((DEBUG_INFO, "ForceDtbtPower asserted \n")); MicroSecondDelay(PeiTbtConfig->DTbtCommonConfig.Gpio3ForcePwrDly * 1000); DEBUG((DEBUG_INFO, "Delay after ForceDtbtPower = 0x%x ms \n", PeiTbtConfig->DTbtCommonConfig.Gpio3ForcePwrDly)); } DEBUG ((DEBUG_INFO, "DTbtForcePower call Return\n")); return EFI_SUCCESS; } /** Clear VGA Registers for DTBT. @param[in] PEI_TBT_POLICY PeiTbtConfig @retval EFI_SUCCESS The function completes successfully @retval EFI_UNSUPPORTED dTBT is not supported. **/ EFI_STATUS EFIAPI DTbtClearVgaRegisters ( IN PEI_TBT_POLICY *PeiTbtConfig ) { UINTN RpDev; UINTN RpFunc; EFI_STATUS Status; UINT64 BridngeBaseAddress; UINT16 Data16; DEBUG ((DEBUG_INFO, "DTbtClearVgaRegisters call Inside\n")); Status = EFI_SUCCESS; Status = GetDTbtRpDevFun(PeiTbtConfig-> DTbtControllerConfig.Type, PeiTbtConfig-> DTbtControllerConfig.PcieRpNumber - 1, &RpDev, &RpFunc); ASSERT_EFI_ERROR(Status); // // VGA Enable and VGA 16-bit decode registers of Bridge control register of Root port where // Host router resides should be cleaned // BridngeBaseAddress = PCI_SEGMENT_LIB_ADDRESS(0, 0, (UINT32)RpDev, (UINT32)RpFunc, 0); Data16 = PciSegmentRead16(BridngeBaseAddress + PCI_BRIDGE_CONTROL_REGISTER_OFFSET); Data16 &= (~(EFI_PCI_BRIDGE_CONTROL_VGA | EFI_PCI_BRIDGE_CONTROL_VGA_16)); PciSegmentWrite16(BridngeBaseAddress + PCI_BRIDGE_CONTROL_REGISTER_OFFSET, Data16); DEBUG ((DEBUG_INFO, "DTbtClearVgaRegisters call Return\n")); return Status; } /** Exectue Mail box command "Boot On". @param[in] PEI_TBT_POLICY PeiTbtConfig @retval EFI_SUCCESS The function completes successfully @retval EFI_UNSUPPORTED dTBT is not supported. **/ EFI_STATUS EFIAPI DTbtBootOn( IN PEI_TBT_POLICY *PeiTbtConfig ) { EFI_STATUS Status; UINT32 OrgBusNumberConfiguration; UINTN RpDev; UINTN RpFunc; DEBUG((DEBUG_INFO, "DTbtBootOn call Inside\n")); Status = EFI_SUCCESS; Status = GetDTbtRpDevFun(PeiTbtConfig-> DTbtControllerConfig.Type, PeiTbtConfig-> DTbtControllerConfig.PcieRpNumber - 1, &RpDev, &RpFunc); ASSERT_EFI_ERROR(Status); OrgBusNumberConfiguration = PciSegmentRead32 (PCI_SEGMENT_LIB_ADDRESS (0, 0, RpDev, RpFunc, PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET)); // // Set Sec/Sub buses to 0xF0 // PciSegmentWrite32(PCI_SEGMENT_LIB_ADDRESS (0, 0, RpDev, RpFunc, PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET), 0x00F0F000); // //When Thunderbolt(TM) boot [TbtBootOn] is enabled in bios setup we need to do the below: //Bios should send "Boot On" message through PCIE2TBT register //The Boot On command as described above would include the command and acknowledge from FW (with the default timeout in BIOS), //once the Boot On command is completed it is guaranteed that the AlpineRidge(AR) device is there and the PCI tunneling was done by FW, //next step from BIOS is enumeration using SMI // if (PeiTbtConfig->DTbtCommonConfig.TbtBootOn > 0) { // // Exectue Mail box command "Boot On / Pre-Boot ACL" // //Command may be executed only during boot/reboot and not during Sx exit flow if (PeiTbtConfig->DTbtCommonConfig.TbtBootOn == 1) { if (!TbtSetPcie2TbtCommand(PCIE2TBT_BOOT_ON, 0xF0, 0, 0, TBT_5S_TIMEOUT)) { // // Nothing to wait, HR is not responsive // DEBUG((DEBUG_INFO, " DTbtBootOn - Boot On message sent failed \n")); } } if (PeiTbtConfig->DTbtCommonConfig.TbtBootOn == 2) { if (!TbtSetPcie2TbtCommand(PCIE2TBT_PREBOOTACL, 0xF0, 0, 0, TBT_3S_TIMEOUT)) { // // Nothing to wait, HR is not responsive // DEBUG((DEBUG_INFO, " DTbtBootOn - Pre-Boot ACL message sent failed \n")); } } } // // Reset Sec/Sub buses to original value // PciSegmentWrite32(PCI_SEGMENT_LIB_ADDRESS (0, 0, RpDev, RpFunc, PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET), OrgBusNumberConfiguration); DEBUG((DEBUG_INFO, "DTbtBootOn call Return\n")); return Status; } /** Exectue Mail box command "USB On". @param[in] PEI_TBT_POLICY PeiTbtConfig @retval EFI_SUCCESS The function completes successfully @retval EFI_UNSUPPORTED dTBT is not supported. **/ EFI_STATUS EFIAPI DTbtUsbOn( IN PEI_TBT_POLICY *PeiTbtConfig ) { EFI_STATUS Status; UINTN RpDev; UINTN RpFunc; UINT32 OrgBusNumberConfiguration; UINT64 TbtBaseAddress; UINT32 MaxWaitIter; UINT32 RegVal; EFI_BOOT_MODE BootMode; DEBUG((DEBUG_INFO, "DTbtUsbOn call Inside\n")); Status = EFI_SUCCESS; Status = GetDTbtRpDevFun(PeiTbtConfig-> DTbtControllerConfig.Type, PeiTbtConfig-> DTbtControllerConfig.PcieRpNumber - 1, &RpDev, &RpFunc); ASSERT_EFI_ERROR(Status); OrgBusNumberConfiguration = PciSegmentRead32(PCI_SEGMENT_LIB_ADDRESS (0, 0, RpDev, RpFunc, PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET)); // // Set Sec/Sub buses to 0xF0 // PciSegmentWrite32(PCI_SEGMENT_LIB_ADDRESS (0, 0, RpDev, RpFunc, PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET), 0x00F0F000); // //When Thunderbolt(TM) Usb boot [TbtUsbOn] is enabled in bios setup we need to do the below: //Bios should send "Usb On" message through PCIE2TBT register //The Usb On command as described above would include the command and acknowledge from FW (with the default timeout in BIOS), //once the Usb On command is completed it is guaranteed that the AlpineRidge(AR) device is there and the PCI tunneling was done by FW, //next step from BIOS is enumeration using SMI // if (PeiTbtConfig->DTbtCommonConfig.TbtUsbOn) { if (PeiTbtConfig->DTbtCommonConfig.TbtBootOn > 0) { MaxWaitIter = 50; // Wait 5 sec TbtBaseAddress = PCI_SEGMENT_LIB_ADDRESS(0, 0xF0, 0, 0, 0); // // Driver clears the PCIe2TBT Valid bit to support two consicutive mailbox commands // PciSegmentWrite32(TbtBaseAddress + PCIE2TBT_DTBT_R, 0); DEBUG((DEBUG_INFO, "TbtBaseAddress + PCIE2TBT_DTBT_R = 0x%lx \n", TbtBaseAddress + PCIE2TBT_DTBT_R)); while (MaxWaitIter-- > 0) { RegVal = PciSegmentRead32(TbtBaseAddress + TBT2PCIE_DTBT_R); if (0xFFFFFFFF == RegVal) { // // Device is not here return now // DEBUG((DEBUG_INFO, "TBT device is not present \n")); break; } if (!(RegVal & TBT2PCIE_DON_R)) { break; } MicroSecondDelay(100 * 1000); } } Status = PeiServicesGetBootMode(&BootMode); ASSERT_EFI_ERROR(Status); // // Exectue Mail box command "Usb On" // //Command may be executed only during boot/reboot and not during S3 exit flow //In case of S4 Exit send USB ON cmd only if Host Router was inactive/not present during S4 entry if ((BootMode == BOOT_ON_S4_RESUME) ) { // USB_ON cmd not required } else { if (!TbtSetPcie2TbtCommand(PCIE2TBT_USB_ON, 0xF0, 0, 0, TBT_5S_TIMEOUT)) { // // Nothing to wait, HR is not responsive // DEBUG((DEBUG_INFO, " TbtBootSupport - Usb On message sent failed \n")); } } } // // Reset Sec/Sub buses to original value // PciSegmentWrite32(PCI_SEGMENT_LIB_ADDRESS (0, 0, RpDev, RpFunc, PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET), OrgBusNumberConfiguration); DEBUG((DEBUG_INFO, "DTbtUsbOn call return\n")); return Status; } /** Exectue Mail box command "Sx Exit". @param[in] PEI_TBT_POLICY PeiTbtConfig @retval EFI_SUCCESS The function completes successfully @retval EFI_UNSUPPORTED dTBT is not supported. **/ EFI_STATUS EFIAPI DTbtSxExitFlow( IN PEI_TBT_POLICY *PeiTbtConfig ) { EFI_STATUS Status; UINT32 OrgBusNumberConfiguration; UINTN RpDev; UINTN RpFunc; UINT32 Count; DEBUG((DEBUG_INFO, "DTbtSxExitFlow call Inside\n")); Status = EFI_SUCCESS; Count = 0; Status = GetDTbtRpDevFun(PeiTbtConfig-> DTbtControllerConfig.Type, PeiTbtConfig-> DTbtControllerConfig.PcieRpNumber - 1, &RpDev, &RpFunc); ASSERT_EFI_ERROR(Status); OrgBusNumberConfiguration = PciSegmentRead32(PCI_SEGMENT_LIB_ADDRESS (0, 0, RpDev, RpFunc, PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET)); // // Set Sec/Sub buses to 0xF0 // PciSegmentWrite32(PCI_SEGMENT_LIB_ADDRESS (0, 0, RpDev, RpFunc, PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET), 0x00F0F000); if ( (PeiTbtConfig->DTbtCommonConfig.TbtBootOn == 2)) { // // WA: When system with TBT 3.1 device, resume SX system need to wait device ready. In document that maximum time out should be 500ms. // while (PciSegmentRead32(PCI_SEGMENT_LIB_ADDRESS(0, 0xf0, 0x0, 0x0, 0x08)) == 0xffffffff) { //End Device will be with Device Number 0x0, Function Number 0x0. MicroSecondDelay(STALL_ONE_MICRO_SECOND * 1000); // 1000usec Count++; if (Count > 10000) { //Allowing Max Delay of 10 sec for CFL-S board. break; } } // // Upon wake, if BIOS saved pre-Sx Host Router state as active (system went to sleep with // attached devices), BIOS should: // 1. Execute "Sx_Exit_TBT_Connected" mailbox command. // 2. If procedure above returns true, BIOS should perform "wait for fast link bring-up" loop // 3. Continue regular wake flow. // // // Exectue Mail box command and perform "wait for fast link bring-up" loop // TbtSetSxMode(0xF0, 0, 0, PeiTbtConfig->DTbtCommonConfig.TbtBootOn); } // // Reset Sec/Sub buses to original value // PciSegmentWrite32(PCI_SEGMENT_LIB_ADDRESS (0, 0, RpDev, RpFunc, PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET), OrgBusNumberConfiguration); DEBUG((DEBUG_INFO, "DTbtSxExitFlow call Return\n")); return Status; } /** Initialize Thunderbolt(TM) @retval EFI_SUCCESS The function completes successfully @retval others **/ EFI_STATUS EFIAPI TbtInit ( VOID ) { EFI_STATUS Status; PEI_TBT_POLICY *PeiTbtConfig; // // Get the TBT Policy // Status = PeiServicesLocatePpi ( &gPeiTbtPolicyPpiGuid, 0, NULL, (VOID **) &PeiTbtConfig ); if (EFI_ERROR(Status)) { DEBUG ((DEBUG_ERROR, " gPeiTbtPolicyPpiGuid Not installed!!!\n")); } ASSERT_EFI_ERROR (Status); // // Exectue Mail box command "Boot On" // Status = DTbtBootOn (PeiTbtConfig); // // Exectue Mail box command "Usb On" // Status = DTbtUsbOn (PeiTbtConfig); // //During boot, reboot and wake (bits [1:0]) of PCH PM_CFG register should be //set to 11b - 10 ms (default value is 0b - 10 us) // Status = DTbtSetTPch25Timing (PeiTbtConfig); // // Configure Tbt Force Power // Status = DTbtForcePower (PeiTbtConfig); // // VGA Enable and VGA 16-bit decode registers of Bridge control register of Root port where // Host router resides should be cleaned // Status = DTbtClearVgaRegisters (PeiTbtConfig); // // Upon wake, if BIOS saved pre-Sx Host Router state as active (system went to sleep with // attached devices), BIOS should: // 1. Execute "Sx_Exit_TBT_Connected" mailbox command. // 2. If procedure above returns true, BIOS should perform "wait for fast link bring-up" loop // 3. Continue regular wake flow. // Status = DTbtSxExitFlow (PeiTbtConfig); return EFI_SUCCESS; }