mirror of https://github.com/acidanthera/audk.git
742 lines
25 KiB
C
742 lines
25 KiB
C
/** @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;
|
|
}
|