From 4e5220964b7108bbc9f731b326680e865aa63560 Mon Sep 17 00:00:00 2001 From: David Wei Date: Tue, 2 Jun 2015 01:47:57 +0000 Subject: Add Sample I2C Library for Baytrail I2C Controller. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: David Wei Reviewed-by: Tim He git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@17546 6f19259b-4bc3-4df7-8a09-765794883524 --- Vlv2TbltDevicePkg/Library/I2CLibDxe/I2CLib.c | 741 ++++++++++++++++++++++ Vlv2TbltDevicePkg/Library/I2CLibDxe/I2CLibDxe.inf | 44 ++ Vlv2TbltDevicePkg/Library/I2CLibDxe/I2CRegs.h | 132 ++++ 3 files changed, 917 insertions(+) create mode 100644 Vlv2TbltDevicePkg/Library/I2CLibDxe/I2CLib.c create mode 100644 Vlv2TbltDevicePkg/Library/I2CLibDxe/I2CLibDxe.inf create mode 100644 Vlv2TbltDevicePkg/Library/I2CLibDxe/I2CRegs.h (limited to 'Vlv2TbltDevicePkg/Library/I2CLibDxe') diff --git a/Vlv2TbltDevicePkg/Library/I2CLibDxe/I2CLib.c b/Vlv2TbltDevicePkg/Library/I2CLibDxe/I2CLib.c new file mode 100644 index 0000000000..dee286ba05 --- /dev/null +++ b/Vlv2TbltDevicePkg/Library/I2CLibDxe/I2CLib.c @@ -0,0 +1,741 @@ +/** @file + Functions for accessing I2C registers. + + Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.
+ + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/Vlv2TbltDevicePkg/Library/I2CLibDxe/I2CLibDxe.inf b/Vlv2TbltDevicePkg/Library/I2CLibDxe/I2CLibDxe.inf new file mode 100644 index 0000000000..277504ad39 --- /dev/null +++ b/Vlv2TbltDevicePkg/Library/I2CLibDxe/I2CLibDxe.inf @@ -0,0 +1,44 @@ +## @file +# Instance of I2C Library. +# +# Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which 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. +# +# + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = I2CLib + FILE_GUID = 7f62bf44-2ba7-4c2d-9d4a-91c8906ff053 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = I2CLib|DXE_DRIVER DXE_RUNTIME_DRIVER UEFI_DRIVER UEFI_APPLICATION + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + I2CLib.c + +[LibraryClasses] + BaseLib + IoLib + TimerLib + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + Vlv2TbltDevicePkg/PlatformPkg.dec + Vlv2DeviceRefCodePkg/Vlv2DeviceRefCodePkg.dec + +[Protocols] + gEfiGlobalNvsAreaProtocolGuid diff --git a/Vlv2TbltDevicePkg/Library/I2CLibDxe/I2CRegs.h b/Vlv2TbltDevicePkg/Library/I2CLibDxe/I2CRegs.h new file mode 100644 index 0000000000..443d57eddf --- /dev/null +++ b/Vlv2TbltDevicePkg/Library/I2CLibDxe/I2CRegs.h @@ -0,0 +1,132 @@ +/** @file + Register Definitions for I2C Driver/PEIM. + + Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.
+ + 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. + +--*/ + +#ifndef I2C_REGS_H +#define I2C_REGS_H + +// +// FIFO write delay value. +// +#define FIFO_WRITE_DELAY 2 + +// +// MMIO Register Definitions. +// +#define R_IC_CON ( 0x00) // I2C Control +#define B_IC_RESTART_EN BIT5 +#define B_IC_SLAVE_DISABLE BIT6 +#define V_SPEED_STANDARD 0x02 +#define V_SPEED_FAST 0x04 +#define V_SPEED_HIGH 0x06 +#define B_MASTER_MODE BIT0 + +#define R_IC_TAR ( 0x04) // I2C Target Address +#define IC_TAR_10BITADDR_MASTER BIT12 + +#define R_IC_SAR ( 0x08) // I2C Slave Address +#define R_IC_HS_MADDR ( 0x0C) // I2C HS MasterMode Code Address +#define R_IC_DATA_CMD ( 0x10) // I2C Rx/Tx Data Buffer and Command + +#define B_READ_CMD BIT8 // 1 = read, 0 = write +#define B_CMD_STOP BIT9 // 1 = STOP +#define B_CMD_RESTART BIT10 // 1 = IC_RESTART_EN + +#define V_WRITE_CMD_MASK ( 0xFF) + +#define R_IC_SS_SCL_HCNT ( 0x14) // Standard Speed I2C Clock SCL High Count +#define R_IC_SS_SCL_LCNT ( 0x18) // Standard Speed I2C Clock SCL Low Count +#define R_IC_FS_SCL_HCNT ( 0x1C) // Full Speed I2C Clock SCL High Count +#define R_IC_FS_SCL_LCNT ( 0x20) // Full Speed I2C Clock SCL Low Count +#define R_IC_HS_SCL_HCNT ( 0x24) // High Speed I2C Clock SCL High Count +#define R_IC_HS_SCL_LCNT ( 0x28) // High Speed I2C Clock SCL Low Count +#define R_IC_INTR_STAT ( 0x2C) // I2C Inetrrupt Status +#define R_IC_INTR_MASK ( 0x30) // I2C Interrupt Mask +#define I2C_INTR_GEN_CALL BIT11 // General call received +#define I2C_INTR_START_DET BIT10 +#define I2C_INTR_STOP_DET BIT9 +#define I2C_INTR_ACTIVITY BIT8 +#define I2C_INTR_TX_ABRT BIT6 // Set on NACK +#define I2C_INTR_TX_EMPTY BIT4 +#define I2C_INTR_TX_OVER BIT3 +#define I2C_INTR_RX_FULL BIT2 // Data bytes in RX FIFO over threshold +#define I2C_INTR_RX_OVER BIT1 +#define I2C_INTR_RX_UNDER BIT0 +#define R_IC_RawIntrStat ( 0x34) // I2C Raw Interrupt Status +#define R_IC_RX_TL ( 0x38) // I2C Receive FIFO Threshold +#define R_IC_TX_TL ( 0x3C) // I2C Transmit FIFO Threshold +#define R_IC_CLR_INTR ( 0x40) // Clear Combined and Individual Interrupts +#define R_IC_CLR_RX_UNDER ( 0x44) // Clear RX_UNDER Interrupt +#define R_IC_CLR_RX_OVER ( 0x48) // Clear RX_OVERinterrupt +#define R_IC_CLR_TX_OVER ( 0x4C) // Clear TX_OVER interrupt +#define R_IC_CLR_RD_REQ ( 0x50) // Clear RD_REQ interrupt +#define R_IC_CLR_TX_ABRT ( 0x54) // Clear TX_ABRT interrupt +#define R_IC_CLR_RX_DONE ( 0x58) // Clear RX_DONE interrupt +#define R_IC_CLR_ACTIVITY ( 0x5C) // Clear ACTIVITY interrupt +#define R_IC_CLR_STOP_DET ( 0x60) // Clear STOP_DET interrupt +#define R_IC_CLR_START_DET ( 0x64) // Clear START_DET interrupt +#define R_IC_CLR_GEN_CALL ( 0x68) // Clear GEN_CALL interrupt +#define R_IC_ENABLE ( 0x6C) // I2C Enable +#define R_IC_STATUS ( 0x70) // I2C Status + +#define R_IC_SDA_HOLD ( 0x7C) // I2C IC_DEFAULT_SDA_HOLD//16bits + +#define STAT_MST_ACTIVITY BIT5 // Master FSM Activity Status. +#define STAT_RFF BIT4 // RX FIFO is completely full +#define STAT_RFNE BIT3 // RX FIFO is not empty +#define STAT_TFE BIT2 // TX FIFO is completely empty +#define STAT_TFNF BIT1 // TX FIFO is not full + +#define R_IC_TXFLR ( 0x74) // Transmit FIFO Level Register +#define R_IC_RXFLR ( 0x78) // Receive FIFO Level Register +#define R_IC_TX_ABRT_SOURCE ( 0x80) // I2C Transmit Abort Status Register +#define R_IC_SLV_DATA_NACK_ONLY ( 0x84) // Generate SLV_DATA_NACK Register +#define R_IC_DMA_CR ( 0x88) // DMA Control Register +#define R_IC_DMA_TDLR ( 0x8C) // DMA Transmit Data Level +#define R_IC_DMA_RDLR ( 0x90) // DMA Receive Data Level +#define R_IC_SDA_SETUP ( 0x94) // I2C SDA Setup Register +#define R_IC_ACK_GENERAL_CALL ( 0x98) // I2C ACK General Call Register +#define R_IC_ENABLE_STATUS ( 0x9C) // I2C Enable Status Register +#define R_IC_COMP_PARAM ( 0xF4) // Component Parameter Register +#define R_IC_COMP_VERSION ( 0xF8) // Component Version ID +#define R_IC_COMP_TYPE ( 0xFC) // Component Type + +#define I2C_SS_SCL_HCNT_VALUE_100M 0x1DD +#define I2C_SS_SCL_LCNT_VALUE_100M 0x1E4 +#define I2C_FS_SCL_HCNT_VALUE_100M 0x54 +#define I2C_FS_SCL_LCNT_VALUE_100M 0x9a +#define I2C_HS_SCL_HCNT_VALUE_100M 0x7 +#define I2C_HS_SCL_LCNT_VALUE_100M 0xE + +#define IC_TAR_10BITADDR_MASTER BIT12 +#define FIFO_SIZE 32 +#define R_IC_INTR_STAT ( 0x2C) // I2c Inetrrupt Status +#define R_IC_INTR_MASK ( 0x30) // I2c Interrupt Mask +#define I2C_INTR_GEN_CALL BIT11 // General call received +#define I2C_INTR_START_DET BIT10 +#define I2C_INTR_STOP_DET BIT9 +#define I2C_INTR_ACTIVITY BIT8 +#define I2C_INTR_TX_ABRT BIT6 // Set on NACK +#define I2C_INTR_TX_EMPTY BIT4 +#define I2C_INTR_TX_OVER BIT3 +#define I2C_INTR_RX_FULL BIT2 // Data bytes in RX FIFO over threshold +#define I2C_INTR_RX_OVER BIT1 +#define I2C_INTR_RX_UNDER BIT0 + +#define R_PCH_LPIO_I2C_MEM_RESETS 0x804 // Software Reset +#define B_PCH_LPIO_I2C_MEM_RESETS_FUNC BIT1 // Function Clock Domain Reset +#define B_PCH_LPIO_I2C_MEM_RESETS_APB BIT0 // APB Domain Reset +#define R_PCH_LPSS_I2C_MEM_PCP 0x800 // Private Clock Parameters + +#endif \ No newline at end of file -- cgit v1.2.3