123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760 |
- /** @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;
- 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;
- }
|