Browse Source

KabylakeOpenBoardPkg: Add I2cHdmiDebugSerialPortLib

Add new SerialPortLib implementation that routes log
messages over the I2C bus included in the integrated
graphics HDMI port. Normally this I2C bus is used to
read the EDID data from the monitor. An unintended
but useful property of this interface is that is does
not require DMA to perform I/O. This means that this
interface can be used to perform I/O before DRAM is
initialized.

HDMI video output is a common feature of many laptops.
This makes the HDMI DDC bus the only I/O interface
that is often exposed outside of the laptop chassis
while simultaneously capable of being used in Pre-Memory.
Oddly, this makes it ideal for closed chassis debug.

Cc: Chasel Chiu <chasel.chiu@intel.com>
Cc: Sai Chaganty <rangasai.v.chaganty@intel.com>
Cc: Isaac Oram <isaac.w.oram@intel.com>
Cc: Benjamin Doron <benjamin.doron00@gmail.com>
Cc: Michael Kubacki <michael.kubacki@microsoft.com>
Cc: Jeremy Soller <jeremy@system76.com>
Signed-off-by: Nate DeSimone <nathaniel.l.desimone@intel.com>
Reviewed-by: Isaac Oram <isaac.w.oram@intel.com>
Nate DeSimone 1 year ago
parent
commit
63f79db91b
20 changed files with 2568 additions and 2 deletions
  1. 9 0
      Platform/Intel/KabylakeOpenBoardPkg/AspireVn7Dash572G/OpenBoardPkgPcd.dsc
  2. 9 0
      Platform/Intel/KabylakeOpenBoardPkg/GalagoPro3/OpenBoardPkgPcd.dsc
  3. 9 0
      Platform/Intel/KabylakeOpenBoardPkg/KabylakeRvp3/OpenBoardPkgPcd.dsc
  4. 53 0
      Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/DxeI2cHdmiDebugSerialPortLib.inf
  5. 161 0
      Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/DxeSmmI2cHdmiDebugSerialPortLib.c
  6. 755 0
      Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/Gmbus.c
  7. 319 0
      Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/Gmbus.h
  8. 189 0
      Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/I2cDebugPortProtocol.c
  9. 77 0
      Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/I2cDebugPortProtocol.h
  10. 44 0
      Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/I2cDebugPortTplDxe.c
  11. 36 0
      Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/I2cDebugPortTplNull.c
  12. 198 0
      Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/I2cHdmiDebugSerialPortLib.c
  13. 79 0
      Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/IgfxI2c.c
  14. 99 0
      Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/IgfxI2c.h
  15. 224 0
      Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/PeiI2cHdmiDebugSerialPortLib.c
  16. 54 0
      Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/PeiI2cHdmiDebugSerialPortLib.inf
  17. 134 0
      Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/SecI2cHdmiDebugSerialPortLib.c
  18. 53 0
      Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/SecI2cHdmiDebugSerialPortLib.inf
  19. 53 0
      Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/SmmI2cHdmiDebugSerialPortLib.inf
  20. 13 2
      Platform/Intel/KabylakeOpenBoardPkg/OpenBoardPkg.dec

+ 9 - 0
Platform/Intel/KabylakeOpenBoardPkg/AspireVn7Dash572G/OpenBoardPkgPcd.dsc

@@ -399,6 +399,15 @@
   ######################################
   gBoardModulePkgTokenSpaceGuid.PcdPs2KbMsEnable|1
   gBoardModulePkgTokenSpaceGuid.PcdSuperIoPciIsaBridgeDevice|{0x00, 0x00, 0x1F, 0x00}
+  gKabylakeOpenBoardPkgTokenSpaceGuid.PcdGttMmAddress|0xDF000000
+
+  ## Specifies the DDC I2C channel to claim as the HDMI debug port
+  #  The value is defined as below.
+  #  2: DDC channel B
+  #  3: DDC channel C
+  #  4: DDC channel D
+  # @Prompt DDC I2C channel to claim as the HDMI debug port
+  gKabylakeOpenBoardPkgTokenSpaceGuid.PcdI2cHdmiDebugPortDdcI2cChannel|0x00  #@todo - Set to correct value for VN7-572G
 
 [PcdsFixedAtBuild.IA32]
   ######################################

+ 9 - 0
Platform/Intel/KabylakeOpenBoardPkg/GalagoPro3/OpenBoardPkgPcd.dsc

@@ -332,6 +332,15 @@
   ######################################
   gBoardModulePkgTokenSpaceGuid.PcdPs2KbMsEnable|1
   gBoardModulePkgTokenSpaceGuid.PcdSuperIoPciIsaBridgeDevice|{0x00, 0x00, 0x1F, 0x00}
+  gKabylakeOpenBoardPkgTokenSpaceGuid.PcdGttMmAddress|0xDF000000
+
+  ## Specifies the DDC I2C channel to claim as the HDMI debug port
+  #  The value is defined as below.
+  #  2: DDC channel B
+  #  3: DDC channel C
+  #  4: DDC channel D
+  # @Prompt DDC I2C channel to claim as the HDMI debug port
+  gKabylakeOpenBoardPkgTokenSpaceGuid.PcdI2cHdmiDebugPortDdcI2cChannel|0x03
 
 [PcdsFixedAtBuild.IA32]
   ######################################

+ 9 - 0
Platform/Intel/KabylakeOpenBoardPkg/KabylakeRvp3/OpenBoardPkgPcd.dsc

@@ -332,6 +332,15 @@
   ######################################
   gBoardModulePkgTokenSpaceGuid.PcdPs2KbMsEnable|1
   gBoardModulePkgTokenSpaceGuid.PcdSuperIoPciIsaBridgeDevice|{0x00, 0x00, 0x1F, 0x00}
+  gKabylakeOpenBoardPkgTokenSpaceGuid.PcdGttMmAddress|0xDF000000
+
+  ## Specifies the DDC I2C channel to claim as the HDMI debug port
+  #  The value is defined as below.
+  #  2: DDC channel B
+  #  3: DDC channel C
+  #  4: DDC channel D
+  # @Prompt DDC I2C channel to claim as the HDMI debug port
+  gKabylakeOpenBoardPkgTokenSpaceGuid.PcdI2cHdmiDebugPortDdcI2cChannel|0x00
 
 [PcdsFixedAtBuild.IA32]
   ######################################

+ 53 - 0
Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/DxeI2cHdmiDebugSerialPortLib.inf

@@ -0,0 +1,53 @@
+### @file
+# Component description file for Serial I/O Port library for the HDMI I2C Debug Port
+#
+# Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = DxeI2cHdmiDebugSerialPortLib
+  FILE_GUID                      = 53A31F61-7B95-4AD0-8F02-B03BCE6FC781
+  VERSION_STRING                 = 1.0
+  MODULE_TYPE                    = DXE_DRIVER
+  LIBRARY_CLASS                  = SerialPortLib|DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER SMM_CORE
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  PcdLib
+  TimerLib
+  PciLib
+  HdmiDebugPchDetectionLib
+
+[Packages]
+  MdePkg/MdePkg.dec
+  KabylakeOpenBoardPkg/OpenBoardPkg.dec
+  KabylakeSiliconPkg/SiPkg.dec
+
+[Sources]
+  DxeSmmI2cHdmiDebugSerialPortLib.c
+  Gmbus.c
+  Gmbus.h
+  I2cDebugPortProtocol.c
+  I2cDebugPortProtocol.h
+  I2cDebugPortTplDxe.c
+  I2cHdmiDebugSerialPortLib.c
+  IgfxI2c.c
+  IgfxI2c.h
+
+[Ppis]
+
+[Guids]
+
+[Pcd]
+  gKabylakeOpenBoardPkgTokenSpaceGuid.PcdI2cHdmiDebugPortDdcI2cChannel    ## CONSUMES
+  gKabylakeOpenBoardPkgTokenSpaceGuid.PcdGttMmAddress                     ## CONSUMES

+ 161 - 0
Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/DxeSmmI2cHdmiDebugSerialPortLib.c

@@ -0,0 +1,161 @@
+/** @file
+  Serial I/O Port library implementation for the HDMI I2C Debug Port
+  DXE/SMM Library implementation
+
+Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <Library/BaseLib.h>
+#include <Library/SerialPortLib.h>
+#include <Library/PcdLib.h>
+#include <Library/TimerLib.h>
+
+#include <IgfxI2c.h>
+#include <I2cDebugPortProtocol.h>
+
+//Once we reach DXE phase we can start assuming global variables are writeable.
+STATIC PCH_TYPE mPchType = PchTypeUnknown;
+STATIC UINT32   mControlBits = 0;
+STATIC UINT8    mI2cHdmiDebugDdcBusPinPair = 0;
+STATIC BOOLEAN  mIgdBusMasterReset = FALSE;
+
+/**
+  Sets the control bits on a serial device.
+
+  @param Control                Sets the bits of Control that are settable.
+
+  @retval RETURN_SUCCESS        The new control bits were set on the serial device.
+  @retval RETURN_UNSUPPORTED    The serial device does not support this operation.
+  @retval RETURN_DEVICE_ERROR   The serial device is not functioning correctly.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortSetControl (
+  IN UINT32 Control
+  )
+{
+  //
+  // check for invalid control parameters
+  //
+  if ((Control & (~(EFI_SERIAL_REQUEST_TO_SEND          |
+                    EFI_SERIAL_DATA_TERMINAL_READY      |
+                    EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE |
+                    EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE |
+                    EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE))) != 0 ) {
+    return EFI_UNSUPPORTED;
+  }
+  Control &= (UINT32) ~(EFI_SERIAL_INPUT_BUFFER_EMPTY);
+  mControlBits = Control;
+  return EFI_SUCCESS;
+}
+
+/**
+  Retrieve the status of the control bits on a serial device.
+
+  @param Control                A pointer to return the current control signals from the serial device.
+
+  @retval RETURN_SUCCESS        The control bits were read from the serial device.
+  @retval RETURN_UNSUPPORTED    The serial device does not support this operation.
+  @retval RETURN_DEVICE_ERROR   The serial device is not functioning correctly.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortGetControl (
+  OUT UINT32 *Control
+  )
+{
+  EFI_STATUS  Status;
+  UINT8       NumberOfBytesInFifoBuffer;
+
+  Status = I2cDebugPortReadyToRead (&NumberOfBytesInFifoBuffer);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  *Control = (EFI_SERIAL_CLEAR_TO_SEND | EFI_SERIAL_DATA_SET_READY |
+              EFI_SERIAL_CARRIER_DETECT | EFI_SERIAL_OUTPUT_BUFFER_EMPTY);
+  if (NumberOfBytesInFifoBuffer <= 0) {
+    *Control |= EFI_SERIAL_INPUT_BUFFER_EMPTY;
+  }
+  *Control |= mControlBits;
+  return Status;
+}
+
+/**
+  Returns the type of PCH on the system
+
+  @retval   The PCH type.
+**/
+PCH_TYPE
+GetPchType (
+  VOID
+  )
+{
+  if (mPchType == PchTypeUnknown) {
+    mPchType = GetPchTypeInternal ();
+  }
+  return mPchType;
+}
+
+/**
+  Returns the GPIO pin pair to use for the I2C HDMI debug port
+
+  @param[out] DdcBusPinPair               - The GPIO pin pair for the I2C HDMI debug port.
+
+  @retval  EFI_SUCCESS                    - The GPIO pin pair was successfully determined
+  @retval  EFI_INVALID_PARAMETER          - The given DDC I2C channel does not exist.
+  @retval  EFI_UNSUPPORTED                - The platform is using a PCH that is not supported yet.
+**/
+EFI_STATUS
+GetGmbusBusPinPairForI2cDebugPort (
+  OUT UINT8             *DdcBusPinPair
+  )
+{
+  EFI_STATUS  Status;
+
+  if (mI2cHdmiDebugDdcBusPinPair == 0) {
+    Status  = GetGmbusBusPinPair (
+                (IGFX_I2C_CHANNEL) PcdGet32 (PcdI2cHdmiDebugPortDdcI2cChannel),
+                &mI2cHdmiDebugDdcBusPinPair
+                );
+    if (EFI_ERROR (Status)) {
+      mI2cHdmiDebugDdcBusPinPair = 0;
+      return Status;
+    }
+  }
+  *DdcBusPinPair = mI2cHdmiDebugDdcBusPinPair;
+  return EFI_SUCCESS;
+}
+
+/**
+  Returns a flag indicating whether the IGD device bus master enable needs to
+  be disabled at the end of the current transaction
+
+  @retval  TRUE                           - IGD Bus Master Enable needs to be reset
+  @retval  FALSE                          - IGD Bus Master Enable does not need to be reset
+**/
+BOOLEAN
+GetIgdBusMasterReset (
+  VOID
+  )
+{
+  return mIgdBusMasterReset;
+}
+
+/**
+  Sets a flag indicating whether the IGD device bus master enable needs to
+  be disabled at the end of the current transaction
+
+  @param[in]  IgdBusMasterReset           - IGD device bus master enable flag
+**/
+VOID
+SetIgdBusMasterReset (
+  BOOLEAN IgdBusMasterReset
+  )
+{
+  mIgdBusMasterReset = IgdBusMasterReset;
+}

+ 755 - 0
Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/Gmbus.c

@@ -0,0 +1,755 @@
+/** @file
+  GMBUS I/O Implementation
+
+  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PcdLib.h>
+#include <Library/IoLib.h>
+#include <Library/PciLib.h>
+
+#include <IgfxI2c.h>
+#include <Gmbus.h>
+
+/**
+  Gets the GttMmAdr BAR value
+
+  @retval  The current value of the GTTMMADR BAR
+**/
+UINTN
+GmbusGetGttMmAdr (
+  VOID
+  )
+{
+  UINTN                   GttMmPciAddress;
+  UINT32                  GttMmAdr;
+
+  //
+  // Check if GTT Memory Mapped BAR has been already assigned, initialize if not
+  //
+  GttMmPciAddress = PCI_LIB_ADDRESS (SA_IGD_BUS, SA_IGD_DEV, SA_IGD_FUN_0, R_SA_IGD_GTTMMADR);
+  GttMmAdr = PciRead32 (GttMmPciAddress) & 0xFFFFFFF0;
+  if (GttMmAdr == 0) {
+    GttMmAdr = (UINT32) FixedPcdGet32 (PcdGttMmAddress);
+    if (GttMmAdr == 0) {
+      return 0;
+    }
+    //
+    // Program and read back GTT Memory Mapped BAR
+    //
+    PciWrite32 (GttMmPciAddress, (UINT32) (GttMmAdr & 0xFF000000));
+    GttMmAdr = PciRead32 (GttMmPciAddress) & 0xFFFFFFF0;
+  }
+  //
+  // Check if Bus Master and Memory access on 0:2:0 is enabled, enable it if not
+  //
+  if ((PciRead16 (
+        PCI_LIB_ADDRESS (SA_IGD_BUS, SA_IGD_DEV, SA_IGD_FUN_0, R_SA_IGD_CMD)
+        )
+      & (BIT2 | BIT1)) != (BIT2 | BIT1)) {
+    //
+    // Enable Bus Master and Memory access on 0:2:0
+    //
+    PciOr16 (PCI_LIB_ADDRESS (SA_IGD_BUS, SA_IGD_DEV, SA_IGD_FUN_0, R_SA_IGD_CMD), (BIT2 | BIT1));
+    //
+    // Set the Reset Bus Master flag so that it will be disabled when the current transaction is done
+    //
+    SetIgdBusMasterReset (TRUE);
+  }
+
+  return GttMmAdr;
+}
+
+/**
+  Reset Bus Master and Memory access on the IGD device to the initial state at
+  the start of the current transaction.
+**/
+VOID
+GmbusResetBusMaster (
+  VOID
+  )
+{
+  if (GetIgdBusMasterReset ()) {
+    //
+    // Check if Bus Master and Memory access on 0:2:0 is enabled, disable it if so
+    //
+    if ((PciRead16 (
+          PCI_LIB_ADDRESS (SA_IGD_BUS, SA_IGD_DEV, SA_IGD_FUN_0, R_SA_IGD_CMD)
+          )
+        & (BIT2 | BIT1)) != 0) {
+      //
+      // Disable Bus Master and Memory access on 0:2:0
+      //
+      PciAnd16 (PCI_LIB_ADDRESS (SA_IGD_BUS, SA_IGD_DEV, SA_IGD_FUN_0, R_SA_IGD_CMD), (UINT16) ~(BIT2 | BIT1));
+    }
+    //
+    // Clear the Reset Bus Master flag
+    //
+    SetIgdBusMasterReset (FALSE);
+  }
+}
+
+/**
+  Writes to the GMBUS0 register (Clock/Port Select)
+
+  @param[in]  GmbusClkPrtSel              - The value to write to GMBUS0
+
+  @retval  EFI_SUCCESS                    - GMBUS0 was successfully written.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while writting to GMBUS0
+**/
+EFI_STATUS
+SetGmbus0ClockPortSelect (
+  IN  UINT32             GmbusClkPrtSel
+  )
+{
+  UINTN                   GttMmAdr;
+
+  GttMmAdr = GmbusGetGttMmAdr ();
+  MmioWrite32 (GttMmAdr + R_SA_GTTMMADR_GMBUS0_CLKPRTSEL, GmbusClkPrtSel);
+  return EFI_SUCCESS;
+}
+
+/**
+  Writes to the GMBUS1 register (Command/Status)
+
+  @param[in]  GmbusCmdSts                 - The value to write to GMBUS1
+
+  @retval  EFI_SUCCESS                    - GMBUS1 was successfully written.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while writting to GMBUS1
+**/
+EFI_STATUS
+SetGmbus1Command (
+  IN  UINT32             GmbusCmdSts
+  )
+{
+  UINTN                   GttMmAdr;
+
+  GttMmAdr = GmbusGetGttMmAdr ();
+  MmioWrite32 (GttMmAdr + R_SA_GTTMMADR_GMBUS1_CMDSTS, GmbusCmdSts);
+  return EFI_SUCCESS;
+}
+
+/**
+  Reads from the GMBUS2 register (GMBUS Status)
+
+  @param[out]  GmbusStatus                - The value read from GMBUS2
+
+  @retval  EFI_SUCCESS                    - GMBUS2 was successfully read.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while reading from GMBUS2
+**/
+EFI_STATUS
+GetGmbus2Status (
+  OUT  UINT32             *GmbusStatus
+  )
+{
+  UINTN                   GttMmAdr;
+
+  GttMmAdr = GmbusGetGttMmAdr ();
+  if (GttMmAdr == 0) {
+    return EFI_UNSUPPORTED;
+  }
+  *GmbusStatus = MmioRead32 (GttMmAdr + R_SA_GTTMMADR_GMBUS2_STATUS);
+  return EFI_SUCCESS;
+}
+
+/**
+  Writes to the GMBUS2 register (GMBUS Status)
+
+  @param[in]  GmbusStatus                 - The value to write to GMBUS2
+
+  @retval  EFI_SUCCESS                    - GMBUS2 was successfully written.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while writting to GMBUS2
+**/
+EFI_STATUS
+SetGmbus2Status (
+  IN  UINT32             GmbusStatus
+  )
+{
+  UINTN                   GttMmAdr;
+
+  GttMmAdr = GmbusGetGttMmAdr ();
+  if (GttMmAdr == 0) {
+    return EFI_UNSUPPORTED;
+  }
+  MmioWrite32 (GttMmAdr + R_SA_GTTMMADR_GMBUS2_STATUS, GmbusStatus);
+  return EFI_SUCCESS;
+}
+
+/**
+  Reads from the GMBUS3 register (GMBUS Data Buffer)
+
+  @param[out]  GmbusData                  - The value read from GMBUS3
+
+  @retval  EFI_SUCCESS                    - GMBUS2 was successfully read.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while reading from GMBUS2
+**/
+EFI_STATUS
+GetGmbus3Data (
+  OUT  UINT32             *GmbusData
+  )
+{
+  UINTN                   GttMmAdr;
+
+  GttMmAdr = GmbusGetGttMmAdr ();
+  if (GttMmAdr == 0) {
+    return EFI_UNSUPPORTED;
+  }
+  *GmbusData = MmioRead32 (GttMmAdr + R_SA_GTTMMADR_GMBUS3_DATA);
+  return EFI_SUCCESS;
+}
+
+/**
+  Writes to the GMBUS3 register (GMBUS Data Buffer)
+
+  @param[in]  GmbusData                   - The value to write to GMBUS3
+
+  @retval  EFI_SUCCESS                    - GMBUS3 was successfully written.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while writting to GMBUS3
+**/
+EFI_STATUS
+SetGmbus3Data (
+  IN  UINT32             GmbusData
+  )
+{
+  UINTN                   GttMmAdr;
+
+  GttMmAdr = GmbusGetGttMmAdr ();
+  if (GttMmAdr == 0) {
+    return EFI_UNSUPPORTED;
+  }
+  MmioWrite32 (GttMmAdr + R_SA_GTTMMADR_GMBUS3_DATA, GmbusData);
+  return EFI_SUCCESS;
+}
+
+/**
+  Set and clear the software clear interrupt bit. This causes a local reset on the GMBUS controller.
+
+  @retval  EFI_SUCCESS                    - The GMBUS error was successfully cleared.
+  @retval  EFI_TIMEOUT                    - The GMBUS I2C controller did not respond within the required timeout period.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while accessing the GMBUS I2C controller.
+**/
+EFI_STATUS
+GmbusRecoverError (
+  VOID
+  )
+{
+  EFI_STATUS              Status;
+
+  //
+  // Setting B_SA_GTTMMADR_GMBUS1_SW_CLR_INT
+  // causes a local reset on the GMBUS controller
+  //
+  Status = SetGmbus1Command ((UINT32) B_SA_GTTMMADR_GMBUS1_SW_CLR_INT);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  Status = SetGmbus1Command (0);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  //
+  // Wait for reset to complete
+  //
+  Status = GmbusWaitForReady (B_SA_GTTMMADR_GMBUS2_BUS_ACTIVE, FALSE);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return Status;
+}
+
+/**
+  Wait for a given bitmask of status bits to de-assert to zero.
+
+  @param[in]  StatusBitMask               - A bitmask of status bits to be compared to the present value of GMBUS2
+  @param[in]  WaitForAssertion            - If TRUE, the Status Bit indicated must be 1, otherwise it must be 0.
+
+  @retval  EFI_SUCCESS                    - The GMBUS controller has cleared all of the bits in the bitmask.
+  @retval  EFI_TIMEOUT                    - The GMBUS controller did not clear all of the bits in the bitmask
+                                            within the required timeout period.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while accessing the GMBUS I2C controller.
+**/
+EFI_STATUS
+GmbusWaitForReady (
+  IN  UINT32              StatusBitMask,
+  IN  BOOLEAN             WaitForAssertion
+  )
+{
+  EFI_STATUS              Status;
+  EFI_STATUS              Status2;
+  UINTN                   Index;
+  UINT32                  GmbusStatus;
+
+  Status = EFI_TIMEOUT;
+  for (Index = 0; Index < GMBUS_TIMEOUT; Index++) {
+    Status2 = GetGmbus2Status (&GmbusStatus);
+    if (EFI_ERROR (Status2)) {
+      return Status2;
+    }
+    if (WaitForAssertion) {
+      if (GmbusStatus & StatusBitMask) {
+        Status = EFI_SUCCESS;
+        break;
+      }
+    } else {
+      if (!(GmbusStatus & StatusBitMask)) {
+        Status = EFI_SUCCESS;
+        break;
+      }
+    }
+  }
+  return Status;
+}
+
+/**
+  Initialize the GMBUS to use a given GPIO pin pair and clock speed in preparation
+  for sending a I2C command to the GMBUS controller.
+
+  @param[in]  BusSpeed                    - The clock rate for the I2C bus.
+  @param[in]  DdcBusPinPair               - The GPIO pin pair the GMBUS controller should use.
+
+  @retval  EFI_SUCCESS                    - The GMBUS has been initialized successfully.
+  @retval  EFI_INVALID_PARAMETER          - The given BusSpeed does not match a valid clock rate value.
+  @retval  EFI_TIMEOUT                    - The GMBUS I2C controller did not respond within the required timeout period.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while accessing the GMBUS I2C controller.
+**/
+EFI_STATUS
+GmbusPrepare (
+  IN  UINT8             BusSpeed,
+  IN  UINT8             DdcBusPinPair
+  )
+{
+  EFI_STATUS              Status;
+  UINT32                  GmbusClkPrtSel;
+  UINT32                  GmbusStatus;
+
+  //
+  // Check that the user provided a valid bus speed
+  //
+  if ((BusSpeed != GMBUS_CLOCK_RATE_100K) && (BusSpeed != GMBUS_CLOCK_RATE_50K) &&
+      (BusSpeed != GMBUS_CLOCK_RATE_400K) && (BusSpeed != GMBUS_CLOCK_RATE_1M)) {
+    return EFI_INVALID_PARAMETER;
+  }
+  //
+  // Wait for GMBUS to complete any pending commands
+  //
+  Status = GmbusWaitForReady (B_SA_GTTMMADR_GMBUS2_INUSE, FALSE);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  //
+  // Program the GMBUS Port and Clock
+  //
+  GmbusClkPrtSel = (BusSpeed << 8) | DdcBusPinPair;
+  Status = SetGmbus0ClockPortSelect (GmbusClkPrtSel);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Check for a NACK that has not been cleared yet. Clear it if found.
+  //
+  Status = GetGmbus2Status (&GmbusStatus);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  if (GmbusStatus & B_SA_GTTMMADR_GMBUS2_NACK_INDICATOR) {
+    Status = GmbusRecoverError ();
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  }
+
+  return Status;
+}
+
+/**
+  Release the GMBUS controller
+
+  @retval  EFI_SUCCESS                    - The GMBUS has been released successfully.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while accessing the GMBUS I2C controller.
+**/
+EFI_STATUS
+GmbusRelease (
+  VOID
+  )
+{
+  EFI_STATUS              Status;
+
+  //
+  // Clear the GMBUS Port and Clock
+  //
+  Status = SetGmbus0ClockPortSelect (0);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Setting the INUSE bit to 1 indicates that software has released the GMBUS resource.
+  // The GMBUS controller will then reset the INUSE bit to 0.
+  //
+  Status = SetGmbus2Status (B_SA_GTTMMADR_GMBUS2_INUSE);
+  if (EFI_ERROR (Status)) {
+  }
+
+  return Status;
+}
+
+/**
+  Reads data from the I2C bus using the GMBUS I2C controller
+
+  @param[in]  DdcBusPinPair               - The GPIO pin pair to use for the read operation
+  @param[in]  SlaveAddress                - The I2C device address to read data from
+  @param[in]  SendStopCondition           - TRUE:  After the read is complete, send a STOP condition to the I2C bus
+                                            FALSE: Don't send a STOP after the read is complete, this allows one to
+                                                   immediately send a repeated START condition to the I2C bus after
+                                                   GmbusRead() exits by calling either GmbusRead() or GmbusWrite()
+                                                   immediately after this function returns.
+  @param[in]  SendIndexBeforeRead         - TRUE:  Before executing the read on the I2C bus, first send a WRITE to the
+                                                   I2C bus using the same SlaveAddress (but with BIT0 set to 0 because
+                                                   the operation is a write) the write will contain a single byte, that
+                                                   byte is the data given in the IndexData parameter.
+                                            FALSE: Just send a read to the I2C bus, the IndexData parameter is ignored.
+  @param[in]  IndexData                   - If SendIndexBeforeRead is TRUE, this byte of data will be written to the I2C
+                                            bus before the I2C bus is read. If SendIndexBeforeRead is FALSE, this
+                                            parameter is ignored.
+  @param[in, out]  ByteCount              - The number of bytes to read from the I2C bus. On output, the number of bytes
+                                            actually read.
+  @param[out] ReadBuffer                  - The memory buffer to return the read data.
+
+  @retval  EFI_SUCCESS                    - The data was successfully read.
+  @retval  EFI_INVALID_PARAMETER          - One of the following conditions:
+                                            * ByteCount is 0 or >GMBUS_MAX_BYTES.
+                                            * ReadBuffer is NULL
+                                            * SlaveAddress does not have BIT0 set (required for reads.)
+  @retval  EFI_TIMEOUT                    - The GMBUS I2C controller did not respond within the required timeout period.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while accessing the GMBUS I2C controller.
+**/
+EFI_STATUS
+GmbusRead (
+  IN      UINT8         DdcBusPinPair,
+  IN      UINT8         SlaveAddress,
+  IN      BOOLEAN       SendStopCondition,
+  IN      BOOLEAN       SendIndexBeforeRead,
+  IN      UINT8         IndexData,
+  IN OUT  UINT32        *ByteCount,
+  OUT     UINT8         *ReadBuffer
+  )
+{
+  EFI_STATUS              Status;
+  EFI_STATUS              Status2;
+  UINT32                  Index;
+  UINT32                  GmbusCmdSts;
+  UINT32                  GmbusStatus;
+  UINT32                  GmbusData;
+  UINT32                  BytesRead;
+
+  Status      = EFI_SUCCESS;
+  BytesRead   = 0;
+  GmbusStatus = 0;
+
+  //
+  // Input Validation
+  //
+  if ((*ByteCount) <= 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+  if ((*ByteCount) > GMBUS_MAX_BYTES) {
+    return EFI_INVALID_PARAMETER;
+  }
+  if (ReadBuffer == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+  if ((SlaveAddress & BIT0) != BIT0) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Configure Gmbus port and clock speed
+  //
+  Status = GmbusPrepare (GMBUS_CLOCK_RATE_50K, (DdcBusPinPair & B_SA_GTTMMADR_GMBUS0_PIN_PAIR_MASK));
+  if (EFI_ERROR (Status)) {
+    goto Done;
+  }
+
+  //
+  // Construct the command for the GMBUS controller
+  //
+  GmbusCmdSts     = ((UINT32) SlaveAddress)                   |
+                    B_SA_GTTMMADR_GMBUS1_BUS_CYCLE_SEL_START  |
+                    B_SA_GTTMMADR_GMBUS1_SW_RDY               |
+                    B_SA_GTTMMADR_GMBUS1_EN_TIMEOUT;
+  GmbusCmdSts    |= (((*ByteCount) << N_SA_GTTMMADR_GMBUS1_TOTAL_BYTE_COUNT) & B_SA_GTTMMADR_GMBUS1_TOTAL_BYTE_COUNT_MASK);
+  if (SendStopCondition) {
+    GmbusCmdSts  |= B_SA_GTTMMADR_GMBUS1_BUS_CYCLE_SEL_STOP;
+  }
+  if (SendIndexBeforeRead) {
+    GmbusCmdSts  |= B_SA_GTTMMADR_GMBUS1_BUS_CYCLE_SEL_INDEX;
+    GmbusCmdSts  |= ((IndexData << N_SA_GTTMMADR_GMBUS1_INDEX) & B_SA_GTTMMADR_GMBUS1_INDEX_MASK);
+  }
+
+  //
+  // Send the command to the GMBUS controller, this will cause the I2C transaction to begin immediately
+  //
+  Status = SetGmbus1Command (GmbusCmdSts);
+  if (EFI_ERROR (Status)) {
+    goto Done;
+  }
+
+  //
+  // Read the data from the GMBUS controller as it arrives
+  //
+  while (BytesRead < (*ByteCount)) {
+    //
+    // Wait for the GMBUS controller to set the HW_RDY bit to 1
+    //
+    // The HW_RDY bit is set under the following conditions:
+    //
+    // * After a reset
+    // * When a transaction is aborted by the setting the SW_CLR_INT bit in the GMBUS1 register
+    // * When an active GMBUS cycle has terminated with a STOP condition
+    // * During a GMBUS write transaction, when the data register can accept another four bytes of data
+    // * During a GMBUS read transaction, when the data register has four bytes of new data or when the read
+    //   transaction DATA phase is complete and the data register contains all remaining data.
+    //
+    Status = GmbusWaitForReady (B_SA_GTTMMADR_GMBUS2_HW_RDY, TRUE);
+    //
+    // Check the GMBUS2 register for error conditions (NACK or Slave Stall Timeout)
+    //
+    Status2 = GetGmbus2Status (&GmbusStatus);
+    if (EFI_ERROR (Status2)) {
+      Status = Status2;
+      goto Done;
+    }
+    if (EFI_ERROR (Status) && ((GmbusStatus & B_SA_GTTMMADR_GMBUS2_NACK_INDICATOR) == 0)) {
+      Status = EFI_DEVICE_ERROR;
+      goto Done;
+    }
+    if (((GmbusStatus & B_SA_GTTMMADR_GMBUS2_NACK_INDICATOR) != 0) ||
+        ((GmbusStatus & B_SA_GTTMMADR_GMBUS2_SLAVE_STALL_TIMEOUT_ERROR) != 0)) {
+      //
+      // If a NACK or Slave Stall Timeout occurs, then a bus error has occurred.
+      // In the event of a bus error, one must reset the GMBUS controller to resume normal operation.
+      //
+      Status = GmbusRecoverError ();
+      if (EFI_ERROR (Status)) {
+        goto Done;
+      }
+      Status = EFI_DEVICE_ERROR;
+      goto Done;
+    }
+    //
+    // No error conditions were encountered, read the data and write it to the data buffer
+    //
+    Status = GetGmbus3Data (&GmbusData);
+    if (EFI_ERROR (Status)) {
+      goto Done;
+    }
+    for (Index = 0; (Index < sizeof (UINT32)) && (BytesRead < (*ByteCount)); Index++) {
+      ReadBuffer[BytesRead] = (GmbusData >> (Index * 8)) & 0xFF;
+      BytesRead++;
+    }
+  }
+
+  //
+  // Wait for the GMBUS controller to enter the IDLE state
+  //
+  Status = GmbusWaitForReady (B_SA_GTTMMADR_GMBUS2_BUS_ACTIVE, FALSE);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+Done:
+  Status2 = GmbusRelease ();
+  if (EFI_ERROR (Status2)) {
+    Status = Status2;
+  }
+  GmbusResetBusMaster ();
+
+  (*ByteCount) = BytesRead;
+  return Status;
+}
+
+/**
+  Writes data to the I2C bus using the GMBUS I2C controller
+
+  @param[in]  DdcBusPinPair               - The GPIO pin pair to use for the write operation
+  @param[in]  SlaveAddress                - The I2C device address to write data to
+  @param[in]  SendStopCondition           - TRUE:  After the write is complete, send a STOP condition to the I2C bus
+                                            FALSE: Don't send a STOP after the write is complete, this allows one to
+                                                   immediately send a repeated START condition to the I2C bus after
+                                                   GmbusRead() exits by calling either GmbusRead() or GmbusWrite()
+                                                   immediately after this function returns.
+  @param[in]  ByteCount                   - The number of bytes to write to the I2C bus.
+  @param[in]  WriteBuffer                 - The data to be written to the I2C bus.
+
+  @retval  EFI_SUCCESS                    - The data was successfully written.
+  @retval  EFI_INVALID_PARAMETER          - One of the following conditions:
+                                            * ByteCount is 0 or >GMBUS_MAX_BYTES.
+                                            * WriteBuffer is NULL
+                                            * SlaveAddress does not have BIT0 cleared (required for writes.)
+  @retval  EFI_TIMEOUT                    - The GMBUS I2C controller did not respond within the required timeout period.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while accessing the GMBUS I2C controller.
+**/
+EFI_STATUS
+GmbusWrite (
+  IN  UINT8             DdcBusPinPair,
+  IN  UINT8             SlaveAddress,
+  IN  BOOLEAN           SendStopCondition,
+  IN  UINT32            ByteCount,
+  IN  UINT8             *WriteBuffer
+  )
+{
+  EFI_STATUS              Status;
+  EFI_STATUS              Status2;
+  UINT32                  Index;
+  UINT32                  GmbusCmdSts;
+  UINT32                  GmbusStatus;
+  UINT32                  GmbusData;
+  UINT32                  BytesWritten;
+  BOOLEAN                 FirstLoop;
+
+  Status        = EFI_SUCCESS;
+  BytesWritten  = 0;
+  GmbusStatus   = 0;
+  FirstLoop     = TRUE;
+
+  //
+  // Input Validation
+  //
+  if (ByteCount <= 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+  if (ByteCount > GMBUS_MAX_BYTES) {
+    return EFI_INVALID_PARAMETER;
+  }
+  if (WriteBuffer == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+  if ((SlaveAddress & BIT0) != 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Configure Gmbus port and clock speed
+  Status = GmbusPrepare (GMBUS_CLOCK_RATE_50K, (DdcBusPinPair & B_SA_GTTMMADR_GMBUS0_PIN_PAIR_MASK));
+  if (EFI_ERROR (Status)) {
+    goto Done;
+  }
+
+  //
+  // Construct the command for the GMBUS controller
+  //
+  GmbusCmdSts     = ((UINT32) SlaveAddress)                   |
+                    B_SA_GTTMMADR_GMBUS1_BUS_CYCLE_SEL_START  |
+                    B_SA_GTTMMADR_GMBUS1_SW_RDY               |
+                    B_SA_GTTMMADR_GMBUS1_EN_TIMEOUT;
+  GmbusCmdSts    |= ((ByteCount << N_SA_GTTMMADR_GMBUS1_TOTAL_BYTE_COUNT) & B_SA_GTTMMADR_GMBUS1_TOTAL_BYTE_COUNT_MASK);
+  if (SendStopCondition) {
+    GmbusCmdSts  |= B_SA_GTTMMADR_GMBUS1_BUS_CYCLE_SEL_STOP;
+  }
+
+  //
+  // Preload the first 4 bytes of data so that when we send the command to the GMBUS
+  // controller the first 4 bytes of data are ready for transmission. The GMBUS controller requires this.
+  //
+  GmbusData = 0;
+  for (Index = 0; (Index < sizeof (UINT32)) && (BytesWritten < ByteCount); Index++) {
+    GmbusData |= (WriteBuffer[BytesWritten] << (Index * 8));
+    BytesWritten++;
+  }
+  Status = SetGmbus3Data (GmbusData);
+  if (EFI_ERROR (Status)) {
+    goto Done;
+  }
+
+  //
+  // Send the command to the GMBUS controller, this will cause the I2C transaction to begin immediately
+  //
+  Status = SetGmbus1Command (GmbusCmdSts);
+  if (EFI_ERROR (Status)) {
+    goto Done;
+  }
+
+  while ((BytesWritten < ByteCount) || FirstLoop) {
+    //
+    // If this is not the first loop, load the next 4 bytes of data into the
+    // GMBUS controller's data buffer.
+    //
+    if(!FirstLoop) {
+      GmbusData = 0;
+      for (Index = 0; (Index < sizeof (UINT32)) && (BytesWritten < ByteCount); Index++) {
+        GmbusData |= (WriteBuffer[BytesWritten] << (Index * 8));
+        BytesWritten++;
+      }
+      Status = SetGmbus3Data (GmbusData);
+      if (EFI_ERROR (Status)) {
+        goto Done;
+      }
+    }
+    FirstLoop = FALSE;
+
+    //
+    // Wait for the GMBUS controller to set the HW_RDY bit to 1
+    //
+    // The HW_RDY bit is set under the following conditions:
+    //
+    // * After a reset
+    // * When a transaction is aborted by the setting the SW_CLR_INT bit in the GMBUS1 register
+    // * When an active GMBUS cycle has terminated with a STOP condition
+    // * During a GMBUS write transaction, when the data register can accept another four bytes of data
+    // * During a GMBUS read transaction, when the data register has four bytes of new data or when the read
+    //   transaction DATA phase is complete and the data register contains all remaining data.
+    //
+    Status = GmbusWaitForReady (B_SA_GTTMMADR_GMBUS2_HW_RDY, TRUE);
+    if (EFI_ERROR (Status)) {
+    }
+    //
+    // Check the GMBUS2 register for error conditions (NACK or Slave Stall Timeout)
+    //
+    Status2 = GetGmbus2Status (&GmbusStatus);
+    if (EFI_ERROR (Status2)) {
+      Status = Status2;
+      goto Done;
+    }
+    if (EFI_ERROR (Status) && ((GmbusStatus & B_SA_GTTMMADR_GMBUS2_NACK_INDICATOR) == 0)) {
+      Status = EFI_DEVICE_ERROR;
+      goto Done;
+    }
+    if (((GmbusStatus & B_SA_GTTMMADR_GMBUS2_NACK_INDICATOR) != 0) ||
+        ((GmbusStatus & B_SA_GTTMMADR_GMBUS2_SLAVE_STALL_TIMEOUT_ERROR) != 0)) {
+      //
+      // If a NACK or Slave Stall Timeout occurs, then a bus error has occurred.
+      // In the event of a bus error, one must reset the GMBUS controller to resume normal operation.
+      //
+      Status = GmbusRecoverError ();
+      if (EFI_ERROR (Status)) {
+        goto Done;
+      }
+      Status = EFI_DEVICE_ERROR;
+      goto Done;
+    }
+  }
+
+  //
+  // Wait for the GMBUS controller to enter the IDLE state
+  //
+  Status = GmbusWaitForReady (B_SA_GTTMMADR_GMBUS2_BUS_ACTIVE, FALSE);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+Done:
+  Status2 = GmbusRelease ();
+  if (EFI_ERROR (Status2)) {
+    Status = Status2;
+  }
+  GmbusResetBusMaster ();
+
+  return Status;
+}

+ 319 - 0
Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/Gmbus.h

@@ -0,0 +1,319 @@
+/** @file
+  GMBUS I/O Registers and Functions
+
+  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  <b>Conventions</b>:
+  - Prefixes:
+    - Definitions beginning with "R_" are registers
+    - Definitions beginning with "B_" are bits within registers
+    - Definitions beginning with "V_" are meaningful values of bits within the registers
+    - Definitions beginning with "S_" are register sizes
+    - Definitions beginning with "N_" are the bit position
+  - In general, SA registers are denoted by "_SA_" in register names
+  - Registers / bits that are different between SA generations are denoted by
+    "_SA_[generation_name]_" in register/bit names. e.g., "_SA_HSW_"
+  - Registers / bits that are different between SKUs are denoted by "_[SKU_name]"
+    at the end of the register/bit names
+  - Registers / bits of new devices introduced in a SA generation will be just named
+    as "_SA_" without [generation_name] inserted.
+**/
+
+#include <Uefi.h>
+#include <SaRegs.h>
+
+#define SA_SEG_NUM                                        0x0000000
+#define R_SA_GTTMMADR_GMBUS0_CLKPRTSEL                    0x00C5100
+#define B_SA_GTTMMADR_GMBUS0_PIN_PAIR_MASK                0x0000007
+#define R_SA_GTTMMADR_GMBUS1_CMDSTS                       0x00C5104
+#define B_SA_GTTMMADR_GMBUS1_SW_CLR_INT                   BIT31
+#define B_SA_GTTMMADR_GMBUS1_SW_RDY                       BIT30
+#define B_SA_GTTMMADR_GMBUS1_EN_TIMEOUT                   BIT29
+#define B_SA_GTTMMADR_GMBUS1_BUS_CYCLE_SEL_MASK           (BIT27 | BIT26 | BIT25)
+/*
+BUS_CYCLE_SEL Decoder Table
+===========================
+Value | Name                    | Description
+------+-------------------------+------------------------------------------------------------------------------------
+000b  | No cycle                | No GMBUS cycle is generated
+001b  | No Index, No Stop, Wait | GMBUS cycle is generated without an INDEX, with no STOP, and ends with a WAIT
+010b  | Reserved                | Reserved
+011b  | Index, No Stop, Wait    | GMBUS cycle is generated with an INDEX, with no STOP, and ends with a WAIT
+100b  | Gen Stop                | Generates a STOP if currently in a WAIT or after the completion of the current byte
+101b  | No Index, Stop          | GMBUS cycle is generated without an INDEX and with a STOP
+110b  | Reserved                | Reserved
+111b  | Index, Stop             | GMBUS cycle is generated with an INDEX and with a STOP All
+*/
+#define B_SA_GTTMMADR_GMBUS1_BUS_CYCLE_SEL_STOP           BIT27
+#define B_SA_GTTMMADR_GMBUS1_BUS_CYCLE_SEL_INDEX          BIT26
+#define B_SA_GTTMMADR_GMBUS1_BUS_CYCLE_SEL_START          BIT25
+#define B_SA_GTTMMADR_GMBUS1_TOTAL_BYTE_COUNT_MASK        0x1FF0000
+#define N_SA_GTTMMADR_GMBUS1_TOTAL_BYTE_COUNT             16
+#define B_SA_GTTMMADR_GMBUS1_INDEX_MASK                   0x000FF00
+#define N_SA_GTTMMADR_GMBUS1_INDEX                        8
+#define R_SA_GTTMMADR_GMBUS2_STATUS                       0x00C5108
+#define B_SA_GTTMMADR_GMBUS2_INUSE                        BIT15
+#define B_SA_GTTMMADR_GMBUS2_SLAVE_STALL_TIMEOUT_ERROR    BIT13
+#define B_SA_GTTMMADR_GMBUS2_HW_RDY                       BIT11
+#define B_SA_GTTMMADR_GMBUS2_NACK_INDICATOR               BIT10
+#define B_SA_GTTMMADR_GMBUS2_BUS_ACTIVE                   BIT9
+#define R_SA_GTTMMADR_GMBUS3_DATA                         0x00C510C
+
+#define GMBUS_MAX_BYTES                                   (0x1FF - 0x8)   //9-bits minus 8 bytes
+
+#define GMBUS_CLOCK_RATE_100K                             0x0000000
+#define GMBUS_CLOCK_RATE_50K                              0x0000001
+#define GMBUS_CLOCK_RATE_400K                             0x0000002
+#define GMBUS_CLOCK_RATE_1M                               0x0000003
+
+#define GMBUS_TIMEOUT                                     0x00249F0
+
+/**
+  Gets the GttMmAdr BAR value
+
+  @retval  The current value of the GTTMMADR BAR
+**/
+UINTN
+GmbusGetGttMmAdr (
+  VOID
+  );
+
+/**
+  Reset Bus Master and Memory access on the IGD device to the initial state at
+  the start of the current transaction.
+**/
+VOID
+GmbusResetBusMaster (
+  VOID
+  );
+
+/**
+  Returns a flag indicating whether the IGD device bus master enable needs to
+  be disabled at the end of the current transaction
+
+  @retval  TRUE                           - IGD Bus Master Enable needs to be reset
+  @retval  FALSE                          - IGD Bus Master Enable does not need to be reset
+**/
+BOOLEAN
+GetIgdBusMasterReset (
+  VOID
+  );
+
+/**
+  Sets a flag indicating whether the IGD device bus master enable needs to
+  be disabled at the end of the current transaction
+
+  @param[in]  IgdBusMasterReset           - IGD device bus master enable flag
+**/
+VOID
+SetIgdBusMasterReset (
+  BOOLEAN IgdBusMasterReset
+  );
+
+/**
+  Writes to the GMBUS0 register (Clock/Port Select)
+
+  @param[in]  GmbusClkPrtSel              - The value to write to GMBUS0
+
+  @retval  EFI_SUCCESS                    - GMBUS0 was successfully written.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while writting to GMBUS0
+**/
+EFI_STATUS
+SetGmbus0ClockPortSelect (
+  IN  UINT32             GmbusClkPrtSel
+  );
+
+/**
+  Writes to the GMBUS1 register (Command/Status)
+
+  @param[in]  GmbusCmdSts                 - The value to write to GMBUS1
+
+  @retval  EFI_SUCCESS                    - GMBUS1 was successfully written.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while writting to GMBUS1
+**/
+EFI_STATUS
+SetGmbus1Command (
+  IN  UINT32             GmbusCmdSts
+  );
+
+/**
+  Reads from the GMBUS2 register (GMBUS Status)
+
+  @param[out]  GmbusStatus                - The value read from GMBUS2
+
+  @retval  EFI_SUCCESS                    - GMBUS2 was successfully read.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while reading from GMBUS2
+**/
+EFI_STATUS
+GetGmbus2Status (
+  OUT  UINT32             *GmbusStatus
+  );
+
+/**
+  Writes to the GMBUS2 register (GMBUS Status)
+
+  @param[in]  GmbusStatus                 - The value to write to GMBUS2
+
+  @retval  EFI_SUCCESS                    - GMBUS2 was successfully written.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while writting to GMBUS2
+**/
+EFI_STATUS
+SetGmbus2Status (
+  IN  UINT32             GmbusStatus
+  );
+
+/**
+  Reads from the GMBUS3 register (GMBUS Data Buffer)
+
+  @param[out]  GmbusData                  - The value read from GMBUS3
+
+  @retval  EFI_SUCCESS                    - GMBUS2 was successfully read.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while reading from GMBUS2
+**/
+EFI_STATUS
+GetGmbus3Data (
+  OUT  UINT32             *GmbusData
+  );
+
+/**
+  Writes to the GMBUS3 register (GMBUS Data Buffer)
+
+  @param[in]  GmbusData                   - The value to write to GMBUS3
+
+  @retval  EFI_SUCCESS                    - GMBUS3 was successfully written.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while writting to GMBUS3
+**/
+EFI_STATUS
+SetGmbus3Data (
+  IN  UINT32             GmbusData
+  );
+
+/**
+  Set and clear the software clear interrupt bit. This causes a local reset on the GMBUS controller.
+
+  @retval  EFI_SUCCESS                    - The GMBUS error was successfully cleared.
+  @retval  EFI_TIMEOUT                    - The GMBUS I2C controller did not respond within the required timeout period.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while accessing the GMBUS I2C controller.
+**/
+EFI_STATUS
+GmbusRecoverError (
+  VOID
+  );
+
+/**
+  Wait for a given bitmask of status bits to de-assert to zero.
+
+  @param[in]  StatusBitMask               - A bitmask of status bits to be compared to the present value of GMBUS2
+  @param[in]  WaitForAssertion            - If TRUE, the Status Bit indicated must be 1, otherwise it must be 0.
+
+  @retval  EFI_SUCCESS                    - The GMBUS controller has cleared all of the bits in the bitmask.
+  @retval  EFI_TIMEOUT                    - The GMBUS controller did not clear all of the bits in the bitmask
+                                            within the required timeout period.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while accessing the GMBUS I2C controller.
+**/
+EFI_STATUS
+GmbusWaitForReady (
+  IN  UINT32              StatusBitMask,
+  IN  BOOLEAN             WaitForAssertion
+  );
+
+/**
+  Initialize the GMBUS to use a given GPIO pin pair and clock speed in preparation
+  for sending a I2C command to the GMBUS controller.
+
+  @param[in]  BusSpeed                    - The clock rate for the I2C bus.
+  @param[in]  DdcBusPinPair               - The GPIO pin pair the GMBUS controller should use.
+
+  @retval  EFI_SUCCESS                    - The GMBUS has been initialized successfully.
+  @retval  EFI_INVALID_PARAMETER          - The given BusSpeed does not match a valid clock rate value.
+  @retval  EFI_TIMEOUT                    - The GMBUS I2C controller did not respond within the required timeout period.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while accessing the GMBUS I2C controller.
+**/
+EFI_STATUS
+GmbusPrepare (
+  IN  UINT8             BusSpeed,
+  IN  UINT8             DdcBusPinPair
+  );
+
+/**
+  Release the GMBUS controller
+
+  @retval  EFI_SUCCESS                    - The GMBUS has been released successfully.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while accessing the GMBUS I2C controller.
+**/
+EFI_STATUS
+GmbusRelease (
+  VOID
+  );
+
+/**
+  Reads data from the I2C bus using the GMBUS I2C controller
+
+  @param[in]  DdcBusPinPair               - The GPIO pin pair to use for the read operation
+  @param[in]  SlaveAddress                - The I2C device address to read data from
+  @param[in]  SendStopCondition           - TRUE:  After the read is complete, send a STOP condition to the I2C bus
+                                            FALSE: Don't send a STOP after the read is complete, this allows one to
+                                                   immediately send a repeated START condition to the I2C bus after
+                                                   GmbusRead() exits by calling either GmbusRead() or GmbusWrite()
+                                                   immediately after this function returns.
+  @param[in]  SendIndexBeforeRead         - TRUE:  Before executing the read on the I2C bus, first send a WRITE to the
+                                                   I2C bus using the same SlaveAddress (but with BIT0 set to 0 because
+                                                   the operation is a write) the write will contain a single byte, that
+                                                   byte is the data given in the IndexData parameter.
+                                            FALSE: Just send a read to the I2C bus, the IndexData parameter is ignored.
+  @param[in]  IndexData                   - If SendIndexBeforeRead is TRUE, this byte of data will be written to the I2C
+                                            bus before the I2C bus is read. If SendIndexBeforeRead is FALSE, this
+                                            parameter is ignored.
+  @param[in, out]  ByteCount              - The number of bytes to read from the I2C bus. On output, the number of bytes
+                                            actually read.
+  @param[out] ReadBuffer                  - The memory buffer to return the read data.
+
+  @retval  EFI_SUCCESS                    - The data was successfully read.
+  @retval  EFI_INVALID_PARAMETER          - One of the following conditions:
+                                            * ByteCount is 0 or >GMBUS_MAX_BYTES.
+                                            * ReadBuffer is NULL
+                                            * SlaveAddress does not have BIT0 set (required for reads.)
+  @retval  EFI_TIMEOUT                    - The GMBUS I2C controller did not respond within the required timeout period.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while accessing the GMBUS I2C controller.
+**/
+EFI_STATUS
+GmbusRead (
+  IN      UINT8         DdcBusPinPair,
+  IN      UINT8         SlaveAddress,
+  IN      BOOLEAN       SendStopCondition,
+  IN      BOOLEAN       SendIndexBeforeRead,
+  IN      UINT8         IndexData,
+  IN OUT  UINT32        *ByteCount,
+  OUT     UINT8         *ReadBuffer
+  );
+
+/**
+  Writes data to the I2C bus using the GMBUS I2C controller
+
+  @param[in]  DdcBusPinPair               - The GPIO pin pair to use for the write operation
+  @param[in]  SlaveAddress                - The I2C device address to write data to
+  @param[in]  SendStopCondition           - TRUE:  After the write is complete, send a STOP condition to the I2C bus
+                                            FALSE: Don't send a STOP after the write is complete, this allows one to
+                                                   immediately send a repeated START condition to the I2C bus after
+                                                   GmbusRead() exits by calling either GmbusRead() or GmbusWrite()
+                                                   immediately after this function returns.
+  @param[in]  ByteCount                   - The number of bytes to write to the I2C bus.
+  @param[in]  WriteBuffer                 - The data to be written to the I2C bus.
+
+  @retval  EFI_SUCCESS                    - The data was successfully written.
+  @retval  EFI_INVALID_PARAMETER          - One of the following conditions:
+                                            * ByteCount is 0 or >GMBUS_MAX_BYTES.
+                                            * WriteBuffer is NULL
+                                            * SlaveAddress does not have BIT0 cleared (required for writes.)
+  @retval  EFI_TIMEOUT                    - The GMBUS I2C controller did not respond within the required timeout period.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while accessing the GMBUS I2C controller.
+**/
+EFI_STATUS
+GmbusWrite (
+  IN  UINT8             DdcBusPinPair,
+  IN  UINT8             SlaveAddress,
+  IN  BOOLEAN           SendStopCondition,
+  IN  UINT32            ByteCount,
+  IN  UINT8             *WriteBuffer
+  );

+ 189 - 0
Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/I2cDebugPortProtocol.c

@@ -0,0 +1,189 @@
+/** @file
+  I2C Debug Port Protocol Implementation
+
+  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/PcdLib.h>
+#include <Library/TimerLib.h>
+
+#include <IgfxI2c.h>
+#include <Gmbus.h>
+#include <I2cDebugPortProtocol.h>
+
+/**
+  Writes data to the I2C debug port
+
+  @param[in]  Buffer                      - The data to be written to the I2C debug port.
+  @param[in]  Count                       - The number of bytes to write to the I2C debug port.
+
+  @retval  EFI_SUCCESS                    - The data was successfully written.
+  @retval  EFI_INVALID_PARAMETER          - One of the following conditions:
+                                            * ByteCount is 0
+                                            * Buffer is NULL
+  @retval  EFI_TIMEOUT                    - The I2C controller did not respond within the required timeout period.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while accessing the I2C controller.
+**/
+EFI_STATUS
+I2cDebugPortWrite (
+  IN  UINT8             *Buffer,
+  IN  UINT32            Count
+  )
+{
+  UINT8       WriteBuffer[I2C_DEBUG_PORT_MAX_DATA_SIZE + 1];
+  EFI_STATUS  Status;
+  UINT32      Index;
+  UINT8       CurrentSize;
+  UINT8       DdcBusPinPair;
+
+  if (Count <= 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+  if (Buffer == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+  Status = GetGmbusBusPinPairForI2cDebugPort (&DdcBusPinPair);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  RaiseTplForI2cDebugPortAccess ();
+  for (Index = 0; Index < Count; Index += I2C_DEBUG_PORT_MAX_DATA_SIZE) {
+    MicroSecondDelay (3000);  //3ms stall to let the BusPirate catch up
+    if ((Index + I2C_DEBUG_PORT_MAX_DATA_SIZE) >= Count) {
+      CurrentSize = (UINT8) (Count - Index);
+    } else {
+      CurrentSize = I2C_DEBUG_PORT_MAX_DATA_SIZE;
+    }
+    WriteBuffer[0]  = (I2C_DEBUG_PORT_WRITE_COMMAND << I2C_DEBUG_PORT_COMMAND_BIT_POSITION) |
+                      (CurrentSize & I2C_DEBUG_PORT_DATA_SIZE_BIT_MASK);
+    CopyMem (&(WriteBuffer[1]), &(Buffer[Index]), CurrentSize);
+    Status = GmbusWrite (DdcBusPinPair, I2C_DEBUG_PORT_WRITE_DEVICE_ADDRESS, TRUE, CurrentSize + 1, &(WriteBuffer[0]));
+    if (EFI_ERROR (Status)) {
+      break;
+    }
+  }
+  RestoreTplAfterI2cDebugPortAccess ();
+
+  return Status;
+}
+
+/**
+  Reads data from the I2C debug port
+
+  @param[out]     Buffer                  - The memory buffer to return the read data.
+  @param[in, out] Count                   - The number of bytes to read from the I2C debug port.
+                                            On output, the number of bytes actually read.
+
+  @retval  EFI_SUCCESS                    - The data was successfully read.
+  @retval  EFI_INVALID_PARAMETER          - One of the following conditions:
+                                            * ByteCount is 0
+                                            * Buffer is NULL
+  @retval  EFI_TIMEOUT                    - The I2C controller did not respond within the required timeout period.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while accessing the I2C controller.
+**/
+EFI_STATUS
+I2cDebugPortRead (
+  OUT     UINT8         *Buffer,
+  IN OUT  UINT32        *Count
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      Index;
+  UINT32      BytesRead;
+  UINT32      CurrentSize;
+  UINT8       DdcBusPinPair;
+  UINT8       GmbusIndexData;
+
+  BytesRead = 0;
+  if ((*Count) <= 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+  if (Buffer == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+  Status = GetGmbusBusPinPairForI2cDebugPort (&DdcBusPinPair);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  RaiseTplForI2cDebugPortAccess ();
+  for (Index = 0; Index < (*Count); Index += I2C_DEBUG_PORT_MAX_DATA_SIZE) {
+    MicroSecondDelay (3000);  //3ms stall to let the BusPirate catch up
+    if ((Index + I2C_DEBUG_PORT_MAX_DATA_SIZE) >= (*Count)) {
+      CurrentSize = (*Count) - Index;
+    } else {
+      CurrentSize = I2C_DEBUG_PORT_MAX_DATA_SIZE;
+    }
+    GmbusIndexData  = (I2C_DEBUG_PORT_READ_COMMAND << I2C_DEBUG_PORT_COMMAND_BIT_POSITION) |
+                      (CurrentSize & I2C_DEBUG_PORT_DATA_SIZE_BIT_MASK);
+    Status  = GmbusRead (
+                DdcBusPinPair,
+                I2C_DEBUG_PORT_READ_DEVICE_ADDRESS,
+                TRUE,
+                TRUE,
+                GmbusIndexData,
+                &CurrentSize,
+                &(Buffer[Index])
+                );
+    if (EFI_ERROR (Status)) {
+      break;
+    }
+    BytesRead += CurrentSize;
+    if (((Index + I2C_DEBUG_PORT_MAX_DATA_SIZE) < (*Count)) && (CurrentSize < I2C_DEBUG_PORT_MAX_DATA_SIZE)) {
+      break;
+    }
+  }
+  RestoreTplAfterI2cDebugPortAccess ();
+
+  (*Count) = BytesRead;
+  return Status;
+}
+
+/**
+  Queries the I2C debug port to see if there are any data waiting to be read
+
+  @param[out] NumberOfBytesInFifoBuffer   - The number of bytes sitting in the I2C debug port's
+                                            FIFO buffer waiting to be read
+
+  @retval  EFI_SUCCESS                    - The I2C debug port was successfully queried
+  @retval  EFI_INVALID_PARAMETER          - One of the following conditions:
+                                            * NumberOfBytesInFifoBuffer is NULL
+  @retval  EFI_TIMEOUT                    - The I2C controller did not respond within the required timeout period.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while accessing the I2C controller.
+**/
+EFI_STATUS
+I2cDebugPortReadyToRead (
+  OUT     UINT8         *NumberOfBytesInFifoBuffer
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      BytesRead;
+  UINT8       DdcBusPinPair;
+  UINT8       GmbusIndexData;
+
+  BytesRead = 1;
+  if (NumberOfBytesInFifoBuffer == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+  Status = GetGmbusBusPinPairForI2cDebugPort (&DdcBusPinPair);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  MicroSecondDelay (3000);  //3ms stall to let the BusPirate catch up
+  GmbusIndexData  = (I2C_DEBUG_PORT_READY_TO_READ_COMMAND << I2C_DEBUG_PORT_COMMAND_BIT_POSITION) |
+                    (1 & I2C_DEBUG_PORT_DATA_SIZE_BIT_MASK); //READY_TO_READ always returns 1 byte
+  RaiseTplForI2cDebugPortAccess ();
+  Status = GmbusRead (DdcBusPinPair, I2C_DEBUG_PORT_READ_DEVICE_ADDRESS, TRUE, TRUE, GmbusIndexData, &BytesRead, NumberOfBytesInFifoBuffer);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  RestoreTplAfterI2cDebugPortAccess ();
+  if (BytesRead != 1) {
+    Status = EFI_DEVICE_ERROR;
+  }
+  return Status;
+}

+ 77 - 0
Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/I2cDebugPortProtocol.h

@@ -0,0 +1,77 @@
+/** @file
+  I2C Debug Port Protocol Implementation
+
+  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi/UefiBaseType.h>
+
+#define I2C_DEBUG_PORT_WRITE_DEVICE_ADDRESS 0xDE
+#define I2C_DEBUG_PORT_READ_DEVICE_ADDRESS 0xDF
+
+#define I2C_DEBUG_PORT_WRITE_COMMAND 0x00
+#define I2C_DEBUG_PORT_READ_COMMAND 0x01
+#define I2C_DEBUG_PORT_READY_TO_READ_COMMAND 0x02
+
+#define I2C_DEBUG_PORT_COMMAND_BIT_POSITION 5
+#define I2C_DEBUG_PORT_COMMAND_BIT_MASK 0x7
+#define I2C_DEBUG_PORT_DATA_SIZE_BIT_MASK 0x1F
+#define I2C_DEBUG_PORT_MAX_DATA_SIZE  I2C_DEBUG_PORT_DATA_SIZE_BIT_MASK
+
+/**
+  Writes data to the I2C debug port
+
+  @param[in]  Buffer                      - The data to be written to the I2C debug port.
+  @param[in]  Count                       - The number of bytes to write to the I2C debug port.
+
+  @retval  EFI_SUCCESS                    - The data was successfully written.
+  @retval  EFI_INVALID_PARAMETER          - One of the following conditions:
+                                            * ByteCount is 0
+                                            * Buffer is NULL
+  @retval  EFI_TIMEOUT                    - The I2C controller did not respond within the required timeout period.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while accessing the I2C controller.
+**/
+EFI_STATUS
+I2cDebugPortWrite (
+  IN  UINT8             *Buffer,
+  IN  UINT32            Count
+  );
+
+/**
+  Reads data from the I2C debug port
+
+  @param[out]     Buffer                  - The memory buffer to return the read data.
+  @param[in, out] Count                   - The number of bytes to read from the I2C debug port.
+                                            On output, the number of bytes actually read.
+
+  @retval  EFI_SUCCESS                    - The data was successfully read.
+  @retval  EFI_INVALID_PARAMETER          - One of the following conditions:
+                                            * ByteCount is 0
+                                            * Buffer is NULL
+  @retval  EFI_TIMEOUT                    - The I2C controller did not respond within the required timeout period.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while accessing the I2C controller.
+**/
+EFI_STATUS
+I2cDebugPortRead (
+  OUT     UINT8         *Buffer,
+  IN OUT  UINT32        *Count
+  );
+
+/**
+  Queries the I2C debug port to see if there are any data waiting to be read
+
+  @param[out] NumberOfBytesInFifoBuffer   - The number of bytes sitting in the I2C debug port's
+                                            FIFO buffer waiting to be read
+
+  @retval  EFI_SUCCESS                    - The I2C debug port was successfully queried
+  @retval  EFI_INVALID_PARAMETER          - One of the following conditions:
+                                            * NumberOfBytesInFifoBuffer is NULL
+  @retval  EFI_TIMEOUT                    - The I2C controller did not respond within the required timeout period.
+  @retval  EFI_DEVICE_ERROR               - An error occurred while accessing the I2C controller.
+**/
+EFI_STATUS
+I2cDebugPortReadyToRead (
+  OUT     UINT8         *NumberOfBytesInFifoBuffer
+  );

+ 44 - 0
Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/I2cDebugPortTplDxe.c

@@ -0,0 +1,44 @@
+/** @file
+  Serial I/O Port library implementation for the HDMI I2C Debug Port
+  DXE Library implementation
+
+Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+STATIC EFI_TPL  mPreviousTpl  = 0;
+
+/**
+  For boot phases that utilize task priority levels (TPLs), this function raises
+  the TPL to the appriopriate level needed to execute I/O to the I2C Debug Port
+**/
+VOID
+RaiseTplForI2cDebugPortAccess (
+  VOID
+  )
+{
+  if (EfiGetCurrentTpl () < TPL_NOTIFY) {
+    mPreviousTpl = gBS->RaiseTPL (TPL_NOTIFY);
+  }
+}
+
+/**
+  For boot phases that utilize task priority levels (TPLs), this function
+  restores the TPL to the previous level after I/O to the I2C Debug Port is
+  complete
+**/
+VOID
+RestoreTplAfterI2cDebugPortAccess (
+  VOID
+  )
+{
+  if (mPreviousTpl > 0) {
+    gBS->RestoreTPL (mPreviousTpl);
+    mPreviousTpl = 0;
+  }
+}

+ 36 - 0
Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/I2cDebugPortTplNull.c

@@ -0,0 +1,36 @@
+/** @file
+  Serial I/O Port library implementation for the HDMI I2C Debug Port
+  Null implementation of Task Priority Level functions.
+  This implementation is used by SEC, PEI, & SMM
+
+Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi/UefiBaseType.h>
+
+/**
+  For boot phases that utilize task priority levels (TPLs), this function raises
+  the TPL to the appriopriate level needed to execute I/O to the I2C Debug Port
+**/
+VOID
+RaiseTplForI2cDebugPortAccess (
+  VOID
+  )
+{
+  return;
+}
+
+/**
+  For boot phases that utilize task priority levels (TPLs), this function
+  restores the TPL to the previous level after I/O to the I2C Debug Port is
+  complete
+**/
+VOID
+RestoreTplAfterI2cDebugPortAccess (
+  VOID
+  )
+{
+  return;
+}

+ 198 - 0
Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/I2cHdmiDebugSerialPortLib.c

@@ -0,0 +1,198 @@
+/** @file
+  Serial I/O Port library implementation for the HDMI I2C Debug Port
+
+Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <Library/BaseLib.h>
+#include <Library/SerialPortLib.h>
+#include <Library/PcdLib.h>
+#include <Library/TimerLib.h>
+
+#include <IgfxI2c.h>
+#include <I2cDebugPortProtocol.h>
+
+/**
+  Initialize the serial device hardware.
+
+  If no initialization is required, then return RETURN_SUCCESS.
+  If the serial device was successfully initialized, then return RETURN_SUCCESS.
+  If the serial device could not be initialized, then return RETURN_DEVICE_ERROR.
+
+  @retval RETURN_SUCCESS        The serial device was initialized.
+  @retval RETURN_DEVICE_ERROR   The serial device could not be initialized.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortInitialize (
+  VOID
+  )
+{
+  return RETURN_SUCCESS;
+}
+
+/**
+  Write data from buffer to serial device.
+
+  Writes NumberOfBytes data bytes from Buffer to the serial device.
+  The number of bytes actually written to the serial device is returned.
+  If the return value is less than NumberOfBytes, then the write operation failed.
+  If Buffer is NULL, then ASSERT().
+  If NumberOfBytes is zero, then return 0.
+
+  @param  Buffer           Pointer to the data buffer to be written.
+  @param  NumberOfBytes    Number of bytes to written to the serial device.
+
+  @retval 0                NumberOfBytes is 0.
+  @retval >0               The number of bytes written to the serial device.
+                           If this value is less than NumberOfBytes, then the write operation failed.
+
+**/
+UINTN
+EFIAPI
+SerialPortWrite (
+  IN UINT8     *Buffer,
+  IN UINTN     NumberOfBytes
+  )
+{
+  EFI_STATUS  Status;
+  /******************************************************/
+  /* WARNING: The GPIOs Need to be programmed first.    */
+  /*          Make sure HdmiDebugGpioInit() runs before */
+  /*          this function is called!!!                */
+  /******************************************************/
+  if (Buffer == NULL) {
+    return 0;
+  }
+  if (NumberOfBytes == 0) {
+    return 0;
+  }
+  Status = I2cDebugPortWrite (Buffer, (UINT32) NumberOfBytes);
+  if (EFI_ERROR (Status)) {
+    return 0;
+  } else {
+    return NumberOfBytes;
+  }
+}
+
+/**
+  Read data from serial device and save the datas in buffer.
+
+  Reads NumberOfBytes data bytes from a serial 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 Buffer is NULL, then ASSERT().
+  If NumberOfBytes is zero, then return 0.
+
+  @param  Buffer           Pointer to the data buffer to store the data read from the serial device.
+  @param  NumberOfBytes    Number of bytes which will be read.
+
+  @retval 0                Read data failed, no data is to be read.
+  @retval >0               Actual number of bytes read from serial device.
+
+**/
+UINTN
+EFIAPI
+SerialPortRead (
+  OUT UINT8     *Buffer,
+  IN  UINTN     NumberOfBytes
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      BytesRead;
+
+  if (Buffer == NULL) {
+    return 0;
+  }
+  if (NumberOfBytes == 0) {
+    return 0;
+  }
+  BytesRead = (UINT32) NumberOfBytes;
+  Status = I2cDebugPortRead (Buffer, &BytesRead);
+  if (EFI_ERROR (Status)) {
+    return 0;
+  } else {
+    return (UINTN) BytesRead;
+  }
+}
+
+/**
+  Polls a serial device to see if there is any data waiting to be read.
+
+  Polls a serial device to see if there is any data waiting to be read.
+  If there is data waiting to be read from the serial device, then TRUE is returned.
+  If there is no data waiting to be read from the serial device, then FALSE is returned.
+
+  @retval TRUE             Data is waiting to be read from the serial device.
+  @retval FALSE            There is no data waiting to be read from the serial device.
+
+**/
+BOOLEAN
+EFIAPI
+SerialPortPoll (
+  VOID
+  )
+{
+  EFI_STATUS  Status;
+  UINT8       NumberOfBytesInFifoBuffer;
+
+  Status = I2cDebugPortReadyToRead (&NumberOfBytesInFifoBuffer);
+  if (EFI_ERROR (Status)) {
+    return FALSE;
+  }
+  if (NumberOfBytesInFifoBuffer <= 0) {
+    return FALSE;
+  }
+  return TRUE;
+}
+
+/**
+  Sets the baud rate, receive FIFO depth, transmit/receice time out, parity,
+  data bits, and stop bits on a serial device.
+
+  @param BaudRate           The requested baud rate. A BaudRate value of 0 will use the
+                            device's default interface speed.
+                            On output, the value actually set.
+  @param ReveiveFifoDepth   The requested depth of the FIFO on the receive side of the
+                            serial interface. A ReceiveFifoDepth value of 0 will use
+                            the device's default FIFO depth.
+                            On output, the value actually set.
+  @param Timeout            The requested time out for a single character in microseconds.
+                            This timeout applies to both the transmit and receive side of the
+                            interface. A Timeout value of 0 will use the device's default time
+                            out value.
+                            On output, the value actually set.
+  @param Parity             The type of parity to use on this serial device. A Parity value of
+                            DefaultParity will use the device's default parity value.
+                            On output, the value actually set.
+  @param DataBits           The number of data bits to use on the serial device. A DataBits
+                            vaule of 0 will use the device's default data bit setting.
+                            On output, the value actually set.
+  @param StopBits           The number of stop bits to use on this serial device. A StopBits
+                            value of DefaultStopBits will use the device's default number of
+                            stop bits.
+                            On output, the value actually set.
+
+  @retval RETURN_SUCCESS            The new attributes were set on the serial device.
+  @retval RETURN_UNSUPPORTED        The serial device does not support this operation.
+  @retval RETURN_INVALID_PARAMETER  One or more of the attributes has an unsupported value.
+  @retval RETURN_DEVICE_ERROR       The serial device is not functioning correctly.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortSetAttributes (
+  IN OUT UINT64             *BaudRate,
+  IN OUT UINT32             *ReceiveFifoDepth,
+  IN OUT UINT32             *Timeout,
+  IN OUT EFI_PARITY_TYPE    *Parity,
+  IN OUT UINT8              *DataBits,
+  IN OUT EFI_STOP_BITS_TYPE *StopBits
+  )
+{
+  return EFI_UNSUPPORTED;
+}

+ 79 - 0
Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/IgfxI2c.c

@@ -0,0 +1,79 @@
+/** @file
+  Intel Graphics I2C Bus I/O
+
+  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/IoLib.h>
+#include <Library/PciLib.h>
+#include <IgfxI2c.h>
+
+/**
+  Returns the GPIO pin pair to use for the given DDC channel
+
+  @param[in]  Channel                     - The DDC I2C channel.
+  @param[out] DdcBusPinPair               - The GPIO pin pair for the given DDC channel.
+
+  @retval  EFI_SUCCESS                    - The GPIO pin pair was successfully determined
+  @retval  EFI_INVALID_PARAMETER          - The given DDC I2C channel does not exist.
+  @retval  EFI_UNSUPPORTED                - The platform is using a PCH that is not supported yet.
+**/
+EFI_STATUS
+GetGmbusBusPinPair (
+  IN  IGFX_I2C_CHANNEL  Channel,
+  OUT UINT8             *DdcBusPinPair
+  )
+{
+  PCH_TYPE  PchType;
+  *DdcBusPinPair = 0;
+
+  PchType = GetPchType ();
+  switch (PchType) {
+    // The PCH design lineage from SkyLake, KabyLake, AmberLake, & early CoffeeLake
+    case PchTypeSptLp:
+    case PchTypeSptH:
+    case PchTypeKbpH:
+      switch (Channel) {
+        case EnumDdcB:
+          *DdcBusPinPair = V_KBL_PCH_HDMI_DDC_B_PIN_PAIR;
+          return EFI_SUCCESS;
+        case EnumDdcC:
+          *DdcBusPinPair = V_KBL_PCH_HDMI_DDC_C_PIN_PAIR;
+          return EFI_SUCCESS;
+        case EnumDdcD:
+          *DdcBusPinPair = V_KBL_PCH_HDMI_DDC_D_PIN_PAIR;
+          return EFI_SUCCESS;
+
+        default:
+          return EFI_INVALID_PARAMETER;
+      }
+      break;
+    // The PCH design lineage from newer CoffeeLake & WhiskeyLake
+    case PchTypeCnlLp:
+    case PchTypeCnlH:
+      switch (Channel) {
+        case EnumDdcB:
+          *DdcBusPinPair = V_CNL_PCH_HDMI_DDC_B_PIN_PAIR;
+          return EFI_SUCCESS;
+        case EnumDdcC:
+          *DdcBusPinPair = V_CNL_PCH_HDMI_DDC_C_PIN_PAIR;
+          return EFI_SUCCESS;
+        case EnumDdcD:
+          *DdcBusPinPair = V_CNL_PCH_HDMI_DDC_D_PIN_PAIR;
+          return EFI_SUCCESS;
+        case EnumDdcF:
+          *DdcBusPinPair = V_CNL_PCH_HDMI_DDC_F_PIN_PAIR;
+          return EFI_SUCCESS;
+
+        default:
+          return EFI_INVALID_PARAMETER;
+      }
+      break;
+  }
+
+  return EFI_UNSUPPORTED;
+}

+ 99 - 0
Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/IgfxI2c.h

@@ -0,0 +1,99 @@
+/** @file
+  Intel Graphics I2C Bus I/O
+
+  Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  <b>Conventions</b>:
+  - Prefixes:
+    - Definitions beginning with "R_" are registers
+    - Definitions beginning with "B_" are bits within registers
+    - Definitions beginning with "V_" are meaningful values of bits within the registers
+    - Definitions beginning with "S_" are register sizes
+    - Definitions beginning with "N_" are the bit position
+**/
+
+#include <Uefi/UefiBaseType.h>
+#include <Library/HdmiDebugPchDetectionLib.h>
+
+//
+// HDMI DDC Pin Pairs
+//
+#define V_CNL_PCH_HDMI_DDC_B_PIN_PAIR                     0x01
+#define V_CNL_PCH_HDMI_DDC_C_PIN_PAIR                     0x02
+#define V_CNL_PCH_HDMI_DDC_D_PIN_PAIR                     0x04
+#define V_CNL_PCH_HDMI_DDC_F_PIN_PAIR                     0x03
+
+#define V_KBL_PCH_HDMI_DDC_B_PIN_PAIR                     0x05
+#define V_KBL_PCH_HDMI_DDC_C_PIN_PAIR                     0x04
+#define V_KBL_PCH_HDMI_DDC_D_PIN_PAIR                     0x06
+
+typedef enum {
+  EnumDdcUnknown = 0,
+  EnumDdcA,
+  EnumDdcB,
+  EnumDdcC,
+  EnumDdcD,
+  EnumDdcE,
+  EnumDdcF,
+  EnumI2cChannelMax
+} IGFX_I2C_CHANNEL;
+
+/**
+  Returns the type of PCH on the system
+
+  @retval   The PCH type.
+**/
+PCH_TYPE
+GetPchType (
+  VOID
+  );
+
+/**
+  Returns the GPIO pin pair to use for the given DDC channel
+
+  @param[in]  Channel                     - The DDC I2C channel.
+  @param[out] DdcBusPinPair               - The GPIO pin pair for the given DDC channel.
+
+  @retval  EFI_SUCCESS                    - The GPIO pin pair was successfully determined
+  @retval  EFI_INVALID_PARAMETER          - The given DDC I2C channel does not exist.
+  @retval  EFI_UNSUPPORTED                - The platform is using a PCH that is not supported yet.
+**/
+EFI_STATUS
+GetGmbusBusPinPair (
+  IN  IGFX_I2C_CHANNEL  Channel,
+  OUT UINT8             *DdcBusPinPair
+  );
+
+/**
+  Returns the GPIO pin pair to use for the I2C HDMI debug port
+
+  @param[out] DdcBusPinPair               - The GPIO pin pair for the I2C HDMI debug port.
+
+  @retval  EFI_SUCCESS                    - The GPIO pin pair was successfully determined
+  @retval  EFI_INVALID_PARAMETER          - The given DDC I2C channel does not exist.
+  @retval  EFI_UNSUPPORTED                - The platform is using a PCH that is not supported yet.
+**/
+EFI_STATUS
+GetGmbusBusPinPairForI2cDebugPort (
+  OUT UINT8             *DdcBusPinPair
+  );
+
+/**
+  For boot phases that utilize task priority levels (TPLs), this function raises
+  the TPL to the appriopriate level needed to execute I/O to the I2C Debug Port
+**/
+VOID
+RaiseTplForI2cDebugPortAccess (
+  VOID
+  );
+
+/**
+  For boot phases that utilize task priority levels (TPLs), this function
+  restores the TPL to the previous level after I/O to the I2C Debug Port is
+  complete
+**/
+VOID
+RestoreTplAfterI2cDebugPortAccess (
+  VOID
+  );

+ 224 - 0
Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/PeiI2cHdmiDebugSerialPortLib.c

@@ -0,0 +1,224 @@
+/** @file
+  Serial I/O Port library implementation for the HDMI I2C Debug Port
+  PEI Library implementation
+
+Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <PiPei.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/SerialPortLib.h>
+#include <Library/HobLib.h>
+#include <Library/PcdLib.h>
+
+#include <IgfxI2c.h>
+#include <I2cDebugPortProtocol.h>
+
+extern EFI_GUID gI2cHdmiDebugHobGuid;
+
+typedef struct {
+  PCH_TYPE              PchType;
+  UINT32                ControlBits;
+  UINT8                 I2cHdmiDebugDdcBusPinPair;
+  BOOLEAN               IgdBusMasterReset;
+} I2C_HDMI_DEBUG_PORT_CONTEXT;
+
+I2C_HDMI_DEBUG_PORT_CONTEXT*
+GetI2cHdmiDebugPortContext (
+  VOID
+  )
+{
+  EFI_STATUS                    Status;
+  EFI_HOB_GUID_TYPE             *GuidHob;
+  I2C_HDMI_DEBUG_PORT_CONTEXT   *Context;
+  UINT8                         I2cHdmiDebugDdcBusPinPair;
+
+  I2cHdmiDebugDdcBusPinPair = 0;
+  GuidHob = GetFirstGuidHob (&gI2cHdmiDebugHobGuid);
+  if (GuidHob == NULL) {
+    Context = (I2C_HDMI_DEBUG_PORT_CONTEXT *) BuildGuidHob (&gI2cHdmiDebugHobGuid, sizeof (I2C_HDMI_DEBUG_PORT_CONTEXT));
+    if (Context == NULL) {
+      return NULL;
+    }
+    ZeroMem ((VOID *) Context, sizeof (I2C_HDMI_DEBUG_PORT_CONTEXT));
+    Context->PchType = GetPchTypeInternal ();
+    Status  = GetGmbusBusPinPair (
+                (IGFX_I2C_CHANNEL) PcdGet32 (PcdI2cHdmiDebugPortDdcI2cChannel),
+                &I2cHdmiDebugDdcBusPinPair
+                );
+    if (EFI_ERROR (Status)) {
+      I2cHdmiDebugDdcBusPinPair = 0;
+    }
+    Context->I2cHdmiDebugDdcBusPinPair  = I2cHdmiDebugDdcBusPinPair;
+    return Context;
+  } else {
+    return GET_GUID_HOB_DATA (GuidHob);
+  }
+}
+
+/**
+  Sets the control bits on a serial device.
+
+  @param Control                Sets the bits of Control that are settable.
+
+  @retval RETURN_SUCCESS        The new control bits were set on the serial device.
+  @retval RETURN_UNSUPPORTED    The serial device does not support this operation.
+  @retval RETURN_DEVICE_ERROR   The serial device is not functioning correctly.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortSetControl (
+  IN UINT32 Control
+  )
+{
+  I2C_HDMI_DEBUG_PORT_CONTEXT   *Context;
+
+  //
+  // check for invalid control parameters
+  //
+  if ((Control & (~(EFI_SERIAL_REQUEST_TO_SEND          |
+                    EFI_SERIAL_DATA_TERMINAL_READY      |
+                    EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE |
+                    EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE |
+                    EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE))) != 0 ) {
+    return EFI_UNSUPPORTED;
+  }
+
+  Context = GetI2cHdmiDebugPortContext ();
+  if (Context == NULL) {
+    return EFI_UNSUPPORTED;
+  }
+  Control &= (UINT32) ~(EFI_SERIAL_INPUT_BUFFER_EMPTY);
+  Context->ControlBits = Control;
+  return EFI_SUCCESS;
+}
+
+/**
+  Retrieve the status of the control bits on a serial device.
+
+  @param Control                A pointer to return the current control signals from the serial device.
+
+  @retval RETURN_SUCCESS        The control bits were read from the serial device.
+  @retval RETURN_UNSUPPORTED    The serial device does not support this operation.
+  @retval RETURN_DEVICE_ERROR   The serial device is not functioning correctly.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortGetControl (
+  OUT UINT32 *Control
+  )
+{
+  EFI_STATUS                    Status;
+  I2C_HDMI_DEBUG_PORT_CONTEXT   *Context;
+  UINT8                         NumberOfBytesInFifoBuffer;
+
+  Context = GetI2cHdmiDebugPortContext ();
+  if (Context == NULL) {
+    return EFI_UNSUPPORTED;
+  }
+  Status = I2cDebugPortReadyToRead (&NumberOfBytesInFifoBuffer);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  *Control = (EFI_SERIAL_CLEAR_TO_SEND | EFI_SERIAL_DATA_SET_READY |
+              EFI_SERIAL_CARRIER_DETECT | EFI_SERIAL_OUTPUT_BUFFER_EMPTY);
+  if (NumberOfBytesInFifoBuffer <= 0) {
+    *Control |= EFI_SERIAL_INPUT_BUFFER_EMPTY;
+  }
+  *Control |= Context->ControlBits;
+  return Status;
+}
+
+/**
+  Returns the type of PCH on the system
+
+  @retval   The PCH type.
+**/
+PCH_TYPE
+GetPchType (
+  VOID
+  )
+{
+  I2C_HDMI_DEBUG_PORT_CONTEXT   *Context;
+
+  Context = GetI2cHdmiDebugPortContext ();
+  if (Context == NULL) {
+    return PchTypeUnknown;
+  }
+  return Context->PchType;
+}
+
+/**
+  Returns the GPIO pin pair to use for the I2C HDMI debug port
+
+  @param[out] DdcBusPinPair               - The GPIO pin pair for the I2C HDMI debug port.
+
+  @retval  EFI_SUCCESS                    - The GPIO pin pair was successfully determined
+  @retval  EFI_INVALID_PARAMETER          - The given DDC I2C channel does not exist.
+  @retval  EFI_UNSUPPORTED                - The platform is using a PCH that is not supported yet.
+**/
+EFI_STATUS
+GetGmbusBusPinPairForI2cDebugPort (
+  OUT UINT8             *DdcBusPinPair
+  )
+{
+  I2C_HDMI_DEBUG_PORT_CONTEXT   *Context;
+
+  Context = GetI2cHdmiDebugPortContext ();
+  if (Context == NULL) {
+    return EFI_UNSUPPORTED;
+  }
+
+  *DdcBusPinPair = Context->I2cHdmiDebugDdcBusPinPair;
+  return EFI_SUCCESS;
+}
+
+/**
+  Returns a flag indicating whether the IGD device bus master enable needs to
+  be disabled at the end of the current transaction
+
+  @retval  TRUE                           - IGD Bus Master Enable needs to be reset
+  @retval  FALSE                          - IGD Bus Master Enable does not need to be reset
+**/
+BOOLEAN
+GetIgdBusMasterReset (
+  VOID
+  )
+{
+  I2C_HDMI_DEBUG_PORT_CONTEXT   *Context;
+
+  Context = GetI2cHdmiDebugPortContext ();
+  if (Context == NULL) {
+    return TRUE;
+  }
+
+  return Context->IgdBusMasterReset;
+}
+
+/**
+  Sets a flag indicating whether the IGD device bus master enable needs to
+  be disabled at the end of the current transaction
+
+  @param[in]  IgdBusMasterReset           - IGD device bus master enable flag
+**/
+VOID
+SetIgdBusMasterReset (
+  BOOLEAN IgdBusMasterReset
+  )
+{
+  I2C_HDMI_DEBUG_PORT_CONTEXT   *Context;
+
+  Context = GetI2cHdmiDebugPortContext ();
+  if (Context == NULL) {
+    return;
+  }
+
+  Context->IgdBusMasterReset = IgdBusMasterReset;
+}

+ 54 - 0
Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/PeiI2cHdmiDebugSerialPortLib.inf

@@ -0,0 +1,54 @@
+### @file
+# Component description file for Serial I/O Port library for the HDMI I2C Debug Port
+#
+# Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = PeiI2cHdmiDebugSerialPortLib
+  FILE_GUID                      = 9B537D5A-BD66-4FD5-A3F2-F3377840492E
+  VERSION_STRING                 = 1.0
+  MODULE_TYPE                    = BASE
+  LIBRARY_CLASS                  = SerialPortLib|PEI_CORE PEIM
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  PcdLib
+  TimerLib
+  PciLib
+  HdmiDebugPchDetectionLib
+
+[Packages]
+  MdePkg/MdePkg.dec
+  KabylakeOpenBoardPkg/OpenBoardPkg.dec
+  KabylakeSiliconPkg/SiPkg.dec
+
+[Sources]
+  PeiI2cHdmiDebugSerialPortLib.c
+  Gmbus.c
+  Gmbus.h
+  I2cDebugPortProtocol.c
+  I2cDebugPortProtocol.h
+  I2cDebugPortTplNull.c
+  I2cHdmiDebugSerialPortLib.c
+  IgfxI2c.c
+  IgfxI2c.h
+
+[Ppis]
+
+[Guids]
+  gI2cHdmiDebugHobGuid
+
+[Pcd]
+  gKabylakeOpenBoardPkgTokenSpaceGuid.PcdI2cHdmiDebugPortDdcI2cChannel    ## CONSUMES
+  gKabylakeOpenBoardPkgTokenSpaceGuid.PcdGttMmAddress                     ## CONSUMES

+ 134 - 0
Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/SecI2cHdmiDebugSerialPortLib.c

@@ -0,0 +1,134 @@
+/** @file
+  Serial I/O Port library implementation for the HDMI I2C Debug Port
+  Generic Base Library implementation
+
+Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <Library/BaseLib.h>
+#include <Library/SerialPortLib.h>
+#include <Library/PcdLib.h>
+
+#include <IgfxI2c.h>
+#include <I2cDebugPortProtocol.h>
+
+/**
+  Sets the control bits on a serial device.
+
+  @param Control                Sets the bits of Control that are settable.
+
+  @retval RETURN_SUCCESS        The new control bits were set on the serial device.
+  @retval RETURN_UNSUPPORTED    The serial device does not support this operation.
+  @retval RETURN_DEVICE_ERROR   The serial device is not functioning correctly.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortSetControl (
+  IN UINT32 Control
+  )
+{
+  //
+  // check for invalid control parameters
+  //
+  if ((Control & (~(EFI_SERIAL_REQUEST_TO_SEND          |
+                    EFI_SERIAL_DATA_TERMINAL_READY      |
+                    EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE |
+                    EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE |
+                    EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE))) != 0 ) {
+    return EFI_UNSUPPORTED;
+  }
+  return EFI_SUCCESS;
+}
+
+/**
+  Retrieve the status of the control bits on a serial device.
+
+  @param Control                A pointer to return the current control signals from the serial device.
+
+  @retval RETURN_SUCCESS        The control bits were read from the serial device.
+  @retval RETURN_UNSUPPORTED    The serial device does not support this operation.
+  @retval RETURN_DEVICE_ERROR   The serial device is not functioning correctly.
+
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortGetControl (
+  OUT UINT32 *Control
+  )
+{
+  EFI_STATUS  Status;
+  UINT8       NumberOfBytesInFifoBuffer;
+
+  Status = I2cDebugPortReadyToRead (&NumberOfBytesInFifoBuffer);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  *Control = (EFI_SERIAL_CLEAR_TO_SEND | EFI_SERIAL_DATA_SET_READY |
+              EFI_SERIAL_CARRIER_DETECT | EFI_SERIAL_OUTPUT_BUFFER_EMPTY);
+  if (NumberOfBytesInFifoBuffer <= 0) {
+    *Control |= EFI_SERIAL_INPUT_BUFFER_EMPTY;
+  }
+  return Status;
+}
+
+/**
+  Returns the type of PCH on the system
+
+  @retval   The PCH type.
+**/
+PCH_TYPE
+GetPchType (
+  VOID
+  )
+{
+  return GetPchTypeInternal ();
+}
+
+/**
+  Returns the GPIO pin pair to use for the I2C HDMI debug port
+
+  @param[out] DdcBusPinPair               - The GPIO pin pair for the I2C HDMI debug port.
+
+  @retval  EFI_SUCCESS                    - The GPIO pin pair was successfully determined
+  @retval  EFI_INVALID_PARAMETER          - The given DDC I2C channel does not exist.
+  @retval  EFI_UNSUPPORTED                - The platform is using a PCH that is not supported yet.
+**/
+EFI_STATUS
+GetGmbusBusPinPairForI2cDebugPort (
+  OUT UINT8             *DdcBusPinPair
+  )
+{
+  return GetGmbusBusPinPair ((IGFX_I2C_CHANNEL) FixedPcdGet32 (PcdI2cHdmiDebugPortDdcI2cChannel), DdcBusPinPair);
+}
+
+/**
+  Returns a flag indicating whether the IGD device bus master enable needs to
+  be disabled at the end of the current transaction
+
+  @retval  TRUE                           - IGD Bus Master Enable needs to be reset
+  @retval  FALSE                          - IGD Bus Master Enable does not need to be reset
+**/
+BOOLEAN
+GetIgdBusMasterReset (
+  VOID
+  )
+{
+  return TRUE;
+}
+
+/**
+  Sets a flag indicating whether the IGD device bus master enable needs to
+  be disabled at the end of the current transaction
+
+  @param[in]  IgdBusMasterReset           - IGD device bus master enable flag
+**/
+VOID
+SetIgdBusMasterReset (
+  BOOLEAN IgdBusMasterReset
+  )
+{
+}

+ 53 - 0
Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/SecI2cHdmiDebugSerialPortLib.inf

@@ -0,0 +1,53 @@
+### @file
+# Component description file for Serial I/O Port library for the HDMI I2C Debug Port
+#
+# Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = BaseI2cHdmiDebugSerialPortLib
+  FILE_GUID                      = 4B838C3E-0D23-4CCE-9069-E0E3D2B9CB49
+  VERSION_STRING                 = 1.0
+  MODULE_TYPE                    = SEC
+  LIBRARY_CLASS                  = SerialPortLib
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  PcdLib
+  TimerLib
+  PciLib
+  HdmiDebugPchDetectionLib
+
+[Packages]
+  MdePkg/MdePkg.dec
+  KabylakeOpenBoardPkg/OpenBoardPkg.dec
+  KabylakeSiliconPkg/SiPkg.dec
+
+[Sources]
+  Gmbus.c
+  Gmbus.h
+  I2cDebugPortProtocol.c
+  I2cDebugPortProtocol.h
+  I2cDebugPortTplNull.c
+  I2cHdmiDebugSerialPortLib.c
+  IgfxI2c.c
+  IgfxI2c.h
+  SecI2cHdmiDebugSerialPortLib.c
+
+[Ppis]
+
+[Guids]
+
+[Pcd]
+  gKabylakeOpenBoardPkgTokenSpaceGuid.PcdI2cHdmiDebugPortDdcI2cChannel    ## CONSUMES
+  gKabylakeOpenBoardPkgTokenSpaceGuid.PcdGttMmAddress                     ## CONSUMES

+ 53 - 0
Platform/Intel/KabylakeOpenBoardPkg/Library/I2cHdmiDebugSerialPortLib/SmmI2cHdmiDebugSerialPortLib.inf

@@ -0,0 +1,53 @@
+### @file
+# Component description file for Serial I/O Port library for the HDMI I2C Debug Port
+#
+# Copyright (c) 2022, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = SmmI2cHdmiDebugSerialPortLib
+  FILE_GUID                      = 14B7E774-CF36-4CEC-AD5E-42FF37363F21
+  VERSION_STRING                 = 1.0
+  MODULE_TYPE                    = DXE_SMM_DRIVER
+  LIBRARY_CLASS                  = SerialPortLib|DXE_SMM_DRIVER SMM_CORE
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  PcdLib
+  TimerLib
+  PciLib
+  HdmiDebugPchDetectionLib
+
+[Packages]
+  MdePkg/MdePkg.dec
+  KabylakeOpenBoardPkg/OpenBoardPkg.dec
+  KabylakeSiliconPkg/SiPkg.dec
+
+[Sources]
+  DxeSmmI2cHdmiDebugSerialPortLib.c
+  Gmbus.c
+  Gmbus.h
+  I2cDebugPortProtocol.c
+  I2cDebugPortProtocol.h
+  I2cDebugPortTplNull.c
+  I2cHdmiDebugSerialPortLib.c
+  IgfxI2c.c
+  IgfxI2c.h
+
+[Ppis]
+
+[Guids]
+
+[Pcd]
+  gKabylakeOpenBoardPkgTokenSpaceGuid.PcdI2cHdmiDebugPortDdcI2cChannel    ## CONSUMES
+  gKabylakeOpenBoardPkgTokenSpaceGuid.PcdGttMmAddress                     ## CONSUMES

+ 13 - 2
Platform/Intel/KabylakeOpenBoardPkg/OpenBoardPkg.dec

@@ -5,7 +5,7 @@
 # INF files to generate AutoGen.c and AutoGen.h files
 # for the build infrastructure.
 #
-# Copyright (c) 2017 - 2019, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2017 - 2022, Intel Corporation. All rights reserved.<BR>
 #
 # SPDX-License-Identifier: BSD-2-Clause-Patent
 #
@@ -35,7 +35,7 @@ gSpiFlashDebugHobGuid                 =  {0xcaaaf418, 0x38a5, 0x4d49, {0xbe, 0x7
 
 gTbtInfoHobGuid                       =  {0x74a81eaa, 0x033c, 0x4783, {0xbe, 0x2b, 0x84, 0x85, 0x74, 0xa6, 0x97, 0xb7}}
 
-gPlatformModuleTokenSpaceGuid         =  {0x69d13bf0, 0xaf91, 0x4d96, {0xaa, 0x9f, 0x21, 0x84, 0xc5, 0xce, 0x3b, 0xc0}}
+gI2cHdmiDebugHobGuid                  =  {0x93a54938, 0x3a3e, 0x48cf, {0x9f, 0x0a, 0xaf, 0x6e, 0x0a, 0xa7, 0xc6, 0x44}}
 
 
 [Protocols]
@@ -52,9 +52,12 @@ gPeiTbtPolicyBoardInitDonePpiGuid     =  {0x970f9c60, 0x8547, 0x49d7, { 0xa4, 0x
 
 [PcdsFixedAtBuild]
 
+gKabylakeOpenBoardPkgTokenSpaceGuid.PcdGttMmAddress|0x00000000|UINT32|0x9000000F
+
 gKabylakeOpenBoardPkgTokenSpaceGuid.PcdLpcIoDecodeRange|0x0010|UINT16|0x10001004
 gKabylakeOpenBoardPkgTokenSpaceGuid.PchLpcIoEnableDecoding|0x3c03|UINT16|0x10001005
 
+
 gKabylakeOpenBoardPkgTokenSpaceGuid.PcdLpcSioIndexPort|0x4e|UINT16|0x90000018
 gKabylakeOpenBoardPkgTokenSpaceGuid.PcdLpcSioDataPort|0x4f|UINT16|0x9000001F
 
@@ -73,6 +76,14 @@ gKabylakeOpenBoardPkgTokenSpaceGuid.PcdFlashNvDebugMessageBase|0x00000000|UINT32
 gKabylakeOpenBoardPkgTokenSpaceGuid.PcdFlashNvDebugMessageSize|0x00000000|UINT32|0x90000031
 gKabylakeOpenBoardPkgTokenSpaceGuid.PcdFlashNvDebugMessageOffset|0x00000000|UINT32|0x90000032
 
+## Specifies the DDC I2C channel to claim as the HDMI debug port
+#  The value is defined as below.
+#  2: DDC channel B
+#  3: DDC channel C
+#  4: DDC channel D
+# @Prompt DDC I2C channel to claim as the HDMI debug port
+gKabylakeOpenBoardPkgTokenSpaceGuid.PcdI2cHdmiDebugPortDdcI2cChannel|0x00000000|UINT32|0x90000035
+
 [PcdsDynamic]
 
 # Board GPIO Table