[edk2] [Patch][edk2-platforms/devel-MinnowBoard3-UDK2017] SMBus Library.

zwei4 posted 1 patch 6 years, 10 months ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/edk2 tags/patchew/20180212080828.21208-1-david.wei@intel.com
.../PeiDxeSmmScSmbusCommonLib.inf                  |  35 ++
.../PeiDxeSmmScSmbusCommonLib/ScSmbusExec.c        | 653 +++++++++++++++++++++
2 files changed, 688 insertions(+)
create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Library/Private/PeiDxeSmmScSmbusCommonLib/PeiDxeSmmScSmbusCommonLib.inf
create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Library/Private/PeiDxeSmmScSmbusCommonLib/ScSmbusExec.c
[edk2] [Patch][edk2-platforms/devel-MinnowBoard3-UDK2017] SMBus Library.
Posted by zwei4 6 years, 10 months ago
Apollo Lake South Cluster SMBus executive code, which is common for PEI, DXE and SMM modules.

Contributed-under: TianoCore Contribution Agreement 1.1

Signed-off-by: zwei4 <david.wei@intel.com>
Cc: Mang Guo <mang.guo@intel.com>
---
 .../PeiDxeSmmScSmbusCommonLib.inf                  |  35 ++
 .../PeiDxeSmmScSmbusCommonLib/ScSmbusExec.c        | 653 +++++++++++++++++++++
 2 files changed, 688 insertions(+)
 create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Library/Private/PeiDxeSmmScSmbusCommonLib/PeiDxeSmmScSmbusCommonLib.inf
 create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Library/Private/PeiDxeSmmScSmbusCommonLib/ScSmbusExec.c

diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Library/Private/PeiDxeSmmScSmbusCommonLib/PeiDxeSmmScSmbusCommonLib.inf b/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Library/Private/PeiDxeSmmScSmbusCommonLib/PeiDxeSmmScSmbusCommonLib.inf
new file mode 100644
index 000000000..2ca6549b8
--- /dev/null
+++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Library/Private/PeiDxeSmmScSmbusCommonLib/PeiDxeSmmScSmbusCommonLib.inf
@@ -0,0 +1,35 @@
+## @file
+#  Apollo Lake South Cluster Smbus Common Lib.
+#
+#  Copyright (c) 1999 - 2018, Intel Corporation. All rights reserved.<BR>
+#
+#  This program and the accompanying materials
+#  are licensed and made available under the terms and conditions of the BSD License
+#  which accompanies this distribution. The full text of the license may be found at
+#  http://opensource.org/licenses/bsd-license.php.
+#
+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = PeiDxeSmmPchSmbusCommonLib
+  FILE_GUID                      = 490CEB45-F3A8-41CD-89CB-C47BF7F7A4B4
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = ScSmbusCommonLib
+
+[Sources]
+  ScSmbusExec.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  BroxtonSiPkg/BroxtonSiPkg.dec
+  BroxtonSiPkg/BroxtonSiPrivate.dec
+
+[LibraryClasses]
+  IoLib
+  DebugLib
+  MmPciLib
diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Library/Private/PeiDxeSmmScSmbusCommonLib/ScSmbusExec.c b/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Library/Private/PeiDxeSmmScSmbusCommonLib/ScSmbusExec.c
new file mode 100644
index 000000000..2c5fe9b7b
--- /dev/null
+++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Library/Private/PeiDxeSmmScSmbusCommonLib/ScSmbusExec.c
@@ -0,0 +1,653 @@
+/** @file
+  Apollo Lake South Cluster Smbus Executive Code (common PEI/DXE/SMM code).
+
+  Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.<BR>
+
+  This program and the accompanying materials
+  are licensed and made available under the terms and conditions of the BSD License
+  which accompanies this distribution.  The full text of the license may be found at
+  http://opensource.org/licenses/bsd-license.php.
+
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <Uefi/UefiBaseType.h>
+#include <Library/IoLib.h>
+#include <Library/DebugLib.h>
+#include <Library/TimerLib.h>
+#include <IndustryStandard/SmBus.h>
+#include <IndustryStandard/Pci30.h>
+#include <ScAccess.h>
+#include <Library/MmPciLib.h>
+#include <Private/Library/ScSmbusCommonLib.h>
+
+/**
+  Get SMBUS IO Base address.
+
+  @param[in]  None
+
+  @retval UINT32                  The SMBUS IO Base Address
+**/
+UINT32
+SmbusGetIoBase (
+  VOID
+  )
+{
+  UINT32  SmbusIoBase;
+
+  SmbusIoBase = MmioRead32 (
+                     MmPciBase (
+                     DEFAULT_PCI_BUS_NUMBER_SC,
+                     PCI_DEVICE_NUMBER_SMBUS,
+                     PCI_FUNCTION_NUMBER_SMBUS)
+                     + R_SMBUS_BASE) & B_SMBUS_BASE_BAR;
+
+  ASSERT (SmbusIoBase != B_SMBUS_BASE_BAR && SmbusIoBase != 0);
+
+  return SmbusIoBase;
+}
+
+/**
+  This function provides a standard way to read PCH Smbus IO registers.
+
+  @param[in] Offset               Register offset from Smbus base IO address.
+
+  @retval UINT8                   Returns data read from IO.
+**/
+UINT8
+EFIAPI
+SmbusIoRead (
+  IN      UINT8           Offset
+  )
+{
+  return IoRead8 (SmbusGetIoBase () + Offset);
+}
+
+/**
+  This function provides a standard way to write PCH Smbus IO registers.
+
+  @param[in] Offset               Register offset from Smbus base IO address.
+  @param[in] Data                 Data to write to register.
+
+**/
+VOID
+EFIAPI
+SmbusIoWrite (
+  IN      UINT8           Offset,
+  IN      UINT8           Data
+  )
+{
+
+  IoWrite8 (SmbusGetIoBase () + Offset, Data);
+  return;
+}
+
+/**
+  This function provides a standard way to check if a SMBus transaction has
+  completed.
+
+  @param[in] StsReg               Not used for input. On return, contains the
+                                  value of the SMBus status register.
+
+  @retval TRUE                    Transaction is complete
+  @retval FALSE                   Otherwise.
+**/
+BOOLEAN
+EFIAPI
+IoDone (
+  IN      UINT8           *StsReg
+  )
+{
+  UINTN StallIndex;
+  UINTN StallTries;
+
+  StallTries = STALL_TIME / STALL_PERIOD;
+
+  for (StallIndex = 0; StallIndex < StallTries; StallIndex++) {
+    *StsReg = SmbusIoRead (R_SMBUS_HSTS);
+    if (*StsReg & (B_SMBUS_INTR | B_SMBUS_BYTE_DONE_STS | B_SMBUS_DERR | B_SMBUS_BERR)) {
+      return TRUE;
+    } else {
+      MicroSecondDelay (STALL_PERIOD);
+    }
+  }
+
+  return FALSE;
+}
+
+/**
+  Check if it's ok to use the bus.
+
+
+  @retval EFI_SUCCESS             SmBus is acquired and it's safe to send commands.
+  @retval EFI_TIMEOUT             SmBus is busy, it's not safe to send commands.
+**/
+EFI_STATUS
+AcquireBus (
+  VOID
+  )
+{
+  UINT8 StsReg;
+
+  StsReg  = 0;
+  StsReg  = SmbusIoRead (R_SMBUS_HSTS);
+  if (StsReg & B_SMBUS_IUS) {
+    return EFI_TIMEOUT;
+  } else if (StsReg & B_SMBUS_HBSY) {
+    ///
+    /// Clear Status Register and exit
+    ///
+    SmbusIoWrite (R_SMBUS_HSTS, B_SMBUS_HSTS_ALL);
+    return EFI_TIMEOUT;
+  } else {
+    ///
+    /// Clear out any odd status information (Will Not Clear In Use)
+    ///
+    SmbusIoWrite (R_SMBUS_HSTS, StsReg);
+    return EFI_SUCCESS;
+  }
+}
+
+/**
+  This function provides a standard way to execute Smbus protocols
+  as defined in the SMBus Specification. The data can either be of
+  the Length byte, word, or a block of data. The resulting transaction will be
+  either the SMBus Slave Device accepts this transaction or this function
+  returns with an error
+
+  @param[in] SlaveAddress         Smbus Slave device the command is directed at
+  @param[in] Command              Slave Device dependent
+  @param[in] Operation            Which SMBus protocol will be used
+  @param[in] PecCheck             Defines if Packet Error Code Checking is to be used
+  @param[in, out] Length          How many bytes to read. Must be 0 <= Length <= 32 depending on Operation
+                                  It will contain the actual number of bytes read/written.
+  @param[in, out] Buffer          Contain the data read/written.
+
+  @retval EFI_SUCCESS             The operation completed successfully.
+  @exception EFI_UNSUPPORTED      The operation is unsupported.
+
+  @retval EFI_INVALID_PARAMETER   Length or Buffer is NULL for any operation besides
+                                  quick read or quick write.
+  @retval EFI_TIMEOUT             The transaction did not complete within an internally
+                                  specified timeout period, or the controller is not
+                                  available for use.
+  @retval EFI_DEVICE_ERROR        There was an Smbus error (NACK) during the operation.
+                                  This could indicate the slave device is not present
+                                  or is in a hung condition.
+**/
+EFI_STATUS
+SmbusExec (
+  IN      EFI_SMBUS_DEVICE_ADDRESS  SlaveAddress,
+  IN      EFI_SMBUS_DEVICE_COMMAND  Command,
+  IN      EFI_SMBUS_OPERATION       Operation,
+  IN      BOOLEAN                   PecCheck,
+  IN OUT  UINTN                     *Length,
+  IN OUT  VOID                      *Buffer
+  )
+{
+  EFI_STATUS  Status;
+  UINT8       AuxcReg;
+  UINT8       AuxStsReg;
+  UINT8       SmbusOperation;
+  UINT8       StsReg;
+  UINT8       SlvAddrReg;
+  UINT8       HostCmdReg;
+  UINT8       BlockCount;
+  BOOLEAN     BufferTooSmall;
+  UINTN       Index;
+  UINTN       BusIndex;
+  UINT8       *CallBuffer;
+  UINT8       SmbusHctl;
+  UINT32      Timeout;
+
+  CallBuffer  = Buffer;
+  BlockCount  = 0;
+
+  ///
+  /// For any operations besides quick read & write, the pointers to
+  /// Length and Buffer must not be NULL.
+  ///
+  if ((Operation != EfiSmbusQuickRead) && (Operation != EfiSmbusQuickWrite)) {
+    if ((Length == NULL) || (Buffer == NULL)) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+  ///
+  /// See if its ok to use the bus based upon INUSE_STS bit.
+  ///
+  Status = AcquireBus ();
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  ///
+  /// This is the main operation loop.  If the operation results in a Smbus
+  /// collision with another master on the bus, it attempts the requested
+  /// transaction again at least BUS_TRIES attempts.
+  ///
+  for (BusIndex = 0; BusIndex < BUS_TRIES; BusIndex++) {
+    ///
+    /// Operation Specifics (pre-execution)
+    ///
+    Status          = EFI_SUCCESS;
+    SmbusOperation  = V_SMBUS_SMB_CMD_QUICK;
+    SlvAddrReg      = (UINT8) ((SlaveAddress.SmbusDeviceAddress << 1) | 1);
+    HostCmdReg      = (UINT8) Command;
+    AuxcReg         = 0;
+
+    switch (Operation) {
+
+    case EfiSmbusQuickWrite:
+      SlvAddrReg--;
+
+    ///
+    /// The "break;" command is not present here to allow code execution
+    /// do drop into the next case, which contains common code to this case.
+    ///
+    case EfiSmbusQuickRead:
+      if (PecCheck == TRUE) {
+        Status = EFI_UNSUPPORTED;
+      }
+      break;
+
+    case EfiSmbusSendByte:
+      HostCmdReg = CallBuffer[0];
+      SlvAddrReg--;
+
+    ///
+    /// The "break;" command is not present here to allow code execution
+    /// do drop into the next case, which contains common code to this case.
+    ///
+    case EfiSmbusReceiveByte:
+      SmbusOperation = V_SMBUS_SMB_CMD_BYTE;
+      if (*Length < 1) {
+        Status = EFI_BUFFER_TOO_SMALL;
+      }
+
+      *Length = 1;
+      break;
+
+    case EfiSmbusWriteByte:
+      SmbusIoWrite (R_SMBUS_HD0, CallBuffer[0]);
+      SlvAddrReg--;
+      *Length = 1;
+
+    ///
+    /// The "break;" command is not present here to allow code execution
+    /// do drop into the next case, which contains common code to this case.
+    ///
+    case EfiSmbusReadByte:
+      if (*Length < 1) {
+        Status = EFI_BUFFER_TOO_SMALL;
+      } else if (*Length == 1) {
+        SmbusOperation = V_SMBUS_SMB_CMD_BYTE_DATA;
+      } else if (*Length <= 256) {
+        if (PecCheck == TRUE) {
+          ///
+          /// The I2C Read command with either PEC_EN or AAC bit set
+          /// produces undefined results.
+          ///
+          Status = EFI_UNSUPPORTED;
+        }
+
+        SmbusOperation = V_SMBUS_SMB_CMD_IIC_READ;
+      } else {
+        Status = EFI_INVALID_PARAMETER;
+      }
+
+      break;
+
+    case EfiSmbusReadWord:
+      SmbusOperation = V_SMBUS_SMB_CMD_WORD_DATA;
+      if (*Length < 2) {
+        Status = EFI_BUFFER_TOO_SMALL;
+      }
+
+      *Length = 2;
+      break;
+
+    case EfiSmbusWriteWord:
+      SmbusOperation = V_SMBUS_SMB_CMD_WORD_DATA;
+      SlvAddrReg--;
+      SmbusIoWrite (R_SMBUS_HD1, CallBuffer[1]);
+      SmbusIoWrite (R_SMBUS_HD0, CallBuffer[0]);
+      if (*Length < 2) {
+        Status = EFI_BUFFER_TOO_SMALL;
+      }
+
+      *Length = 2;
+      break;
+
+    case EfiSmbusWriteBlock:
+      SmbusIoWrite (R_SMBUS_HD0, *(UINT8 *) Length);
+      SlvAddrReg--;
+      BlockCount = (UINT8) (*Length);
+
+    ///
+    /// The "break;" command is not present here to allow code execution
+    /// do drop into the next case, which contains common code to this case.
+    ///
+    case EfiSmbusReadBlock:
+      SmbusOperation = V_SMBUS_SMB_CMD_BLOCK;
+      if ((*Length < 1) || (*Length > 32)) {
+        Status = EFI_INVALID_PARAMETER;
+        break;
+      }
+
+      AuxcReg |= B_SMBUS_E32B;
+      break;
+
+    case EfiSmbusProcessCall:
+      SmbusOperation = V_SMBUS_SMB_CMD_PROCESS_CALL;
+      SmbusIoWrite (R_SMBUS_HD1, CallBuffer[1]);
+      SmbusIoWrite (R_SMBUS_HD0, CallBuffer[0]);
+      if (*Length < 2) {
+        Status = EFI_BUFFER_TOO_SMALL;
+      }
+
+      *Length = 2;
+      break;
+
+    case EfiSmbusBWBRProcessCall:
+      ///
+      /// The write byte count cannot be zero or more than
+      /// 32 bytes.
+      ///
+      if ((*Length < 1) || (*Length > 32)) {
+        Status = EFI_INVALID_PARAMETER;
+        break;
+      }
+
+      SmbusIoWrite (R_SMBUS_HD0, *(UINT8 *) Length);
+      BlockCount      = (UINT8) (*Length);
+      SmbusOperation  = V_SMBUS_SMB_CMD_BLOCK_PROCESS;
+
+      AuxcReg |= B_SMBUS_E32B;
+      break;
+
+    default:
+      Status = EFI_INVALID_PARAMETER;
+      break;
+    }
+
+    if (EFI_ERROR (Status)) {
+      break;
+    }
+
+    if (PecCheck == TRUE) {
+      AuxcReg |= B_SMBUS_AAC;
+    }
+    ///
+    /// Set Auxiliary Control register
+    ///
+    SmbusIoWrite (R_SMBUS_AUXC, AuxcReg);
+
+    ///
+    /// Reset the pointer of the internal buffer
+    ///
+    SmbusIoRead (R_SMBUS_HCTL);
+
+    ///
+    /// Now that the 32 byte buffer is turned on, we can write th block data
+    /// into it
+    ///
+    if ((Operation == EfiSmbusWriteBlock) || (Operation == EfiSmbusBWBRProcessCall)) {
+      for (Index = 0; Index < BlockCount; Index++) {
+        ///
+        /// Write next byte
+        ///
+        SmbusIoWrite (R_SMBUS_HBD, CallBuffer[Index]);
+      }
+    }
+    ///
+    /// Set SMBus slave address for the device to send/receive from
+    ///
+    SmbusIoWrite (R_SMBUS_TSA, SlvAddrReg);
+
+    ///
+    /// For I2C read, send DATA1 register for the offset (address)
+    /// within the serial memory chips
+    ///
+    if ((Operation == EfiSmbusReadByte) && (*Length > 1)) {
+      SmbusIoWrite (R_SMBUS_HD1, HostCmdReg);
+    } else {
+      ///
+      /// Set Command register
+      ///
+      SmbusIoWrite (R_SMBUS_HCMD, HostCmdReg);
+    }
+    ///
+    /// Set Control Register (Initiate Operation, Interrupt disabled)
+    ///
+    SmbusIoWrite (R_SMBUS_HCTL, (UINT8) (SmbusOperation + B_SMBUS_START));
+
+    ///
+    /// Wait for IO to complete
+    ///
+    if (!IoDone (&StsReg)) {
+      Status = EFI_TIMEOUT;
+      break;
+    } else if (StsReg & B_SMBUS_DERR) {
+      AuxStsReg = SmbusIoRead (R_SMBUS_AUXS);
+      if (AuxStsReg & B_SMBUS_CRCE) {
+        Status = EFI_CRC_ERROR;
+      } else {
+        Status = EFI_DEVICE_ERROR;
+      }
+      break;
+    } else if (StsReg & B_SMBUS_BERR) {
+      ///
+      /// Clear the Bus Error for another try
+      ///
+      Status = EFI_DEVICE_ERROR;
+      SmbusIoWrite (R_SMBUS_HSTS, B_SMBUS_BERR);
+      ///
+      /// Clear Status Registers
+      ///
+      SmbusIoWrite (R_SMBUS_HSTS, B_SMBUS_HSTS_ALL);
+      SmbusIoWrite (R_SMBUS_AUXS, B_SMBUS_CRCE);
+      ///
+      /// If bus collision happens, stall some time, then try again
+      /// Here we choose 10 milliseconds to avoid MTCP transfer.
+      ///
+      MicroSecondDelay (STALL_PERIOD);
+      continue;
+    }
+    ///
+    /// successfull completion
+    /// Operation Specifics (post-execution)
+    ///
+    switch (Operation) {
+
+    case EfiSmbusReadWord:
+    ///
+    /// The "break;" command is not present here to allow code execution
+    /// do drop into the next case, which contains common code to this case.
+    ///
+    case EfiSmbusProcessCall:
+      CallBuffer[1] = SmbusIoRead (R_SMBUS_HD1);
+      CallBuffer[0] = SmbusIoRead (R_SMBUS_HD0);
+      break;
+
+    case EfiSmbusReadByte:
+      if (*Length > 1) {
+        for (Index = 0; Index < *Length; Index++) {
+          ///
+          /// Read the byte
+          ///
+          CallBuffer[Index] = SmbusIoRead (R_SMBUS_HBD);
+          ///
+          /// After receiving byte n-1 (1-base) of the message, the
+          /// software will then set the LAST BYTE bit. The software
+          /// will then clear the BYTE_DONE_STS bit.
+          ///
+          if (Index == ((*Length - 1) - 1)) {
+            SmbusHctl = SmbusIoRead (R_SMBUS_HCTL) | (UINT8) B_SMBUS_LAST_BYTE;
+            SmbusIoWrite (R_SMBUS_HCTL, SmbusHctl);
+          } else if (Index == (*Length - 1)) {
+            ///
+            /// Clear the LAST BYTE bit after receiving byte n (1-base) of the message
+            ///
+            SmbusHctl = SmbusIoRead (R_SMBUS_HCTL) & (UINT8) ~B_SMBUS_LAST_BYTE;
+            SmbusIoWrite (R_SMBUS_HCTL, SmbusHctl);
+          }
+          ///
+          /// Clear the BYTE_DONE_STS bit
+          ///
+          SmbusIoWrite (R_SMBUS_HSTS, B_SMBUS_BYTE_DONE_STS);
+          ///
+          /// Check BYTE_DONE_STS bit to know if it has completed transmission
+          /// of a byte. No need to check it for the last byte.
+          ///
+          if (Index < (*Length - 1)) {
+            ///
+            /// If somehow board operates at 10Khz, it will take 0.9 ms (9/10Khz) for another byte.
+            /// Add 10 us delay for a loop of 100 that the total timeout is 1 ms to take care of
+            /// the slowest case.
+            ///
+            for (Timeout = 0; Timeout < 100; Timeout++) {
+              if ((SmbusIoRead (R_SMBUS_HSTS) & (UINT8) B_SMBUS_BYTE_DONE_STS) != 0) {
+                break;
+              }
+              ///
+              /// Delay 10 us
+              ///
+              MicroSecondDelay (STALL_PERIOD);
+            }
+
+            if (Timeout >= 100) {
+              Status = EFI_TIMEOUT;
+              break;
+            }
+          }
+        }
+        break;
+      }
+
+    case EfiSmbusReceiveByte:
+      CallBuffer[0] = SmbusIoRead (R_SMBUS_HD0);
+      break;
+
+    case EfiSmbusWriteBlock:
+      SmbusIoWrite (R_SMBUS_HSTS, B_SMBUS_BYTE_DONE_STS);
+      break;
+
+    case EfiSmbusReadBlock:
+      BufferTooSmall = FALSE;
+      ///
+      /// Find out how many bytes will be in the block
+      ///
+      BlockCount = SmbusIoRead (R_SMBUS_HD0);
+      if (*Length < BlockCount) {
+        BufferTooSmall = TRUE;
+      } else {
+        for (Index = 0; Index < BlockCount; Index++) {
+          ///
+          /// Read the byte
+          ///
+          CallBuffer[Index] = SmbusIoRead (R_SMBUS_HBD);
+        }
+      }
+
+      *Length = BlockCount;
+      if (BufferTooSmall) {
+        Status = EFI_BUFFER_TOO_SMALL;
+      }
+      break;
+
+    case EfiSmbusBWBRProcessCall:
+      ///
+      /// Find out how many bytes will be in the block
+      ///
+      BlockCount = SmbusIoRead (R_SMBUS_HD0);
+      ///
+      /// The read byte count cannot be zero.
+      ///
+      if (BlockCount < 1) {
+        Status = EFI_BUFFER_TOO_SMALL;
+        break;
+      }
+      ///
+      /// The combined data payload (the write byte count + the read byte count)
+      /// must not exceed 32 bytes
+      ///
+      if (((UINT8) (*Length) + BlockCount) > 32) {
+        Status = EFI_DEVICE_ERROR;
+        break;
+      }
+
+      for (Index = 0; Index < BlockCount; Index++) {
+        ///
+        /// Read the byte
+        ///
+        CallBuffer[Index] = SmbusIoRead (R_SMBUS_HBD);
+      }
+
+      *Length = BlockCount;
+      break;
+
+    default:
+      break;
+    };
+
+    if ((StsReg & B_SMBUS_BERR) && (Status != EFI_BUFFER_TOO_SMALL)) {
+      ///
+      /// Clear the Bus Error for another try
+      ///
+      Status = EFI_DEVICE_ERROR;
+      SmbusIoWrite (R_SMBUS_HSTS, B_SMBUS_BERR);
+      ///
+      /// If bus collision happens, stall some time, then try again
+      /// Here we choose 10 milliseconds to avoid MTCP transfer.
+      ///
+      MicroSecondDelay (STALL_PERIOD);
+      continue;
+    } else {
+      break;
+    }
+  }
+  ///
+  /// Clear Status Registers and exit
+  ///
+  SmbusIoWrite (R_SMBUS_HSTS, B_SMBUS_HSTS_ALL);
+  SmbusIoWrite (R_SMBUS_AUXS, B_SMBUS_CRCE);
+  SmbusIoWrite (R_SMBUS_AUXC, 0);
+  return Status;
+}
+
+/**
+  This function initializes the Smbus Registers.
+
+  @param[in]  None
+
+  @retval[in]  None
+**/
+VOID
+InitializeSmbusRegisters (
+  VOID
+  )
+{
+  UINTN SmbusRegBase;
+
+  SmbusRegBase = MmPciBase (
+                  DEFAULT_PCI_BUS_NUMBER_SC,
+                  PCI_DEVICE_NUMBER_SMBUS,
+                  PCI_FUNCTION_NUMBER_SMBUS
+                  );
+  ///
+  /// Enable the Smbus I/O Enable
+  ///
+  MmioOr8 (SmbusRegBase + PCI_COMMAND_OFFSET, (UINT8) EFI_PCI_COMMAND_IO_SPACE);
+
+  ///
+  /// Enable the Smbus host controller
+  ///
+  MmioAndThenOr8 (
+    SmbusRegBase + R_SMBUS_HOSTC,
+    (UINT8) (~(B_SMBUS_HOSTC_SMI_EN | B_SMBUS_HOSTC_I2C_EN)),
+    B_SMBUS_HOSTC_HST_EN
+    );
+
+  SmbusIoWrite (R_SMBUS_HSTS, B_SMBUS_HSTS_ALL);
+}
\ No newline at end of file
-- 
2.14.1.windows.1

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel