1018 lines
24 KiB
C
1018 lines
24 KiB
C
/** @file
|
|
For I2C Master
|
|
|
|
;******************************************************************************
|
|
;* Copyright (c) 2012 - 2020, Insyde Software Corp. All Rights Reserved.
|
|
;*
|
|
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
|
|
;* transmit, broadcast, present, recite, release, license or otherwise exploit
|
|
;* any part of this publication in any form, by any means, without the prior
|
|
;* written permission of Insyde Software Corporation.
|
|
;*
|
|
;******************************************************************************
|
|
*/
|
|
|
|
|
|
#include "I2cMasterDxe.h"
|
|
|
|
UINT64 mRdscFreq = 0;
|
|
|
|
UINT8
|
|
I2cHcRead8 (
|
|
IN I2C_BUS_INSTANCE *I2cPort,
|
|
IN UINT16 Offset
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function provides a standard way to read ICH Smbus IO registers.
|
|
|
|
Arguments:
|
|
|
|
I2cPort: Pointer to I2cPort data structure.
|
|
Offset: Register offset from Smbus base IO address.
|
|
|
|
Returns:
|
|
|
|
Returns data read from IO.
|
|
|
|
--*/
|
|
{
|
|
return MmioRead8((UINTN)I2cPort->I2cBase + Offset);
|
|
}
|
|
|
|
UINT16
|
|
I2cHcRead16 (
|
|
IN I2C_BUS_INSTANCE *I2cPort,
|
|
IN UINT16 Offset
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function provides a standard way to read ICH Smbus IO registers.
|
|
|
|
Arguments:
|
|
|
|
I2cPort: Pointer to private data structure.
|
|
Offset: Register offset from Smbus base IO address.
|
|
|
|
Returns:
|
|
|
|
Returns data read from IO.
|
|
|
|
--*/
|
|
{
|
|
return MmioRead16((UINTN)I2cPort->I2cBase + Offset);
|
|
}
|
|
|
|
UINT32
|
|
I2cHcRead32 (
|
|
IN I2C_BUS_INSTANCE *I2cPort,
|
|
IN UINT16 Offset
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function provides a standard way to read ICH Smbus IO registers.
|
|
|
|
Arguments:
|
|
|
|
I2cPort: Pointer to private data structure.
|
|
Offset: Register offset from Smbus base IO address.
|
|
|
|
Returns:
|
|
|
|
Returns data read from IO.
|
|
|
|
--*/
|
|
{
|
|
return MmioRead32((UINTN)I2cPort->I2cBase + Offset);
|
|
}
|
|
|
|
VOID
|
|
I2cHcWrite8 (
|
|
IN I2C_BUS_INSTANCE *I2cPort,
|
|
IN UINT16 Offset,
|
|
IN UINT8 Data
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function provides a standard way to write ICH Smbus IO registers.
|
|
|
|
Arguments:
|
|
|
|
I2cPort: Pointer to private data structure.
|
|
Offset: Register offset from Smbus base IO address.
|
|
Data: Data to write to register.
|
|
|
|
Returns:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Write New Value
|
|
//
|
|
MmioWrite8 ((UINTN)I2cPort->I2cBase + Offset, Data);
|
|
}
|
|
|
|
VOID
|
|
I2cHcWrite16 (
|
|
IN I2C_BUS_INSTANCE *I2cPort,
|
|
IN UINT16 Offset,
|
|
IN UINT16 Data
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function provides a standard way to write ICH Smbus IO registers.
|
|
|
|
Arguments:
|
|
|
|
I2cPort: Pointer to private data structure.
|
|
Offset: Register offset from Smbus base IO address.
|
|
Data: Data to write to register.
|
|
|
|
Returns:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Write New Value
|
|
//
|
|
MmioWrite16 ((UINTN)I2cPort->I2cBase + Offset, Data);
|
|
}
|
|
|
|
VOID
|
|
I2cHcWrite32 (
|
|
IN I2C_BUS_INSTANCE *I2cPort,
|
|
IN UINT16 Offset,
|
|
IN UINT32 Data
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function provides a standard way to write ICH Smbus IO registers.
|
|
|
|
Arguments:
|
|
|
|
I2cPort: Pointer to private data structure.
|
|
Offset: Register offset from Smbus base IO address.
|
|
Data: Data to write to register.
|
|
|
|
Returns:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Write New Value
|
|
//
|
|
MmioWrite32 ((UINTN)I2cPort->I2cBase + Offset, Data);
|
|
}
|
|
|
|
EFI_STATUS
|
|
I2cEnable(
|
|
IN I2C_BUS_INSTANCE *I2cPort
|
|
)
|
|
{
|
|
UINT32 NumTries;
|
|
|
|
NumTries = 10000;
|
|
|
|
I2cPort->I2cHcWrite16(I2cPort, R_I2C_ENABLE, 1);
|
|
|
|
while ( 0 == ( I2cPort->I2cHcRead16 (I2cPort, R_I2C_ENABLE_STATUS) & 1 )) {
|
|
gBS->Stall(10);
|
|
NumTries --;
|
|
if (0 == NumTries) {
|
|
return RETURN_NOT_READY;
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
I2cDisable (
|
|
IN I2C_BUS_INSTANCE *I2cPort
|
|
)
|
|
{
|
|
UINT32 NumTries;
|
|
|
|
NumTries = 10000;
|
|
|
|
I2cPort->I2cHcWrite16(I2cPort, R_I2C_ENABLE, 0);
|
|
|
|
while ( 0 != ( I2cPort->I2cHcRead16 ( I2cPort, R_I2C_ENABLE_STATUS ) & 1 )) {
|
|
gBS->Stall(10);
|
|
NumTries --;
|
|
if (0 == NumTries) {
|
|
return RETURN_NOT_READY;
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
BOOLEAN
|
|
I2cHardwareActive (
|
|
IN I2C_BUS_INSTANCE *I2cPort
|
|
)
|
|
{
|
|
if (I2cPort->I2cHcRead32(I2cPort, R_IC_STATUS) & STAT_MST_ACTIVITY) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
EFI_STATUS
|
|
TxAbortChk(
|
|
IN I2C_BUS_INSTANCE *I2cPort
|
|
)
|
|
{
|
|
UINT16 RawIntrValue;
|
|
|
|
RawIntrValue = I2cPort->I2cHcRead16(I2cPort, R_I2C_RAW_INTR_STAT);
|
|
if ((RawIntrValue & B_I2C_INTR_TX_ABRT) == B_I2C_INTR_TX_ABRT){
|
|
|
|
I2cPort->I2cHcRead16(I2cPort, R_I2C_CLR_TX_ABRT);
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
CalibrateTscFrequency (
|
|
OUT UINT64 *Frequency
|
|
)
|
|
{
|
|
UINT64 BeginValue;
|
|
UINT64 EndValue;
|
|
|
|
if (Frequency == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
BeginValue = AsmReadTsc ();
|
|
gBS->Stall (100);
|
|
EndValue = AsmReadTsc ();
|
|
|
|
*Frequency = MultU64x32(EndValue - BeginValue, 10000);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Reset the I2C controller and configure it for use
|
|
|
|
The controller's I2C bus frequency is set to 100 KHz.
|
|
|
|
@param[in] This I2C bus I2cPort info
|
|
@param[in] Slave Slave Address
|
|
|
|
**/
|
|
VOID
|
|
I2cReset (
|
|
IN I2C_BUS_INSTANCE *I2cPort
|
|
)
|
|
{
|
|
UINT8 Index;
|
|
UINT8 SpeedIndex;
|
|
EFI_STATUS Status;
|
|
UINT16 RawIntrValue = 0x0;
|
|
|
|
if (mRdscFreq == 0) {
|
|
CalibrateTscFrequency(&mRdscFreq);
|
|
}
|
|
|
|
if (I2cPort->Info.Init){
|
|
I2cPort->Info.Init(I2cPort);
|
|
}
|
|
|
|
Status = I2cDisable ( I2cPort );
|
|
|
|
SpeedIndex = (UINT8)((I2cPort->I2cBusSpeed>>1) - 1);
|
|
|
|
for (Index = 0; Index < 3; Index++) {
|
|
I2cPort->I2cHcWrite16(I2cPort, 0x14 + (Index * 8), I2cPort->Info.SpeedClockInfo[Index].SclHcnt);
|
|
I2cPort->I2cHcWrite16(I2cPort, 0x18 + (Index * 8), I2cPort->Info.SpeedClockInfo[Index].SclLcnt);
|
|
}
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
// How to get value without calculate HCNT / LCNT:
|
|
// 1. boot to os and install i2c driver if controller avaliable.
|
|
// 2. find i2c mmio base.
|
|
// 3. Each chipset platform may has certain IC_SDA_HOLD Register offset,
|
|
// look it up from spec and set it to SHR
|
|
// IC_SDA_HOLD.IC_SDA_RX_HOLD
|
|
// IC_SDA_HOLD.IC_SDA_TX_HOLD
|
|
|
|
//
|
|
// IC_SDA_HOLD.IC_SDA_RX_HOLD &
|
|
// IC_SDA_HOLD.IC_SDA_TX_HOLD
|
|
//
|
|
I2cPort->I2cHcWrite32(I2cPort, I2cPort->Info.SdaHoldTimeLen, (I2cPort->Info.SpeedClockInfo[SpeedIndex].SdaHold << 16 | I2cPort->Info.SpeedClockInfo[SpeedIndex].SdaHold));
|
|
|
|
|
|
|
|
Status = I2cEnable ( I2cPort );
|
|
|
|
//
|
|
// The I2C flushes/resets/empties the TX FIFO whenever this bit is set.
|
|
// The TX FIFO remains in this flushed state until the register ic_clr_tx_abrt is read.
|
|
//
|
|
RawIntrValue = I2cPort->I2cHcRead16(I2cPort, R_I2C_RAW_INTR_STAT);
|
|
if ((RawIntrValue & B_I2C_INTR_TX_ABRT) == B_I2C_INTR_TX_ABRT){
|
|
I2cPort->I2cHcRead16(I2cPort, R_I2C_CLR_TX_ABRT);
|
|
}
|
|
}
|
|
|
|
RETURN_STATUS
|
|
I2cStartRequest (
|
|
IN I2C_BUS_INSTANCE *I2cPort,
|
|
IN UINTN SlaveAddress,
|
|
IN UINTN *WriteBytes,
|
|
IN UINT8 *WriteBuffer,
|
|
IN UINTN *ReadBytes,
|
|
OUT UINT8 *ReadBuffer,
|
|
IN BOOLEAN P
|
|
)
|
|
{
|
|
UINT8 *ReceiveDataEnd;
|
|
UINT8 *ReceiveRequest;
|
|
UINT8 *TransmitEnd;
|
|
UINT16 ReceiveData;
|
|
UINT16 RawIntrStat;
|
|
UINT32 I2cStatus;
|
|
UINT32 NumTries;
|
|
UINT32 Eflags;
|
|
UINT64 RdTstStart;
|
|
UINT64 RdTscEnd;
|
|
UINT64 RdTscInterval;
|
|
EFI_STATUS Status;
|
|
CONST UINT32 Timeout = 1000;
|
|
EFI_TPL OriginalTpl;
|
|
|
|
OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
|
|
|
|
RawIntrStat = 0;
|
|
NumTries = 0;
|
|
ReceiveRequest = NULL;
|
|
|
|
//
|
|
// Verify the parameters
|
|
//
|
|
Status = RETURN_SUCCESS;
|
|
|
|
if ( 1023 < SlaveAddress ) {
|
|
Status = RETURN_INVALID_PARAMETER;
|
|
goto ExitI2cStartRequest;
|
|
}
|
|
|
|
NumTries = 100 * 1000;
|
|
|
|
while ( I2cHardwareActive ( I2cPort )) {
|
|
gBS->Stall(10);
|
|
NumTries --;
|
|
if(0 == NumTries) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto ExitI2cStartRequest;
|
|
}
|
|
}
|
|
|
|
Status = I2cDisable ( I2cPort );
|
|
|
|
I2cPort->I2cHcWrite16 ( I2cPort, I2cPort->Info.CRG, 1);
|
|
I2cPort->I2cHcWrite16 ( I2cPort, R_IC_INTR_MASK, 0x0);
|
|
I2cPort->I2cHcWrite16 ( I2cPort, R_IC_TAR, (UINT16) SlaveAddress );
|
|
I2cPort->I2cHcWrite16 ( I2cPort, R_I2C_RX_TL, 0);
|
|
I2cPort->I2cHcWrite16 ( I2cPort, R_I2C_TX_TL, 0 );
|
|
|
|
//Setup clock frequency and speed mode
|
|
//enable master FSM, disable slave FSM
|
|
I2cPort->I2cHcWrite32( I2cPort, R_IC_CON, B_IC_RESTART_EN | B_IC_SLAVE_DISABLE | B_MASTER_MODE | I2cPort->I2cBusSpeed);
|
|
|
|
Status = I2cEnable(I2cPort);
|
|
|
|
if( EFI_ERROR(Status) ) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto ExitI2cStartRequest;
|
|
}
|
|
|
|
I2cPort->I2cHcRead16 ( I2cPort, R_I2C_CLR_TX_ABRT);
|
|
|
|
ReceiveDataEnd = &ReadBuffer [ *ReadBytes ];
|
|
TransmitEnd = &WriteBuffer [ *WriteBytes ];
|
|
RdTstStart = AsmReadTsc();
|
|
RdTscEnd = RdTstStart + MultU64x32(DivU64x32Remainder(mRdscFreq, 1000, NULL), Timeout) ;
|
|
|
|
if (RdTscEnd < RdTstStart) {
|
|
DEBUG((DEBUG_ERROR, "Rdtsc overflow\n"));
|
|
Status = RETURN_INVALID_PARAMETER;
|
|
goto ExitI2cStartRequest;
|
|
}
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
if (WriteBytes) {
|
|
|
|
LocalIrqSave(&Eflags);
|
|
|
|
*WriteBytes = 0;
|
|
while ( TransmitEnd > WriteBuffer ) {
|
|
|
|
I2cStatus = I2cPort->I2cHcRead16 ( I2cPort, R_IC_STATUS);
|
|
|
|
Status = TxAbortChk(I2cPort);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
break;
|
|
}
|
|
|
|
if ( AsmReadTsc() > RdTscEnd ) {
|
|
// this shouldn't happen , the timeout value should be enough to transfer all the bytes.
|
|
// if timeout value isn't enough, do the following workaround
|
|
RdTscEnd += 1000000;
|
|
while( (0 == (I2cPort->I2cHcRead16 ( I2cPort, R_IC_STATUS ) & STAT_TFNF) ) &&
|
|
( AsmReadTsc() < RdTscEnd ));
|
|
|
|
if( I2cPort->I2cHcRead16 ( I2cPort, R_IC_STATUS ) & STAT_TFNF ) {
|
|
I2cPort->I2cHcWrite16 ( I2cPort, R_IC_DATA_CMD, *WriteBuffer | B_STOP_CMD );
|
|
gBS->Stall(2000); //finish the data transmission in fifo
|
|
} else {
|
|
}
|
|
Status = EFI_TIMEOUT;
|
|
break;
|
|
}
|
|
if ( 0 == ( I2cStatus & STAT_TFNF )) {
|
|
gBS->Stall(2); //finish the data transmission in fifo
|
|
continue;
|
|
}
|
|
|
|
if( (TransmitEnd == (WriteBuffer + 1)) && !P) {
|
|
I2cPort->I2cHcWrite16 ( I2cPort, R_IC_DATA_CMD, *WriteBuffer | B_STOP_CMD);
|
|
WriteBuffer ++;
|
|
} else {
|
|
I2cPort->I2cHcWrite16 ( I2cPort, R_IC_DATA_CMD, *WriteBuffer++ );
|
|
}
|
|
(*WriteBytes)++;
|
|
// Add a small delay to work around some odd behavior being seen. Without
|
|
// this delay bytes get dropped.
|
|
gBS->Stall (I2cPort->IntervalTime);
|
|
}
|
|
|
|
LocalIrqRestore(Eflags);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
goto ExitI2cStartRequest;
|
|
}
|
|
|
|
//Wait for bytes to go
|
|
//Write into Tx fifo doesn't mean the dat will go correctly on the SDA data line
|
|
while(1) {
|
|
|
|
Status = TxAbortChk(I2cPort);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
goto ExitI2cStartRequest;
|
|
}
|
|
|
|
if( 0 == I2cPort->I2cHcRead16(I2cPort, R_IC_TXFLR)){
|
|
break;
|
|
}
|
|
|
|
if( AsmReadTsc() > RdTscEnd ) {
|
|
Status = EFI_TIMEOUT;
|
|
goto ExitI2cStartRequest;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(EFI_ERROR(Status))
|
|
goto ExitI2cStartRequest;
|
|
|
|
|
|
if ( ReadBytes) {
|
|
*ReadBytes = 0;
|
|
ReceiveRequest = ReadBuffer;
|
|
LocalIrqSave(&Eflags);
|
|
|
|
while ( (ReceiveDataEnd > ReceiveRequest) || (ReceiveDataEnd > ReadBuffer)) {
|
|
|
|
// Check for NACK
|
|
Status = TxAbortChk(I2cPort);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
break;
|
|
}
|
|
|
|
// Determine if another byte was received
|
|
I2cStatus = I2cPort->I2cHcRead16 ( I2cPort, R_IC_STATUS );
|
|
if ( 0 != ( I2cStatus & STAT_RFNE )) {
|
|
ReceiveData = I2cPort->I2cHcRead16 ( I2cPort, R_IC_DATA_CMD );
|
|
|
|
*ReadBuffer++ = (UINT8)ReceiveData;
|
|
(*ReadBytes)++;
|
|
}
|
|
|
|
if ( AsmReadTsc() > RdTscEnd ) {
|
|
// this shouldn't happen , the timeout value should be enough to transfer all the bytes.
|
|
// if timeout value isn't enough, do the following workaround
|
|
if( ReceiveDataEnd > ReceiveRequest ) {
|
|
RdTscEnd += 1000000;
|
|
while( (0 == (I2cPort->I2cHcRead16 ( I2cPort, R_IC_STATUS ) & STAT_TFNF) ) &&
|
|
( AsmReadTsc() < RdTscEnd )
|
|
);
|
|
if( I2cPort->I2cHcRead16 ( I2cPort, R_IC_STATUS ) & STAT_TFNF ) {
|
|
I2cPort->I2cHcWrite16 ( I2cPort, R_IC_DATA_CMD, B_READ_CMD | B_STOP_CMD );
|
|
gBS->Stall(2000); //finish the data transmission in fifo
|
|
} else {
|
|
DEBUG((DEBUG_INFO, "Tricky things happen!!!\r\n\r\n\r\n"));
|
|
}
|
|
} else {
|
|
DEBUG((DEBUG_INFO, "All read command has been transferred\r\n"));
|
|
}
|
|
Status = EFI_TIMEOUT;
|
|
break;
|
|
}
|
|
|
|
if (ReceiveDataEnd == ReceiveRequest) {
|
|
continue;
|
|
}
|
|
|
|
// Wait until a read request will fit
|
|
if ( 0 == ( I2cStatus & STAT_TFNF )) {
|
|
gBS->Stall (10);
|
|
continue;
|
|
}
|
|
// Issue the next read request
|
|
if(ReceiveDataEnd == ( ReceiveRequest + 1 ) ) {
|
|
I2cPort->I2cHcWrite16 ( I2cPort, R_IC_DATA_CMD, B_READ_CMD | B_STOP_CMD );
|
|
} else {
|
|
I2cPort->I2cHcWrite16 ( I2cPort, R_IC_DATA_CMD, B_READ_CMD );
|
|
}
|
|
|
|
ReceiveRequest += 1;
|
|
gBS->Stall (I2cPort->IntervalTime);
|
|
}
|
|
LocalIrqRestore(Eflags);
|
|
}
|
|
|
|
RdTscInterval = AsmReadTsc() - RdTstStart;
|
|
|
|
ExitI2cStartRequest:
|
|
|
|
gBS->RestoreTPL (OriginalTpl);
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
LocateI2cBase(
|
|
IN I2C_BUS_INSTANCE *I2cPort,
|
|
OUT EFI_PHYSICAL_ADDRESS *Base0BackUp,
|
|
OUT UINT32 *PciCmdBackUp
|
|
)
|
|
{
|
|
UINTN PciI2cDevBase;
|
|
EFI_STATUS Status;
|
|
EFI_PHYSICAL_ADDRESS MmioBase0;
|
|
UINT32 MmioBaseLow; // For supporting 64-bit address, above 4GB
|
|
UINT32 MmioBaseHigh; // For supporting 64-bit address, above 4GB
|
|
MmioBase0 = 0;
|
|
MmioBaseLow = 0;
|
|
MmioBaseHigh = 0;
|
|
|
|
if (I2cPort->Info.GetDeviceMode (I2cPort) != I2C_DEVICE_MODE_PCI) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
PciI2cDevBase = MmPciAddress (
|
|
0,
|
|
I2cPort->PciI2c.Bus,
|
|
I2cPort->PciI2c.Dev,
|
|
I2cPort->PciI2c.Func,
|
|
0
|
|
);
|
|
|
|
if (MmioRead16(PciI2cDevBase + PCI_VENDOR_ID_OFFSET) == 0xFFFF) {
|
|
return EFI_ALREADY_STARTED;
|
|
}
|
|
|
|
MmioBaseLow = MmioRead32(PciI2cDevBase + 0x10);
|
|
if ((MmioBaseLow & 0x6) != 0) {
|
|
//
|
|
// 64-bit address space.
|
|
//
|
|
MmioBaseHigh = MmioRead32(PciI2cDevBase + 0x14);
|
|
}
|
|
MmioBaseLow = MmioBaseLow & 0xFFFFF000;
|
|
*Base0BackUp = LShiftU64((UINT64)MmioBaseHigh,32) + MmioBaseLow;
|
|
// *Base0BackUp = MmioRead32(PciI2cDevBase + 0x10);
|
|
|
|
*PciCmdBackUp = MmioRead32 (PciI2cDevBase + PCI_COMMAND_OFFSET);
|
|
|
|
if (*Base0BackUp == 0) {
|
|
Status = gDS->AllocateMemorySpace (
|
|
EfiGcdAllocateAnySearchBottomUp,
|
|
EfiGcdMemoryTypeMemoryMappedIo,
|
|
12,
|
|
0x1000,
|
|
&MmioBase0,
|
|
gImageHandle,
|
|
NULL
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
} else {
|
|
MmioBase0 = (UINT64)(UINTN)LShiftU64((UINT64)MmioBaseHigh, 32) + MmioBaseLow;
|
|
}
|
|
|
|
I2cPort->I2cBase = (UINT64)(UINTN)MmioBase0;
|
|
|
|
MmioAnd32 (PciI2cDevBase + PCI_COMMAND_OFFSET, (UINT32)~(EFI_PCI_COMMAND_MEMORY_SPACE|EFI_PCI_COMMAND_BUS_MASTER));
|
|
MmioWrite32 (PciI2cDevBase + 0x10, ((UINT32)I2cPort->I2cBase)|BIT2);
|
|
MmioOr32 (PciI2cDevBase + PCI_COMMAND_OFFSET, (EFI_PCI_COMMAND_MEMORY_SPACE|EFI_PCI_COMMAND_BUS_MASTER));
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
FreeI2cBase(
|
|
IN I2C_BUS_INSTANCE *I2cPort,
|
|
IN EFI_PHYSICAL_ADDRESS Base0Org,
|
|
IN UINT32 PciCmdOrg
|
|
)
|
|
{
|
|
UINTN PciI2cDevBase;
|
|
|
|
if (I2cPort->Info.GetDeviceMode (I2cPort) != I2C_DEVICE_MODE_PCI) {
|
|
return;
|
|
}
|
|
|
|
PciI2cDevBase = MmPciAddress (
|
|
0,
|
|
I2cPort->PciI2c.Bus,
|
|
I2cPort->PciI2c.Dev,
|
|
I2cPort->PciI2c.Func,
|
|
0
|
|
);
|
|
|
|
if (MmioRead16(PciI2cDevBase + PCI_VENDOR_ID_OFFSET) == 0xFFFF) {
|
|
return;
|
|
}
|
|
|
|
if (MmioRead32(PciI2cDevBase + 0x10) != Base0Org) {
|
|
MmioAnd32 (PciI2cDevBase + PCI_COMMAND_OFFSET, (UINT32)~(EFI_PCI_COMMAND_MEMORY_SPACE|EFI_PCI_COMMAND_BUS_MASTER));
|
|
MmioWrite32 (PciI2cDevBase + 0x10, (UINT32)(UINTN)Base0Org);
|
|
|
|
gDS->FreeMemorySpace (I2cPort->I2cBase, (UINT64) 0x1000);
|
|
}
|
|
|
|
MmioWrite32 (PciI2cDevBase + PCI_COMMAND_OFFSET, PciCmdOrg);
|
|
}
|
|
|
|
VOID
|
|
PollI2cNotify (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Function to be called every time periodic event happens. This will check if
|
|
the SMBus Host Controller has received a Host Notify command. If so, it will
|
|
see if a notification has been reqested on that event and make any callbacks
|
|
that may be necessary.
|
|
|
|
Arguments:
|
|
|
|
Event: The periodic event that occured and got us into this callback.
|
|
Context: Event context. Will be NULL in this case, since we already have our
|
|
I2cPort data in a module global variable.
|
|
|
|
Returns:
|
|
|
|
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
I2C_ASYNC_NODE *Node;
|
|
I2C_BUS_INSTANCE *I2cContext = NULL;
|
|
LIST_ENTRY *TempList1 = NULL;
|
|
LIST_ENTRY *TempList2 = NULL;
|
|
|
|
I2cContext = (I2C_BUS_INSTANCE*)Context;
|
|
|
|
if (IsListEmpty(&I2cContext->AsyncQueue)) {
|
|
return;
|
|
}
|
|
|
|
for (TempList1 = GetFirstNode(&I2cContext->AsyncQueue); !IsNull(&I2cContext->AsyncQueue, TempList1); ) {
|
|
|
|
TempList2 = GetNextNode(&I2cContext->AsyncQueue, TempList1);
|
|
|
|
Node = I2C_ASYNC_CONTEXT_FROM_LINK (TempList1);
|
|
|
|
if (Node->I2cDev != NULL && I2cContext->Info.GpioArrived (I2cContext, Node->I2cDev->GpioHc, Node->I2cDev->GpioPin, Node->I2cDev->GpioLevel)) {
|
|
|
|
I2cContext->CacheMode = TRUE;
|
|
I2cContext->CacheAvailable = 0;
|
|
|
|
//
|
|
// We have a match, notify the requested function
|
|
//
|
|
Status = I2cContext->MasterApi.StartRequest (
|
|
&I2cContext->MasterApi,
|
|
Node->I2cDev->I2cDevice->SlaveAddressArray[0],
|
|
Node->RequestPacket,
|
|
NULL,
|
|
Node->I2cStatus
|
|
);
|
|
|
|
|
|
gBS->SignalEvent (Node->Event);
|
|
|
|
RemoveEntryList (TempList1);
|
|
|
|
if (Node->I2cDev) {
|
|
FreePool (Node->I2cDev);
|
|
}
|
|
|
|
if (Node->RequestPacket) {
|
|
FreePool (Node->RequestPacket);
|
|
}
|
|
|
|
if (Node) {
|
|
FreePool (Node);
|
|
}
|
|
|
|
//
|
|
// Restore the mode back to normal
|
|
//
|
|
I2cContext->CacheMode = FALSE;
|
|
}
|
|
TempList1 = TempList2;
|
|
}
|
|
}
|
|
|
|
EFI_STATUS
|
|
InitializePeriodicEvent (
|
|
IN I2C_BUS_INSTANCE *I2cContext
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
Status = gBS->CreateEvent (
|
|
(EVT_TIMER | EVT_NOTIFY_SIGNAL),
|
|
TPL_NOTIFY,
|
|
PollI2cNotify,
|
|
I2cContext,
|
|
&I2cContext->TimEvent
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = gBS->SetTimer (
|
|
I2cContext->TimEvent,
|
|
TimerPeriodic,
|
|
10 * MILLISECOND
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
gBS->CloseEvent (I2cContext->TimEvent);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
FindI2cDev (
|
|
IN EFI_HANDLE Handle,
|
|
IN UINTN SlaveAddress,
|
|
OUT EFI_I2C_DEVICE **I2cDev
|
|
)
|
|
{
|
|
UINTN Index;
|
|
EFI_STATUS Status;
|
|
EFI_I2C_ENUMERATE_PROTOCOL *I2cEnumerate;
|
|
|
|
if (I2cDev == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = gBS->HandleProtocol (
|
|
Handle,
|
|
&gEfiI2cEnumerateProtocolGuid,
|
|
(VOID**) &I2cEnumerate
|
|
);
|
|
ASSERT ( EFI_SUCCESS == Status );
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
*I2cDev = NULL;
|
|
|
|
do {
|
|
Status = I2cEnumerate->Enumerate (I2cEnumerate, I2cDev);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
break;
|
|
}
|
|
|
|
ASSERT ( *I2cDev != NULL );
|
|
|
|
for (Index = 0; Index < (*I2cDev)->SlaveAddressCount; Index++) {
|
|
if ((*I2cDev)->SlaveAddressArray[Index] == SlaveAddress) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
} while (1);
|
|
|
|
*I2cDev = NULL;
|
|
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
EFI_STATUS
|
|
FindI2cHidDev (
|
|
IN EFI_I2C_DEVICE *I2cDev,
|
|
OUT H2O_I2C_HID_DEVICE **I2cHidDev
|
|
)
|
|
{
|
|
UINTN DeviceHandleCount;
|
|
UINTN Index;
|
|
BOOLEAN Found;
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE *DeviceHandleBuffer;
|
|
H2O_I2C_HID_DEVICE *I2cHidDevice;
|
|
|
|
DeviceHandleBuffer = NULL;
|
|
Found = FALSE;
|
|
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gI2cHidDeviceInfoGuid,
|
|
NULL,
|
|
&DeviceHandleCount,
|
|
&DeviceHandleBuffer
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
Status = EFI_NOT_FOUND;
|
|
goto ExitFindI2cHideDev;
|
|
}
|
|
|
|
for (Index = 0; Index < DeviceHandleCount; Index++) {
|
|
Status = gBS->HandleProtocol (
|
|
DeviceHandleBuffer[Index],
|
|
|
|
&gI2cHidDeviceInfoGuid,
|
|
&I2cHidDevice
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
if (CompareGuid (I2cHidDevice->I2cDevice->DeviceGuid, I2cDev->DeviceGuid)) {
|
|
*I2cHidDev = AllocateCopyPool (sizeof (H2O_I2C_HID_DEVICE), I2cHidDevice);
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
|
|
}
|
|
FreePool (DeviceHandleBuffer);
|
|
|
|
ExitFindI2cHideDev:
|
|
if (Found) {
|
|
Status = EFI_SUCCESS;
|
|
} else {
|
|
Status = EFI_NOT_FOUND;
|
|
}
|
|
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
EFI_STATUS
|
|
I2cPusAsyncQueue (
|
|
IN I2C_BUS_INSTANCE *I2cContext,
|
|
IN UINTN SlaveAddress,
|
|
IN EFI_I2C_REQUEST_PACKET *RequestPacket,
|
|
IN EFI_EVENT Event,
|
|
OUT EFI_STATUS *I2cStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Register a callback in the event of a Host Notify command being sent by a
|
|
specified Slave Device.
|
|
|
|
Arguments:
|
|
|
|
This: Pointer to the instance of the EFI_SMBUS_HC_PROTOCOL.
|
|
SlaveAddress: Address of the device whose Host Notify command we want to
|
|
trap.
|
|
Data: Data of the Host Notify command we want to trap.
|
|
NotifyFunction: Function to be called in the event the desired Host Notify
|
|
command occurs.
|
|
|
|
Returns:
|
|
|
|
EFI_INVALID_PARAMETER: NotifyFunction was NULL.
|
|
EFI_OUT_OF_RESOURCES: Unable to allocate space to register the notification.
|
|
EFI_UNSUPPORTED: Unable to create the event needed for notifications.
|
|
EFI_SUCCESS: Function completed successfully
|
|
|
|
-*/
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Length;
|
|
I2C_ASYNC_NODE *NewNode;
|
|
EFI_I2C_DEVICE *I2cDev;
|
|
H2O_I2C_HID_DEVICE *I2cHidDev;
|
|
|
|
if (Event == NULL || RequestPacket == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = FindI2cDev ( I2cContext->Handle, SlaveAddress, &I2cDev);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = FindI2cHidDev (I2cDev, &I2cHidDev);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
NewNode = AllocateZeroPool(sizeof(I2C_ASYNC_NODE));
|
|
|
|
if (NewNode == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
ASSERT_EFI_ERROR (Status);
|
|
return Status;
|
|
}
|
|
//
|
|
// If this is the first notification request, start an event to periodically
|
|
// check for a Notify master command.
|
|
//
|
|
if (!I2cContext->TimEvent) {
|
|
Status = InitializePeriodicEvent (I2cContext);
|
|
if (EFI_ERROR(Status)) {
|
|
FreePool (NewNode);
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
}
|
|
|
|
Length = sizeof(EFI_I2C_REQUEST_PACKET);
|
|
if (RequestPacket->OperationCount == 2) {
|
|
Length += sizeof(EFI_I2C_OPERATION);
|
|
}
|
|
|
|
|
|
NewNode->Signature = I2C_PRIVATE_DATA_SIGNATURE;
|
|
NewNode->I2cDev = I2cHidDev;
|
|
NewNode->Event = Event;
|
|
NewNode->I2cStatus = I2cStatus;
|
|
NewNode->RequestPacket = AllocateZeroPool(Length);
|
|
if (NewNode->RequestPacket == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
ASSERT_EFI_ERROR (Status);
|
|
FreePool (NewNode);
|
|
return Status;
|
|
}
|
|
CopyMem ( NewNode->RequestPacket, RequestPacket, Length);
|
|
|
|
InsertTailList (&I2cContext->AsyncQueue, &NewNode->Link);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|