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] -=-=-=-=-=-=-=-=-=-=-=-