blob: dee286ba05089034191bcf3f9eff306365e2db94 [file] [log] [blame]
/** @file
Functions for accessing I2C registers.
Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials are licensed and made available under
the terms and conditions of the BSD License that accompanies this distribution.
The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php.
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
--*/
#include <Library/DebugLib.h>
#include <Library/TimerLib.h>
#include <PchRegs/PchRegsPcu.h>
#include <PchRegs.h>
#include <PlatformBaseAddresses.h>
#include <PchRegs/PchRegsLpss.h>
#include <Library/I2CLib.h>
#include <Protocol/GlobalNvsArea.h>
#include <Library/UefiBootServicesTableLib.h>
#include <I2CRegs.h>
#define GLOBAL_NVS_OFFSET(Field) (UINTN)((CHAR8*)&((EFI_GLOBAL_NVS_AREA*)0)->Field - (CHAR8*)0)
#define PCIEX_BASE_ADDRESS 0xE0000000
#define PCI_EXPRESS_BASE_ADDRESS ((VOID *) (UINTN) PCIEX_BASE_ADDRESS)
#define MmPciAddress( Segment, Bus, Device, Function, Register ) \
((UINTN)PCI_EXPRESS_BASE_ADDRESS + \
(UINTN)(Bus << 20) + \
(UINTN)(Device << 15) + \
(UINTN)(Function << 12) + \
(UINTN)(Register) \
)
#define PCI_D31F0_REG_BASE PCIEX_BASE_ADDRESS + (UINT32) (31 << 15)
typedef struct _LPSS_PCI_DEVICE_INFO {
UINTN Segment;
UINTN BusNum;
UINTN DeviceNum;
UINTN FunctionNum;
UINTN Bar0;
UINTN Bar1;
} LPSS_PCI_DEVICE_INFO;
LPSS_PCI_DEVICE_INFO mLpssPciDeviceList[] = {
{0, DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_LPSS_DMAC1, PCI_FUNCTION_NUMBER_PCH_LPSS_DMAC, 0xFE900000, 0xFE908000},
{0, DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_LPSS_I2C, PCI_FUNCTION_NUMBER_PCH_LPSS_I2C0, 0xFE910000, 0xFE918000},
{0, DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_LPSS_I2C, PCI_FUNCTION_NUMBER_PCH_LPSS_I2C1, 0xFE920000, 0xFE928000},
{0, DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_LPSS_I2C, PCI_FUNCTION_NUMBER_PCH_LPSS_I2C2, 0xFE930000, 0xFE938000},
{0, DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_LPSS_I2C, PCI_FUNCTION_NUMBER_PCH_LPSS_I2C3, 0xFE940000, 0xFE948000},
{0, DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_LPSS_I2C, PCI_FUNCTION_NUMBER_PCH_LPSS_I2C4, 0xFE950000, 0xFE958000},
{0, DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_LPSS_I2C, PCI_FUNCTION_NUMBER_PCH_LPSS_I2C5, 0xFE960000, 0xFE968000},
{0, DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_LPSS_I2C, PCI_FUNCTION_NUMBER_PCH_LPSS_I2C6, 0xFE970000, 0xFE978000}
};
#define LPSS_PCI_DEVICE_NUMBER sizeof(mLpssPciDeviceList)/sizeof(LPSS_PCI_DEVICE_INFO)
STATIC UINTN mI2CBaseAddress = 0;
STATIC UINT16 mI2CSlaveAddress = 0;
UINT16 mI2cMode=B_IC_RESTART_EN | B_IC_SLAVE_DISABLE | B_MASTER_MODE ;
UINTN mI2cNvsBaseAddress[] = {
GLOBAL_NVS_OFFSET(LDMA2Addr),
GLOBAL_NVS_OFFSET(I2C1Addr),
GLOBAL_NVS_OFFSET(I2C2Addr),
GLOBAL_NVS_OFFSET(I2C3Addr),
GLOBAL_NVS_OFFSET(I2C4Addr),
GLOBAL_NVS_OFFSET(I2C5Addr),
GLOBAL_NVS_OFFSET(I2C6Addr),
GLOBAL_NVS_OFFSET(I2C7Addr)
};
/**
This function get I2Cx controller base address (BAR0).
@param I2cControllerIndex Bus Number of I2C controller.
@return I2C BAR.
**/
UINTN
GetI2cBarAddr(
IN UINT8 I2cControllerIndex
)
{
EFI_STATUS Status;
EFI_GLOBAL_NVS_AREA_PROTOCOL *GlobalNvsArea;
UINTN AcpiBaseAddr;
UINTN PciMmBase=0;
ASSERT(gBS!=NULL);
Status = gBS->LocateProtocol (
&gEfiGlobalNvsAreaProtocolGuid,
NULL,
&GlobalNvsArea
);
//
// PCI mode from PEI ( Global NVS is not ready).
//
if (EFI_ERROR(Status)) {
DEBUG ((EFI_D_INFO, "GetI2cBarAddr() gEfiGlobalNvsAreaProtocolGuid:%r\n", Status));
//
// Global NVS is not ready.
//
return 0;
}
AcpiBaseAddr = *(UINTN*)((CHAR8*)GlobalNvsArea->Area + mI2cNvsBaseAddress[I2cControllerIndex + 1]);
//
//PCI mode from DXE (global NVS protocal) to LPSS OnReadytoBoot(swith to ACPI).
//
if(AcpiBaseAddr==0) {
PciMmBase = MmPciAddress (
mLpssPciDeviceList[I2cControllerIndex + 1].Segment,
mLpssPciDeviceList[I2cControllerIndex + 1].BusNum,
mLpssPciDeviceList[I2cControllerIndex + 1].DeviceNum,
mLpssPciDeviceList[I2cControllerIndex + 1].FunctionNum,
0
);
DEBUG((EFI_D_ERROR, "\nGetI2cBarAddr() I2C Device %x %x %x PciMmBase:%x\n", \
mLpssPciDeviceList[I2cControllerIndex + 1].BusNum, \
mLpssPciDeviceList[I2cControllerIndex + 1].DeviceNum, \
mLpssPciDeviceList[I2cControllerIndex + 1].FunctionNum, PciMmBase));
if (MmioRead32 (PciMmBase) != 0xFFFFFFFF) {
if((MmioRead32 (PciMmBase+R_PCH_LPSS_I2C_STSCMD)& B_PCH_LPSS_I2C_STSCMD_MSE)) {
//
// Get the address allocted.
//
mLpssPciDeviceList[I2cControllerIndex + 1].Bar0=MmioRead32 (PciMmBase+R_PCH_LPSS_I2C_BAR);
mLpssPciDeviceList[I2cControllerIndex + 1].Bar1=MmioRead32 (PciMmBase+R_PCH_LPSS_I2C_BAR1);
}
}
AcpiBaseAddr =mLpssPciDeviceList[I2cControllerIndex+1].Bar0;
}
//
// ACPI mode from BDS: LPSS OnReadytoBoot
//
else {
DEBUG ((EFI_D_INFO, "GetI2cBarAddr() NVS Varialable is updated by this LIB or LPSS \n"));
}
DEBUG ((EFI_D_INFO, "GetI2cBarAddr() I2cControllerIndex+1 0x%x AcpiBaseAddr:0x%x \n", (I2cControllerIndex + 1), AcpiBaseAddr));
return AcpiBaseAddr;
}
/**
This function enables I2C controllers.
@param I2cControllerIndex Bus Number of I2C controllers.
@return Result of the I2C initialization.
**/
EFI_STATUS
ProgramPciLpssI2C (
IN UINT8 I2cControllerIndex
)
{
UINT32 PmcBase;
UINTN PciMmBase=0;
EFI_STATUS Status;
EFI_GLOBAL_NVS_AREA_PROTOCOL *GlobalNvsArea;
UINT32 PmcFunctionDsiable[]= {
B_PCH_PMC_FUNC_DIS_LPSS2_FUNC1,
B_PCH_PMC_FUNC_DIS_LPSS2_FUNC2,
B_PCH_PMC_FUNC_DIS_LPSS2_FUNC3,
B_PCH_PMC_FUNC_DIS_LPSS2_FUNC4,
B_PCH_PMC_FUNC_DIS_LPSS2_FUNC5,
B_PCH_PMC_FUNC_DIS_LPSS2_FUNC6,
B_PCH_PMC_FUNC_DIS_LPSS2_FUNC7
};
DEBUG ((EFI_D_INFO, "ProgramPciLpssI2C() Start\n"));
//
// Set the VLV Function Disable Register to ZERO
//
PmcBase = MmioRead32 (PCI_D31F0_REG_BASE + R_PCH_LPC_PMC_BASE) & B_PCH_LPC_PMC_BASE_BAR;
if(MmioRead32(PmcBase+R_PCH_PMC_FUNC_DIS)&PmcFunctionDsiable[I2cControllerIndex]) {
DEBUG ((EFI_D_INFO, "ProgramPciLpssI2C() End:I2C[%x] is disabled\n",I2cControllerIndex));
return EFI_NOT_READY;
}
DEBUG ((EFI_D_INFO, "ProgramPciLpssI2C()------------I2cControllerIndex=%x,PMC=%x\n",I2cControllerIndex,MmioRead32(PmcBase+R_PCH_PMC_FUNC_DIS)));
{
PciMmBase = MmPciAddress (
mLpssPciDeviceList[I2cControllerIndex+1].Segment,
mLpssPciDeviceList[I2cControllerIndex+1].BusNum,
mLpssPciDeviceList[I2cControllerIndex+1].DeviceNum,
mLpssPciDeviceList[I2cControllerIndex+1].FunctionNum,
0
);
DEBUG((EFI_D_ERROR, "Program Pci Lpss I2C Device %x %x %x PciMmBase:%x\n", \
mLpssPciDeviceList[I2cControllerIndex+1].BusNum, \
mLpssPciDeviceList[I2cControllerIndex+1].DeviceNum, \
mLpssPciDeviceList[I2cControllerIndex+1].FunctionNum, PciMmBase));
if (MmioRead32 (PciMmBase) != 0xFFFFFFFF) {
if((MmioRead32 (PciMmBase+R_PCH_LPSS_I2C_STSCMD)& B_PCH_LPSS_I2C_STSCMD_MSE)) {
//
// Get the address allocted.
//
mLpssPciDeviceList[I2cControllerIndex+1].Bar0=MmioRead32 (PciMmBase+R_PCH_LPSS_I2C_BAR);
mLpssPciDeviceList[I2cControllerIndex+1].Bar1=MmioRead32 (PciMmBase+R_PCH_LPSS_I2C_BAR1);
DEBUG((EFI_D_ERROR, "ProgramPciLpssI2C() bar0:0x%x bar1:0x%x\n",mLpssPciDeviceList[I2cControllerIndex+1].Bar0, mLpssPciDeviceList[I2cControllerIndex+1].Bar1));
} else {
//
// Program BAR 0
//
ASSERT (((mLpssPciDeviceList[I2cControllerIndex+1].Bar0 & B_PCH_LPSS_I2C_BAR_BA) == mLpssPciDeviceList[I2cControllerIndex+1].Bar0) && (mLpssPciDeviceList[I2cControllerIndex+1].Bar0 != 0));
MmioWrite32 ((UINTN) (PciMmBase + R_PCH_LPSS_I2C_BAR), (UINT32) (mLpssPciDeviceList[I2cControllerIndex+1].Bar0 & B_PCH_LPSS_I2C_BAR_BA));
//
// Program BAR 1
//
ASSERT (((mLpssPciDeviceList[I2cControllerIndex+1].Bar1 & B_PCH_LPSS_I2C_BAR1_BA) == mLpssPciDeviceList[I2cControllerIndex+1].Bar1) && (mLpssPciDeviceList[I2cControllerIndex+1].Bar1 != 0));
MmioWrite32 ((UINTN) (PciMmBase + R_PCH_LPSS_I2C_BAR1), (UINT32) (mLpssPciDeviceList[I2cControllerIndex+1].Bar1 & B_PCH_LPSS_I2C_BAR1_BA));
//
// Bus Master Enable & Memory Space Enable
//
MmioOr32 ((UINTN) (PciMmBase + R_PCH_LPSS_I2C_STSCMD), (UINT32) (B_PCH_LPSS_I2C_STSCMD_BME | B_PCH_LPSS_I2C_STSCMD_MSE));
ASSERT (MmioRead32 (mLpssPciDeviceList[I2cControllerIndex+1].Bar0) != 0xFFFFFFFF);
}
//
// Release Resets
//
MmioWrite32 (mLpssPciDeviceList[I2cControllerIndex+1].Bar0 + R_PCH_LPIO_I2C_MEM_RESETS,(B_PCH_LPIO_I2C_MEM_RESETS_FUNC | B_PCH_LPIO_I2C_MEM_RESETS_APB));
//
// Activate Clocks
//
MmioWrite32 (mLpssPciDeviceList[I2cControllerIndex+1].Bar0 + R_PCH_LPSS_I2C_MEM_PCP,0x80020003);//No use for A0
DEBUG ((EFI_D_INFO, "ProgramPciLpssI2C() Programmed()\n"));
}
//
// BDS: already switched to ACPI mode
//
else {
ASSERT(gBS!=NULL);
Status = gBS->LocateProtocol (
&gEfiGlobalNvsAreaProtocolGuid,
NULL,
&GlobalNvsArea
);
if (EFI_ERROR(Status)) {
DEBUG ((EFI_D_INFO, "GetI2cBarAddr() gEfiGlobalNvsAreaProtocolGuid:%r\n", Status));
//
// gEfiGlobalNvsAreaProtocolGuid is not ready.
//
return 0;
}
mLpssPciDeviceList[I2cControllerIndex + 1].Bar0 = *(UINTN*)((CHAR8*)GlobalNvsArea->Area + mI2cNvsBaseAddress[I2cControllerIndex + 1]);
DEBUG ((EFI_D_INFO, "ProgramPciLpssI2C(): is switched to ACPI 0x:%x \n",mLpssPciDeviceList[I2cControllerIndex + 1].Bar0));
}
}
DEBUG ((EFI_D_INFO, "ProgramPciLpssI2C() End\n"));
return EFI_SUCCESS;
}
/**
Disable I2C Bus.
@param VOID.
@return Result of the I2C disabling.
**/
RETURN_STATUS
I2cDisable (
VOID
)
{
//
// 0.1 seconds
//
UINT32 NumTries = 10000;
MmioWrite32 ( mI2CBaseAddress + R_IC_ENABLE, 0 );
while ( 0 != ( MmioRead32 ( mI2CBaseAddress + R_IC_ENABLE_STATUS) & 1)) {
MicroSecondDelay (10);
NumTries --;
if(0 == NumTries) {
return RETURN_NOT_READY;
}
}
return RETURN_SUCCESS;
}
/**
Enable I2C Bus.
@param VOID.
@return Result of the I2C disabling.
**/
RETURN_STATUS
I2cEnable (
VOID
)
{
//
// 0.1 seconds
//
UINT32 NumTries = 10000;
MmioWrite32 (mI2CBaseAddress + R_IC_ENABLE, 1);
while (0 == (MmioRead32 (mI2CBaseAddress + R_IC_ENABLE_STATUS) & 1)) {
MicroSecondDelay (10);
NumTries --;
if(0 == NumTries){
return RETURN_NOT_READY;
}
}
return RETURN_SUCCESS;
}
/**
Enable I2C Bus.
@param VOID.
@return Result of the I2C enabling.
**/
RETURN_STATUS
I2cBusFrequencySet (
IN UINTN BusClockHertz
)
{
DEBUG((EFI_D_INFO,"InputFreq BusClockHertz: %d\r\n",BusClockHertz));
//
// Set the 100 KHz clock divider according to SV result and I2C spec
//
MmioWrite32 ( mI2CBaseAddress + R_IC_SS_SCL_HCNT, (UINT16)0x214 );
MmioWrite32 ( mI2CBaseAddress + R_IC_SS_SCL_LCNT, (UINT16)0x272 );
//
// Set the 400 KHz clock divider according to SV result and I2C spec
//
MmioWrite32 ( mI2CBaseAddress + R_IC_FS_SCL_HCNT, (UINT16)0x50 );
MmioWrite32 ( mI2CBaseAddress + R_IC_FS_SCL_LCNT, (UINT16)0xAD );
switch ( BusClockHertz ) {
case 100 * 1000:
MmioWrite32 ( mI2CBaseAddress + R_IC_SDA_HOLD, (UINT16)0x40);//100K
mI2cMode = V_SPEED_STANDARD;
break;
case 400 * 1000:
MmioWrite32 ( mI2CBaseAddress + R_IC_SDA_HOLD, (UINT16)0x32);//400K
mI2cMode = V_SPEED_FAST;
break;
default:
MmioWrite32 ( mI2CBaseAddress + R_IC_SDA_HOLD, (UINT16)0x09);//3.4M
mI2cMode = V_SPEED_HIGH;
}
//
// Select the frequency counter,
// Enable restart condition,
// Enable master FSM, disable slave FSM.
//
mI2cMode |= B_IC_RESTART_EN | B_IC_SLAVE_DISABLE | B_MASTER_MODE;
return EFI_SUCCESS;
}
/**
Initializes the host controller to execute I2C commands.
@param I2cControllerIndex Index of I2C controller in LPSS device. 0 represents I2C0, which is PCI function 1 of LPSS device.
@return EFI_SUCCESS Opcode initialization on the I2C host controller completed.
@return EFI_DEVICE_ERROR Device error, operation failed.
**/
EFI_STATUS
I2CInit (
IN UINT8 I2cControllerIndex,
IN UINT16 SlaveAddress
)
{
EFI_STATUS Status=RETURN_SUCCESS;
UINT32 NumTries = 0;
UINTN GnvsI2cBarAddr=0;
//
// Verify the parameters
//
if ((1023 < SlaveAddress) || (6 < I2cControllerIndex)) {
Status = RETURN_INVALID_PARAMETER;
DEBUG((EFI_D_INFO,"I2CInit Exit with RETURN_INVALID_PARAMETER\r\n"));
return Status;
}
MmioWrite32 ( mI2CBaseAddress + R_IC_TAR, (UINT16)SlaveAddress );
mI2CSlaveAddress = SlaveAddress;
//
// 1.PEI: program and init ( before pci enumeration).
// 2.DXE:update address and re-init ( after pci enumeration).
// 3.BDS:update ACPI address and re-init ( after acpi mode is enabled).
//
if(mI2CBaseAddress == mLpssPciDeviceList[I2cControllerIndex + 1].Bar0) {
//
// I2CInit is already called.
//
GnvsI2cBarAddr=GetI2cBarAddr(I2cControllerIndex);
if((GnvsI2cBarAddr == 0)||(GnvsI2cBarAddr == mI2CBaseAddress)) {
DEBUG((EFI_D_INFO,"I2CInit Exit with mI2CBaseAddress:%x == [%x].Bar0\r\n",mI2CBaseAddress,I2cControllerIndex+1));
return RETURN_SUCCESS;
}
}
Status=ProgramPciLpssI2C(I2cControllerIndex);
if(Status!=EFI_SUCCESS) {
return Status;
}
mI2CBaseAddress = (UINT32) mLpssPciDeviceList[I2cControllerIndex + 1].Bar0;
DEBUG ((EFI_D_ERROR, "mI2CBaseAddress = 0x%x \n",mI2CBaseAddress));
//
// 1 seconds.
//
NumTries = 10000;
while ((1 == ( MmioRead32 ( mI2CBaseAddress + R_IC_STATUS) & STAT_MST_ACTIVITY ))) {
MicroSecondDelay(10);
NumTries --;
if(0 == NumTries) {
DEBUG((EFI_D_INFO, "Try timeout\r\n"));
return RETURN_DEVICE_ERROR;
}
}
Status = I2cDisable();
DEBUG((EFI_D_INFO, "I2cDisable Status = %r\r\n", Status));
I2cBusFrequencySet(400 * 1000);
MmioWrite32(mI2CBaseAddress + R_IC_INTR_MASK, 0x0);
if (0x7f < SlaveAddress )
SlaveAddress = ( SlaveAddress & 0x3ff ) | IC_TAR_10BITADDR_MASTER;
MmioWrite32 ( mI2CBaseAddress + R_IC_TAR, (UINT16)SlaveAddress );
MmioWrite32 ( mI2CBaseAddress + R_IC_RX_TL, 0);
MmioWrite32 ( mI2CBaseAddress + R_IC_TX_TL, 0 );
MmioWrite32 ( mI2CBaseAddress + R_IC_CON, mI2cMode);
Status = I2cEnable();
DEBUG((EFI_D_INFO, "I2cEnable Status = %r\r\n", Status));
MmioRead32 ( mI2CBaseAddress + R_IC_CLR_TX_ABRT );
return EFI_SUCCESS;
}
/**
Reads a Byte from I2C Device.
@param I2cControllerIndex I2C Bus no to which the I2C device has been connected
@param SlaveAddress Device Address from which the byte value has to be read
@param Offset Offset from which the data has to be read
@param *Byte Address to which the value read has to be stored
@param Start Whether a RESTART is issued before the byte is sent or received
@param End Whether STOP is generated after a data byte is sent or received
@return EFI_SUCCESS IF the byte value has been successfully read
@return EFI_DEVICE_ERROR Operation Failed, Device Error
**/
EFI_STATUS
ByteReadI2CBasic(
IN UINT8 I2cControllerIndex,
IN UINT8 SlaveAddress,
IN UINTN ReadBytes,
OUT UINT8 *ReadBuffer,
IN UINT8 Start,
IN UINT8 End
)
{
EFI_STATUS Status;
UINT32 I2cStatus;
UINT16 ReceiveData;
UINT8 *ReceiveDataEnd;
UINT8 *ReceiveRequest;
UINT16 RawIntrStat;
UINT32 Count=0;
Status = EFI_SUCCESS;
ReceiveDataEnd = &ReadBuffer [ReadBytes];
if( ReadBytes ) {
ReceiveRequest = ReadBuffer;
DEBUG((EFI_D_INFO,"Read: ---------------%d bytes to RX\r\n",ReceiveDataEnd - ReceiveRequest));
while ((ReceiveDataEnd > ReceiveRequest) || (ReceiveDataEnd > ReadBuffer)) {
//
// Check for NACK
//
RawIntrStat = (UINT16)MmioRead32 (mI2CBaseAddress + R_IC_RawIntrStat);
if ( 0 != ( RawIntrStat & I2C_INTR_TX_ABRT )) {
MmioRead32 ( mI2CBaseAddress + R_IC_CLR_TX_ABRT );
Status = RETURN_DEVICE_ERROR;
DEBUG((EFI_D_INFO,"TX ABRT ,%d bytes hasn't been transferred\r\n",ReceiveDataEnd - ReceiveRequest));
break;
}
//
// Determine if another byte was received
//
I2cStatus = (UINT16)MmioRead32 (mI2CBaseAddress + R_IC_STATUS);
if (0 != ( I2cStatus & STAT_RFNE )) {
ReceiveData = (UINT16)MmioRead32 ( mI2CBaseAddress + R_IC_DATA_CMD );
*ReadBuffer++ = (UINT8)ReceiveData;
DEBUG((EFI_D_INFO,"MmioRead32 ,1 byte 0x:%x is received\r\n",ReceiveData));
}
if(ReceiveDataEnd == ReceiveRequest) {
MicroSecondDelay ( FIFO_WRITE_DELAY );
DEBUG((EFI_D_INFO,"ReceiveDataEnd==ReceiveRequest------------%x\r\n",I2cStatus & STAT_RFNE));
Count++;
if(Count<1024) {
//
// To avoid sys hung without ul-pmc device on RVP,
// waiting the last request to get data and make (ReceiveDataEnd > ReadBuffer) =TRUE.
//
continue;
} else {
break;
}
}
//
// Wait until a read request will fit.
//
if (0 == (I2cStatus & STAT_TFNF)) {
DEBUG((EFI_D_INFO,"Wait until a read request will fit\r\n"));
MicroSecondDelay (10);
continue;
}
//
// Issue the next read request.
//
if(End && Start) {
MmioWrite32 ( mI2CBaseAddress + R_IC_DATA_CMD, B_READ_CMD|B_CMD_RESTART|B_CMD_STOP);
} else if (!End && Start) {
MmioWrite32 ( mI2CBaseAddress + R_IC_DATA_CMD, B_READ_CMD|B_CMD_RESTART);
} else if (End && !Start) {
MmioWrite32 ( mI2CBaseAddress + R_IC_DATA_CMD, B_READ_CMD|B_CMD_STOP);
} else if (!End && !Start) {
MmioWrite32 ( mI2CBaseAddress + R_IC_DATA_CMD, B_READ_CMD);
}
MicroSecondDelay (FIFO_WRITE_DELAY);
ReceiveRequest += 1;
}
}
return Status;
}
/**
Writes a Byte to I2C Device.
@param I2cControllerIndex I2C Bus no to which the I2C device has been connected
@param SlaveAddress Device Address from which the byte value has to be written
@param Offset Offset from which the data has to be read
@param *Byte Address to which the value written is stored
@param Start Whether a RESTART is issued before the byte is sent or received
@param End Whether STOP is generated after a data byte is sent or received
@return EFI_SUCCESS IF the byte value has been successfully written
@return EFI_DEVICE_ERROR Operation Failed, Device Error
**/
EFI_STATUS ByteWriteI2CBasic(
IN UINT8 I2cControllerIndex,
IN UINT8 SlaveAddress,
IN UINTN WriteBytes,
IN UINT8 *WriteBuffer,
IN UINT8 Start,
IN UINT8 End
)
{
EFI_STATUS Status;
UINT32 I2cStatus;
UINT8 *TransmitEnd;
UINT16 RawIntrStat;
UINT32 Count=0;
Status = EFI_SUCCESS;
Status=I2CInit(I2cControllerIndex, SlaveAddress);
if(Status!=EFI_SUCCESS)
return Status;
TransmitEnd = &WriteBuffer[WriteBytes];
if( WriteBytes ) {
DEBUG((EFI_D_INFO,"Write: --------------%d bytes to TX\r\n",TransmitEnd - WriteBuffer));
while (TransmitEnd > WriteBuffer) {
I2cStatus = MmioRead32 (mI2CBaseAddress + R_IC_STATUS);
RawIntrStat = (UINT16)MmioRead32 (mI2CBaseAddress + R_IC_RawIntrStat);
if (0 != ( RawIntrStat & I2C_INTR_TX_ABRT)) {
MmioRead32 ( mI2CBaseAddress + R_IC_CLR_TX_ABRT);
Status = RETURN_DEVICE_ERROR;
DEBUG((EFI_D_ERROR,"TX ABRT TransmitEnd:0x%x WriteBuffer:0x%x\r\n", TransmitEnd, WriteBuffer));
break;
}
if (0 == (I2cStatus & STAT_TFNF)) {
//
// If TX not full , will send cmd or continue to wait
//
MicroSecondDelay (FIFO_WRITE_DELAY);
continue;
}
if(End && Start) {
MmioWrite32 (mI2CBaseAddress + R_IC_DATA_CMD, (*WriteBuffer++)|B_CMD_RESTART|B_CMD_STOP);
} else if (!End && Start) {
MmioWrite32 (mI2CBaseAddress + R_IC_DATA_CMD, (*WriteBuffer++)|B_CMD_RESTART);
} else if (End && !Start) {
MmioWrite32 (mI2CBaseAddress + R_IC_DATA_CMD, (*WriteBuffer++)|B_CMD_STOP);
} else if (!End && !Start ) {
MmioWrite32 (mI2CBaseAddress + R_IC_DATA_CMD, (*WriteBuffer++));
}
//
// Add a small delay to work around some odd behavior being seen. Without this delay bytes get dropped.
//
MicroSecondDelay ( FIFO_WRITE_DELAY );//wait after send cmd
//
// Time out
//
while(1) {
RawIntrStat = MmioRead16 ( mI2CBaseAddress + R_IC_RawIntrStat );
if (0 != ( RawIntrStat & I2C_INTR_TX_ABRT)) {
MmioRead16 (mI2CBaseAddress + R_IC_CLR_TX_ABRT);
Status = RETURN_DEVICE_ERROR;
DEBUG((EFI_D_ERROR,"TX ABRT TransmitEnd:0x%x WriteBuffer:0x%x\r\n", TransmitEnd, WriteBuffer));
}
if(0 == MmioRead16(mI2CBaseAddress + R_IC_TXFLR)) break;
MicroSecondDelay (FIFO_WRITE_DELAY);
Count++;
if(Count<1024) {
//
// to avoid sys hung without ul-pmc device on RVP.
// Waiting the last request to get data and make (ReceiveDataEnd > ReadBuffer) =TRUE.
//
continue;
} else {
break;
}
}//while( 1 )
}
}
return Status;
}
/**
Reads a Byte from I2C Device.
@param I2cControllerIndex I2C Bus no to which the I2C device has been connected
@param SlaveAddress Device Address from which the byte value has to be read
@param Offset Offset from which the data has to be read
@param ReadBytes Number of bytes to be read
@param *ReadBuffer Address to which the value read has to be stored
@return EFI_SUCCESS IF the byte value has been successfully read
@return EFI_DEVICE_ERROR Operation Failed, Device Error
**/
EFI_STATUS ByteReadI2C(
IN UINT8 I2cControllerIndex,
IN UINT8 SlaveAddress,
IN UINT8 Offset,
IN UINTN ReadBytes,
OUT UINT8 *ReadBuffer
)
{
EFI_STATUS Status;
DEBUG ((EFI_D_INFO, "ByteReadI2C:---offset:0x%x\n",Offset));
Status = ByteWriteI2CBasic(I2cControllerIndex, SlaveAddress,1,&Offset,TRUE,FALSE);
Status = ByteReadI2CBasic(I2cControllerIndex, SlaveAddress,ReadBytes,ReadBuffer,TRUE,TRUE);
return Status;
}
/**
Writes a Byte to I2C Device.
@param I2cControllerIndex I2C Bus no to which the I2C device has been connected
@param SlaveAddress Device Address from which the byte value has to be written
@param Offset Offset from which the data has to be written
@param WriteBytes Number of bytes to be written
@param *Byte Address to which the value written is stored
@return EFI_SUCCESS IF the byte value has been successfully read
@return EFI_DEVICE_ERROR Operation Failed, Device Error
**/
EFI_STATUS ByteWriteI2C(
IN UINT8 I2cControllerIndex,
IN UINT8 SlaveAddress,
IN UINT8 Offset,
IN UINTN WriteBytes,
IN UINT8 *WriteBuffer
)
{
EFI_STATUS Status;
DEBUG ((EFI_D_INFO, "ByteWriteI2C:---offset/bytes/buf:0x%x,0x%x,0x%x,0x%x\n",Offset,WriteBytes,WriteBuffer,*WriteBuffer));
Status = ByteWriteI2CBasic(I2cControllerIndex, SlaveAddress,1,&Offset,TRUE,FALSE);
Status = ByteWriteI2CBasic(I2cControllerIndex, SlaveAddress,WriteBytes,WriteBuffer,FALSE,TRUE);
return Status;
}