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