/** @file
GMBUS I/O Implementation
Copyright (c) 2022, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include
#include
/**
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;
UINT8 GmbusClockRate;
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
//
GmbusClockRate = FixedPcdGet8 (PcdI2cHdmiDebugPortGmbusClockRate);
Status = GmbusPrepare (GmbusClockRate, (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;
UINT8 GmbusClockRate;
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
//
GmbusClockRate = FixedPcdGet8 (PcdI2cHdmiDebugPortGmbusClockRate);
Status = GmbusPrepare (GmbusClockRate, (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;
}