This adds the SmbusRead() function designed for SMBUS transaction to
support the extraction of the data lenth byte from the initial byte of
the SMBUS Block Read, allowing the I2C master to accurately read the
SMBUS response with the exact data length. This addresses the issue
where the SmbusLib:SmBusReadBlock() function consistently reads a
32-byte block of data.

Signed-off-by: Nhi Pham <n...@os.amperecomputing.com>
---
 Silicon/Ampere/AmpereAltraPkg/Include/Library/I2cLib.h    |  31 +++++
 Silicon/Ampere/AmpereAltraPkg/Library/DwI2cLib/DwI2cLib.c | 137 
+++++++++++++++++++-
 2 files changed, 163 insertions(+), 5 deletions(-)

diff --git a/Silicon/Ampere/AmpereAltraPkg/Include/Library/I2cLib.h 
b/Silicon/Ampere/AmpereAltraPkg/Include/Library/I2cLib.h
index f13794171029..d460f49efccb 100644
--- a/Silicon/Ampere/AmpereAltraPkg/Include/Library/I2cLib.h
+++ b/Silicon/Ampere/AmpereAltraPkg/Include/Library/I2cLib.h
@@ -65,6 +65,37 @@ I2cRead (
   IN OUT UINT32 *ReadLength
   );
 
+/**
+  SMBUS block read.
+
+  @param[in]     Bus          I2C bus Id.
+  @param[in]     SlaveAddr    The address of slave device on the bus.
+  @param[in]     BufCmd       Buffer where to send the command.
+  @param[in]     CmdLength    Length of BufCmd.
+  @param[in,out] Buf          Buffer where to put the read data to.
+  @param[in,out] ReadLength   Pointer to length of buffer.
+  @param[in]     PecCheck     If Packet Error Code (PEC) checking is required 
for this operation.
+
+  @retval EFI_SUCCESS            Read successfully.
+  @retval EFI_INVALID_PARAMETER  A parameter is invalid.
+  @retval EFI_UNSUPPORTED        The bus is not supported.
+  @retval EFI_NOT_READY          The device/bus is not ready.
+  @retval EFI_TIMEOUT            Timeout when transferring data.
+  @retval EFI_CRC_ERROR          There are errors on receiving data.
+
+**/
+EFI_STATUS
+EFIAPI
+SmbusRead (
+  IN     UINT32  Bus,
+  IN     UINT32  SlaveAddr,
+  IN     UINT8   *BufCmd,
+  IN     UINT32  CmdLength,
+  IN OUT UINT8   *Buf,
+  IN OUT UINT32  *ReadLength,
+  IN     BOOLEAN PecCheck
+  );
+
 /**
  Setup new transaction with I2C slave device.
 
diff --git a/Silicon/Ampere/AmpereAltraPkg/Library/DwI2cLib/DwI2cLib.c 
b/Silicon/Ampere/AmpereAltraPkg/Library/DwI2cLib/DwI2cLib.c
index 669ba2ea98a4..9e52ae69e7cd 100644
--- a/Silicon/Ampere/AmpereAltraPkg/Library/DwI2cLib/DwI2cLib.c
+++ b/Silicon/Ampere/AmpereAltraPkg/Library/DwI2cLib/DwI2cLib.c
@@ -337,6 +337,11 @@ I2cWaitTxData (
       DEBUG ((DEBUG_ERROR, "%a: Timeout waiting for TX buffer available\n", 
__FUNCTION__));
       return EFI_TIMEOUT;
     }
+
+    if ((I2cCheckErrors (Bus) & DW_IC_INTR_TX_ABRT) != 0) {
+      return EFI_ABORTED;
+    }
+
     MicroSecondDelay (mI2cBusList[Bus].PollingTime);
   }
 
@@ -542,13 +547,61 @@ InternalI2cWrite (
   return Status;
 }
 
+EFI_STATUS
+InternalSmbusReadDataLength (
+  UINT32  Bus,
+  UINT32 *Length
+  )
+{
+  EFI_STATUS Status;
+  UINTN      Base;
+  UINT32     CmdSend;
+
+  Base = mI2cBusList[Bus].Base;
+
+  CmdSend = DW_IC_DATA_CMD_CMD;
+  MmioWrite32 (Base + DW_IC_DATA_CMD, CmdSend);
+  I2cSync ();
+
+  if (I2cCheckErrors (Bus) != 0) {
+    DEBUG ((DEBUG_ERROR, "%a: Sending reading command error\n", __func__));
+    return EFI_CRC_ERROR;
+  }
+
+  Status = I2cWaitRxData (Bus);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Reading Smbus data length failed to wait data\n",
+      __func__
+      ));
+
+    if (Status != EFI_ABORTED) {
+      MmioWrite32 (Base + DW_IC_DATA_CMD, DW_IC_DATA_CMD_STOP);
+      I2cSync ();
+    }
+
+    return Status;
+  }
+
+  *Length = MmioRead32 (Base + DW_IC_DATA_CMD) & DW_IC_DATA_CMD_DAT_MASK;
+  I2cSync ();
+
+  if (I2cCheckErrors (Bus) != 0) {
+    DEBUG ((DEBUG_ERROR, "%a: Sending reading command error\n", __func__));
+    return EFI_CRC_ERROR;
+  }
+  return EFI_SUCCESS;
+}
+
 EFI_STATUS
 InternalI2cRead (
   UINT32  Bus,
-  UINT8  *BufCmd,
-  UINT32 CmdLength,
-  UINT8  *Buf,
-  UINT32 *Length
+  UINT8   *BufCmd,
+  UINT32  CmdLength,
+  UINT8   *Buf,
+  UINT32  *Length,
+  BOOLEAN IsSmbus,
+  BOOLEAN PecCheck
   )
 {
   EFI_STATUS Status;
@@ -559,6 +612,7 @@ InternalI2cRead (
   UINTN      Count;
   UINTN      ReadCount;
   UINTN      WriteCount;
+  UINT32     ResponseLen;
 
   Status = EFI_SUCCESS;
   Base = mI2cBusList[Bus].Base;
@@ -601,6 +655,36 @@ InternalI2cRead (
   }
 
   WriteCount = 0;
+  if (IsSmbus) {
+    //
+    // Read Smbus Data Length, first byte of the Smbus response data.
+    //
+    Status = InternalSmbusReadDataLength (Bus, &ResponseLen);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a: InternalSmbusReadDataLength failed\n", 
__func__));
+      goto Exit;
+    }
+
+    WriteCount++;
+    Buf[ReadCount++] = ResponseLen;
+
+    //
+    // Abort the transaction when the requested length is shorter than the 
actual response data
+    // or if there is no response data when PEC disabled.
+    //
+    if ((*Length < (ResponseLen + 2)) || (!PecCheck && ResponseLen == 0)) {
+      MmioWrite32 (Base + DW_IC_DATA_CMD, DW_IC_DATA_CMD_CMD | 
DW_IC_DATA_CMD_STOP);
+      I2cSync ();
+      Status = EFI_INVALID_PARAMETER;
+      goto Exit;
+    }
+
+    *Length = ResponseLen + 1; // Response Data Length + 8-bit Byte Count field
+    if (PecCheck) {
+      *Length += 1; // ++ 8-bit PEC field
+    }
+  }
+
   while ((*Length - ReadCount) != 0) {
     TxLimit = mI2cBusList[Bus].TxFifo - MmioRead32 (Base + DW_IC_TXFLR);
     RxLimit = mI2cBusList[Bus].RxFifo - MmioRead32 (Base + DW_IC_RXFLR);
@@ -742,7 +826,50 @@ I2cRead (
 
   I2cSetSlaveAddr (Bus, SlaveAddr);
 
-  return InternalI2cRead (Bus, BufCmd, CmdLength, Buf, ReadLength);
+  return InternalI2cRead (Bus, BufCmd, CmdLength, Buf, ReadLength, FALSE, 
FALSE);
+}
+
+/**
+  SMBUS block read.
+
+  @param[in]     Bus          I2C bus Id.
+  @param[in]     SlaveAddr    The address of slave device on the bus.
+  @param[in]     BufCmd       Buffer where to send the command.
+  @param[in]     CmdLength    Length of BufCmd.
+  @param[in,out] Buf          Buffer where to put the read data to.
+  @param[in,out] ReadLength   Pointer to length of buffer.
+  @param[in]     PecCheck     If Packet Error Code (PEC) checking is required 
for this operation.
+
+  @retval EFI_SUCCESS            Read successfully.
+  @retval EFI_INVALID_PARAMETER  A parameter is invalid.
+  @retval EFI_UNSUPPORTED        The bus is not supported.
+  @retval EFI_NOT_READY          The device/bus is not ready.
+  @retval EFI_TIMEOUT            Timeout when transferring data.
+  @retval EFI_CRC_ERROR          There are errors on receiving data.
+
+**/
+EFI_STATUS
+EFIAPI
+SmbusRead (
+  IN     UINT32  Bus,
+  IN     UINT32  SlaveAddr,
+  IN     UINT8   *BufCmd,
+  IN     UINT32  CmdLength,
+  IN OUT UINT8   *Buf,
+  IN OUT UINT32  *ReadLength,
+  IN     BOOLEAN PecCheck
+  )
+{
+  if (Bus >= AC01_I2C_MAX_BUS_NUM
+      || Buf == NULL
+      || ReadLength == NULL)
+  {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  I2cSetSlaveAddr (Bus, SlaveAddr);
+
+  return InternalI2cRead (Bus, BufCmd, CmdLength, Buf, ReadLength, TRUE, 
PecCheck);
 }
 
 /**
-- 
2.25.1



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#120163): https://edk2.groups.io/g/devel/message/120163
Mute This Topic: https://groups.io/mt/107662232/21656
Group Owner: devel+ow...@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com]
-=-=-=-=-=-=-=-=-=-=-=-


Reply via email to