/** @file
I2C Debug Port Protocol Implementation
Copyright (c) 2022, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include
#include
/**
Writes data to the I2C debug port
@param[in] Buffer - The data to be written to the I2C debug port.
@param[in] Count - The number of bytes to write to the I2C debug port.
@retval EFI_SUCCESS - The data was successfully written.
@retval EFI_INVALID_PARAMETER - One of the following conditions:
* ByteCount is 0
* Buffer is NULL
@retval EFI_TIMEOUT - The I2C controller did not respond within the required timeout period.
@retval EFI_DEVICE_ERROR - An error occurred while accessing the I2C controller.
**/
EFI_STATUS
I2cDebugPortWrite (
IN UINT8 *Buffer,
IN UINT32 Count
)
{
UINT8 WriteBuffer[I2C_DEBUG_PORT_MAX_DATA_SIZE + 1];
EFI_STATUS Status;
UINT32 Index;
UINT32 ImplementationDelayUs;
UINT8 CurrentSize;
UINT8 DdcBusPinPair;
if (Count <= 0) {
return EFI_INVALID_PARAMETER;
}
if (Buffer == NULL) {
return EFI_INVALID_PARAMETER;
}
Status = GetGmbusBusPinPairForI2cDebugPort (&DdcBusPinPair);
if (EFI_ERROR (Status)) {
return Status;
}
ImplementationDelayUs = FixedPcdGet32 (PcdI2cHdmiDebugPortPacketStallUs); //BP: 3ms stall to catch up
RaiseTplForI2cDebugPortAccess ();
for (Index = 0; Index < Count; Index += I2C_DEBUG_PORT_MAX_DATA_SIZE) {
MicroSecondDelay (ImplementationDelayUs);
if ((Index + I2C_DEBUG_PORT_MAX_DATA_SIZE) >= Count) {
CurrentSize = (UINT8) (Count - Index);
} else {
CurrentSize = I2C_DEBUG_PORT_MAX_DATA_SIZE;
}
WriteBuffer[0] = (I2C_DEBUG_PORT_WRITE_COMMAND << I2C_DEBUG_PORT_COMMAND_BIT_POSITION) |
(CurrentSize & I2C_DEBUG_PORT_DATA_SIZE_BIT_MASK);
CopyMem (&(WriteBuffer[1]), &(Buffer[Index]), CurrentSize);
Status = GmbusWrite (DdcBusPinPair, I2C_DEBUG_PORT_WRITE_DEVICE_ADDRESS, TRUE, CurrentSize + 1, &(WriteBuffer[0]));
if (EFI_ERROR (Status)) {
break;
}
}
RestoreTplAfterI2cDebugPortAccess ();
return Status;
}
/**
Reads data from the I2C debug port
@param[out] Buffer - The memory buffer to return the read data.
@param[in, out] Count - The number of bytes to read from the I2C debug port.
On output, the number of bytes actually read.
@retval EFI_SUCCESS - The data was successfully read.
@retval EFI_INVALID_PARAMETER - One of the following conditions:
* ByteCount is 0
* Buffer is NULL
@retval EFI_TIMEOUT - The I2C controller did not respond within the required timeout period.
@retval EFI_DEVICE_ERROR - An error occurred while accessing the I2C controller.
**/
EFI_STATUS
I2cDebugPortRead (
OUT UINT8 *Buffer,
IN OUT UINT32 *Count
)
{
EFI_STATUS Status;
UINT32 Index;
UINT32 BytesRead;
UINT32 ImplementationDelayUs;
UINT32 CurrentSize;
UINT8 DdcBusPinPair;
UINT8 GmbusIndexData;
BytesRead = 0;
if ((*Count) <= 0) {
return EFI_INVALID_PARAMETER;
}
if (Buffer == NULL) {
return EFI_INVALID_PARAMETER;
}
Status = GetGmbusBusPinPairForI2cDebugPort (&DdcBusPinPair);
if (EFI_ERROR (Status)) {
return Status;
}
ImplementationDelayUs = FixedPcdGet32 (PcdI2cHdmiDebugPortPacketStallUs); //BP: 3ms stall to catch up
RaiseTplForI2cDebugPortAccess ();
for (Index = 0; Index < (*Count); Index += I2C_DEBUG_PORT_MAX_DATA_SIZE) {
MicroSecondDelay (ImplementationDelayUs);
if ((Index + I2C_DEBUG_PORT_MAX_DATA_SIZE) >= (*Count)) {
CurrentSize = (*Count) - Index;
} else {
CurrentSize = I2C_DEBUG_PORT_MAX_DATA_SIZE;
}
GmbusIndexData = (I2C_DEBUG_PORT_READ_COMMAND << I2C_DEBUG_PORT_COMMAND_BIT_POSITION) |
(CurrentSize & I2C_DEBUG_PORT_DATA_SIZE_BIT_MASK);
Status = GmbusRead (
DdcBusPinPair,
I2C_DEBUG_PORT_READ_DEVICE_ADDRESS,
TRUE,
TRUE,
GmbusIndexData,
&CurrentSize,
&(Buffer[Index])
);
if (EFI_ERROR (Status)) {
break;
}
BytesRead += CurrentSize;
if (((Index + I2C_DEBUG_PORT_MAX_DATA_SIZE) < (*Count)) && (CurrentSize < I2C_DEBUG_PORT_MAX_DATA_SIZE)) {
break;
}
}
RestoreTplAfterI2cDebugPortAccess ();
(*Count) = BytesRead;
return Status;
}
/**
Queries the I2C debug port to see if there are any data waiting to be read
@param[out] NumberOfBytesInFifoBuffer - The number of bytes sitting in the I2C debug port's
FIFO buffer waiting to be read
@retval EFI_SUCCESS - The I2C debug port was successfully queried
@retval EFI_INVALID_PARAMETER - One of the following conditions:
* NumberOfBytesInFifoBuffer is NULL
@retval EFI_TIMEOUT - The I2C controller did not respond within the required timeout period.
@retval EFI_DEVICE_ERROR - An error occurred while accessing the I2C controller.
**/
EFI_STATUS
I2cDebugPortReadyToRead (
OUT UINT8 *NumberOfBytesInFifoBuffer
)
{
EFI_STATUS Status;
UINT32 BytesRead;
UINT8 DdcBusPinPair;
UINT32 ImplementationDelayUs;
UINT8 GmbusIndexData;
BytesRead = 1;
if (NumberOfBytesInFifoBuffer == NULL) {
return EFI_INVALID_PARAMETER;
}
Status = GetGmbusBusPinPairForI2cDebugPort (&DdcBusPinPair);
if (EFI_ERROR (Status)) {
return Status;
}
ImplementationDelayUs = FixedPcdGet32 (PcdI2cHdmiDebugPortPacketStallUs); //BP: 3ms stall to catch up
MicroSecondDelay (ImplementationDelayUs);
GmbusIndexData = (I2C_DEBUG_PORT_READY_TO_READ_COMMAND << I2C_DEBUG_PORT_COMMAND_BIT_POSITION) |
(1 & I2C_DEBUG_PORT_DATA_SIZE_BIT_MASK); //READY_TO_READ always returns 1 byte
RaiseTplForI2cDebugPortAccess ();
Status = GmbusRead (DdcBusPinPair, I2C_DEBUG_PORT_READ_DEVICE_ADDRESS, TRUE, TRUE, GmbusIndexData, &BytesRead, NumberOfBytesInFifoBuffer);
if (EFI_ERROR (Status)) {
return Status;
}
RestoreTplAfterI2cDebugPortAccess ();
if (BytesRead != 1) {
Status = EFI_DEVICE_ERROR;
}
return Status;
}