/** @file I2C PEI Lib Instance. Copyright (c) 1999- 2015, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "I2CDelayPei.h" #include "I2CIoLibPei.h" #include "I2CAccess.h" #include "I2CLibPei.h" #include #include #include #include #include #include #include #define LPSS_PCI_DEVICE_NUMBER 8 #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 #define PEI_TEPM_LPSS_DMA_BAR 0xFE900000 #define PEI_TEPM_LPSS_I2C0_BAR 0xFE910000 #define PCI_CONFIG_SPACE_SIZE 0x10000 EFI_GUID mI2CPeiInitGuid = { 0x96DED71A, 0xB9E7, 0x4EAD, 0x96, 0x2C, 0x01, 0x69, 0x3C, 0xED, 0x2A, 0x64 }; UINT16 I2CGPIO[]= { // // 19.1.6 I2C0 // I2C0_SDA-OD-O - write 0x2003CC81 to IOBASE + 0x0210 // I2C0_SCL-OD-O - write 0x2003CC81 to IOBASE + 0x0200 // 0x0210, 0x0200, // // 19.1.7 I2C1 // I2C1_SDA-OD-O/I - write 0x2003CC81 to IOBASE + 0x01F0 // I2C1_SCL-OD-O/I - write 0x2003CC81 to IOBASE + 0x01E0 // 0x01F0, 0x01E0, // // 19.1.8 I2C2 // I2C2_SDA-OD-O/I - write 0x2003CC81 to IOBASE + 0x01D0 // I2C2_SCL-OD-O/I - write 0x2003CC81 to IOBASE + 0x01B0 // 0x01D0, 0x01B0, // // 19.1.9 I2C3 // I2C3_SDA-OD-O/I - write 0x2003CC81 to IOBASE + 0x0190 // I2C3_SCL-OD-O/I - write 0x2003CC81 to IOBASE + 0x01C0 // 0x0190, 0x01C0, // // 19.1.10 I2C4 // I2C4_SDA-OD-O/I - write 0x2003CC81 to IOBASE + 0x01A0 // I2C4_SCL-OD-O/I - write 0x2003CC81 to IOBASE + 0x0170 // 0x01A0, 0x0170, // // 19.1.11 I2C5 // I2C5_SDA-OD-O/I - write 0x2003CC81 to IOBASE + 0x0150 // I2C5_SCL-OD-O/I - write 0x2003CC81 to IOBASE + 0x0140 // 0x0150, 0x0140, // // 19.1.12 I2C6 // I2C6_SDA-OD-O/I - write 0x2003CC81 to IOBASE + 0x0180 // I2C6_SCL-OD-O/I - write 0x2003CC81 to IOBASE + 0x0160 // 0x0180, 0x0160 }; /** Constructor of this library. @param VOID @return EFI_SUCCESS **/ EFI_STATUS EFIAPI IntelI2CPeiLibConstructor ( IN EFI_PEI_FILE_HANDLE FileHandle, IN CONST EFI_PEI_SERVICES **PeiServices ) { UINTN Index; for (Index = 0; Index < sizeof(I2CGPIO)/sizeof(UINT16); Index ++) { I2CLibPeiMmioWrite32(IO_BASE_ADDRESS+I2CGPIO[Index], 0x2003CC81); } return EFI_SUCCESS; } /** Programe all I2C controllers on LPSS. I2C0 is function 1 of LPSS. I2C1 is function 2 of LPSS, etc.. @param VOID @return EFI_SUCCESS **/ EFI_STATUS ProgramPciLpssI2C ( VOID ) { UINT32 PmcBase; UINT32 DevID; UINTN PciMmBase=0; UINTN Index; UINTN Bar0; UINTN Bar1; DEBUG ((EFI_D_INFO, "Pei ProgramPciLpssI2C() Start\n")); // // Set the VLV Function Disable Register to ZERO // PmcBase = I2CLibPeiMmioRead32(PciD31F0RegBase + R_PCH_LPC_PMC_BASE) & B_PCH_LPC_PMC_BASE_BAR; if(I2CLibPeiMmioRead32(PmcBase + R_PCH_PMC_FUNC_DIS)& (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)) { I2CLibPeiMmioWrite32( PmcBase+R_PCH_PMC_FUNC_DIS, I2CLibPeiMmioRead32(PmcBase + R_PCH_PMC_FUNC_DIS)& \ ~(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() enable all I2C controllers\n")); } for(Index = 0; Index < LPSS_PCI_DEVICE_NUMBER; Index ++) { PciMmBase = MmPciAddress ( 0, DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_LPSS_I2C, Index, 0 ); DevID = I2CLibPeiMmioRead32(PciMmBase); Bar0 = PEI_TEPM_LPSS_DMA_BAR + (Index * PCI_CONFIG_SPACE_SIZE); Bar1 = Bar0 + 0x8000; DEBUG((EFI_D_ERROR, "Program Pci Lpss I2C Device Function=%x DevID=%08x\n", Index, DevID)); // // Check if device present // if (DevID != 0xFFFFFFFF) { if(!(I2CLibPeiMmioRead32 (PciMmBase + R_PCH_LPSS_I2C_STSCMD) & B_PCH_LPSS_I2C_STSCMD_MSE)) { // // Program BAR 0 // I2CLibPeiMmioWrite32((UINTN) (PciMmBase + R_PCH_LPSS_I2C_BAR), (UINT32)(Bar0 & B_PCH_LPSS_I2C_BAR_BA)); DEBUG ((EFI_D_ERROR, "I2CBaseAddress1 = 0x%x \n",I2CLibPeiMmioRead32 (PciMmBase+R_PCH_LPSS_I2C_BAR))); // // Program BAR 1 // I2CLibPeiMmioWrite32 ((UINTN)(PciMmBase + R_PCH_LPSS_I2C_BAR1), (UINT32)(Bar1 & B_PCH_LPSS_I2C_BAR1_BA)); DEBUG ((EFI_D_ERROR, "I2CBaseAddress1 = 0x%x \n",I2CLibPeiMmioRead32(PciMmBase+R_PCH_LPSS_I2C_BAR1))); // // Bus Master Enable & Memory Space Enable // I2CLibPeiMmioWrite32((UINTN) (PciMmBase + R_PCH_LPSS_I2C_STSCMD), (UINT32)(B_PCH_LPSS_I2C_STSCMD_BME | B_PCH_LPSS_I2C_STSCMD_MSE)); } // // Release Resets // I2CLibPeiMmioWrite32 (Bar0 + R_PCH_LPIO_I2C_MEM_RESETS, (B_PCH_LPIO_I2C_MEM_RESETS_FUNC | B_PCH_LPIO_I2C_MEM_RESETS_APB)); // // Activate Clocks // I2CLibPeiMmioWrite32 (Bar0 + R_PCH_LPSS_I2C_MEM_PCP, 0x80020003);//No use for A0 DEBUG ((EFI_D_INFO, "ProgramPciLpssI2C() Programmed()\n")); } } DEBUG ((EFI_D_INFO, "Pei ProgramPciLpssI2C() End\n")); return EFI_SUCCESS; } /** Disable I2C Bus. @param I2cControllerIndex Index of I2C controller. @return EFI_SUCCESS **/ EFI_STATUS I2cDisable ( IN UINT8 I2cControllerIndex ) { UINTN I2CBaseAddress; UINT32 NumTries = 10000; // 0.1 seconds I2CBaseAddress = (UINT32) PEI_TEPM_LPSS_I2C0_BAR + I2cControllerIndex * PCI_CONFIG_SPACE_SIZE; I2CLibPeiMmioWrite16 (I2CBaseAddress + R_IC_ENABLE, 0); while (0 != ( I2CLibPeiMmioRead16 (I2CBaseAddress + R_IC_ENABLE_STATUS ) & 1)) { MicroSecondDelay (10); NumTries --; if(0 == NumTries) return EFI_NOT_READY; } return EFI_SUCCESS; } /** Enable I2C Bus. @param I2cControllerIndex Index of I2C controller. @return EFI_SUCCESS **/ EFI_STATUS I2cEnable ( IN UINT8 I2cControllerIndex ) { UINTN I2CBaseAddress; UINT32 NumTries = 10000; // 0.1 seconds I2CBaseAddress = (UINT32) PEI_TEPM_LPSS_I2C0_BAR+ I2cControllerIndex * PCI_CONFIG_SPACE_SIZE; I2CLibPeiMmioWrite16 (I2CBaseAddress + R_IC_ENABLE, 1); while (0 == ( I2CLibPeiMmioRead16 ( I2CBaseAddress + R_IC_ENABLE_STATUS ) & 1)) { MicroSecondDelay (10); NumTries --; if(0 == NumTries) return EFI_NOT_READY; } return EFI_SUCCESS; } /** Set the I2C controller bus clock frequency. @param[in] This Address of the library's I2C context structure @param[in] PlatformData Address of the platform configuration data @param[in] BusClockHertz New I2C bus clock frequency in Hertz @retval RETURN_SUCCESS The bus frequency was set successfully. @retval RETURN_UNSUPPORTED The controller does not support this frequency. **/ EFI_STATUS I2cBusFrequencySet ( IN UINTN I2CBaseAddress, IN UINTN BusClockHertz, IN UINT16 *I2cMode ) { DEBUG((EFI_D_INFO,"InputFreq BusClockHertz: %d\r\n",BusClockHertz)); *I2cMode = B_IC_RESTART_EN | B_IC_SLAVE_DISABLE | B_MASTER_MODE; // // Set the 100 KHz clock divider // // From Table 10 of the I2C specification // // High: 4.00 uS // Low: 4.70 uS // I2CLibPeiMmioWrite16 ( I2CBaseAddress + R_IC_SS_SCL_HCNT, (UINT16)0x214 ); I2CLibPeiMmioWrite16 ( I2CBaseAddress + R_IC_SS_SCL_LCNT, (UINT16)0x272 ); // // Set the 400 KHz clock divider // // From Table 10 of the I2C specification // // High: 0.60 uS // Low: 1.30 uS // I2CLibPeiMmioWrite16 ( I2CBaseAddress + R_IC_FS_SCL_HCNT, (UINT16)0x50 ); I2CLibPeiMmioWrite16 ( I2CBaseAddress + R_IC_FS_SCL_LCNT, (UINT16)0xAD ); switch ( BusClockHertz ) { case 100 * 1000: I2CLibPeiMmioWrite32 ( I2CBaseAddress + R_IC_SDA_HOLD, (UINT16)0x40);//100K *I2cMode |= V_SPEED_STANDARD; break; case 400 * 1000: I2CLibPeiMmioWrite32 ( I2CBaseAddress + R_IC_SDA_HOLD, (UINT16)0x32);//400K *I2cMode |= V_SPEED_FAST; break; default: I2CLibPeiMmioWrite32 ( I2CBaseAddress + R_IC_SDA_HOLD, (UINT16)0x09);//3.4M *I2cMode |= V_SPEED_HIGH; } 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 ( UINT8 I2cControllerIndex, UINT16 SlaveAddress ) { EFI_STATUS Status; UINT32 NumTries = 0; UINTN I2CBaseAddress; UINT16 I2cMode; UINTN PciMmBase=0; PciMmBase = MmPciAddress ( 0, DEFAULT_PCI_BUS_NUMBER_PCH, PCI_DEVICE_NUMBER_PCH_LPSS_I2C, (I2cControllerIndex + 1), 0 ); I2CBaseAddress = I2CLibPeiMmioRead32 (PciMmBase+R_PCH_LPSS_I2C_BAR); // // Verify the parameters // if (1023 < SlaveAddress ) { Status = EFI_INVALID_PARAMETER; DEBUG((EFI_D_INFO,"I2cStartRequest Exit with Status %r\r\n", Status)); return Status; } if(I2CBaseAddress == (PEI_TEPM_LPSS_I2C0_BAR + I2cControllerIndex * PCI_CONFIG_SPACE_SIZE)) { return EFI_SUCCESS; } ProgramPciLpssI2C(); I2CBaseAddress = (UINT32) (PEI_TEPM_LPSS_I2C0_BAR + I2cControllerIndex * PCI_CONFIG_SPACE_SIZE); DEBUG ((EFI_D_ERROR, "I2CBaseAddress = 0x%x \n",I2CBaseAddress)); NumTries = 10000; // 1 seconds while ((1 == ( I2CLibPeiMmioRead32 ( I2CBaseAddress + R_IC_STATUS) & STAT_MST_ACTIVITY ))) { MicroSecondDelay(10); NumTries --; if(0 == NumTries) return EFI_DEVICE_ERROR; } Status = I2cDisable (I2cControllerIndex); DEBUG((EFI_D_INFO, "I2cDisable Status = %r\r\n", Status)); I2cBusFrequencySet(I2CBaseAddress, 400 * 1000, &I2cMode);//Set I2cMode I2CLibPeiMmioWrite16(I2CBaseAddress + R_IC_INTR_MASK, 0x0); if (0x7F < SlaveAddress) { SlaveAddress = (SlaveAddress & 0x3ff ) | IC_TAR_10BITADDR_MASTER; } I2CLibPeiMmioWrite16 (I2CBaseAddress + R_IC_TAR, (UINT16) SlaveAddress ); I2CLibPeiMmioWrite16 (I2CBaseAddress + R_IC_RX_TL, 0); I2CLibPeiMmioWrite16 (I2CBaseAddress + R_IC_TX_TL, 0 ); I2CLibPeiMmioWrite16 (I2CBaseAddress + R_IC_CON, I2cMode); Status = I2cEnable(I2cControllerIndex); DEBUG((EFI_D_INFO, "I2cEnable Status = %r\r\n", Status)); I2CLibPeiMmioRead16 ( I2CBaseAddress + 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 @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; UINTN I2CBaseAddress; I2CBaseAddress = (UINT32)(PEI_TEPM_LPSS_I2C0_BAR + I2cControllerIndex * PCI_CONFIG_SPACE_SIZE); Status = EFI_SUCCESS; I2CInit(I2cControllerIndex, SlaveAddress); 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 = I2CLibPeiMmioRead16 (I2CBaseAddress + R_IC_RawIntrStat ); if ( 0 != (RawIntrStat & I2C_INTR_TX_ABRT )) { I2CLibPeiMmioRead16 ( I2CBaseAddress + 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 = I2CLibPeiMmioRead16 ( I2CBaseAddress + R_IC_STATUS ); if ( 0 != ( I2cStatus & STAT_RFNE )) { ReceiveData = I2CLibPeiMmioRead16 ( I2CBaseAddress + R_IC_DATA_CMD ); *ReadBuffer++ = (UINT8)ReceiveData; DEBUG((EFI_D_INFO,"MmioRead32 ,1 byte 0x:%x is received\r\n",ReceiveData)); } if(ReceiveDataEnd==ReceiveRequest) { // // Waiting the last request to get data and make (ReceiveDataEnd > ReadBuffer) =TRUE. // continue; } // // Wait until a read request will fit // if ( 0 == ( I2cStatus & STAT_TFNF )) { MicroSecondDelay ( 10 ); continue; } // // Issue the next read request // if(End && Start ) { I2CLibPeiMmioWrite16 ( I2CBaseAddress + R_IC_DATA_CMD, B_READ_CMD|B_CMD_RESTART|B_CMD_STOP); } else if (!End && Start ) { I2CLibPeiMmioWrite16 ( I2CBaseAddress + R_IC_DATA_CMD, B_READ_CMD|B_CMD_RESTART); } else if (End && !Start ) { I2CLibPeiMmioWrite16 ( I2CBaseAddress + R_IC_DATA_CMD, B_READ_CMD|B_CMD_STOP); } else if (!End && !Start ) { I2CLibPeiMmioWrite16 ( I2CBaseAddress + R_IC_DATA_CMD, B_READ_CMD); } 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 @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; UINTN I2CBaseAddress; I2CBaseAddress = (UINT32)PEI_TEPM_LPSS_I2C0_BAR+ I2cControllerIndex * PCI_CONFIG_SPACE_SIZE; Status = EFI_SUCCESS; I2CInit(I2cControllerIndex, SlaveAddress); TransmitEnd = &WriteBuffer [WriteBytes]; if( WriteBytes ) { DEBUG((EFI_D_INFO,"Write: --------------%d bytes to TX\r\n", TransmitEnd - WriteBuffer)); while ( TransmitEnd > WriteBuffer) { I2cStatus = I2CLibPeiMmioRead16 (I2CBaseAddress + R_IC_STATUS); RawIntrStat = I2CLibPeiMmioRead16 (I2CBaseAddress + R_IC_RawIntrStat); if ( 0 != (RawIntrStat & I2C_INTR_TX_ABRT)) { I2CLibPeiMmioRead16 (I2CBaseAddress + 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)) { continue; } if(End && Start) { I2CLibPeiMmioWrite16 (I2CBaseAddress + R_IC_DATA_CMD, (*WriteBuffer++) | B_CMD_RESTART | B_CMD_STOP); } else if (!End && Start ) { I2CLibPeiMmioWrite16 (I2CBaseAddress + R_IC_DATA_CMD, (*WriteBuffer++) | B_CMD_RESTART); } else if (End && !Start ) { I2CLibPeiMmioWrite16 (I2CBaseAddress + R_IC_DATA_CMD, (*WriteBuffer++) | B_CMD_STOP); } else if (!End && !Start ) { I2CLibPeiMmioWrite16 (I2CBaseAddress + 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 ); } } if(EFI_ERROR(Status)) { DEBUG((EFI_D_INFO,"I2cStartRequest Exit with Status %r\r\n",Status)); } 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_ERROR, "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_ERROR, "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; }