From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=134.134.136.65; helo=mga03.intel.com; envelope-from=david.wei@intel.com; receiver=edk2-devel@lists.01.org Received: from mga03.intel.com (mga03.intel.com [134.134.136.65]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 060EB210C27A8 for ; Wed, 25 Jul 2018 20:54:47 -0700 (PDT) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga007.fm.intel.com ([10.253.24.52]) by orsmga103.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 25 Jul 2018 20:54:47 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.51,403,1526367600"; d="scan'208";a="57240453" Received: from zwei4-mobl1.ccr.corp.intel.com ([10.239.193.156]) by fmsmga007.fm.intel.com with ESMTP; 25 Jul 2018 20:54:45 -0700 From: zwei4 To: edk2-devel@lists.01.org Cc: David Wei , Kelly Steele , Mike Wu , Mang Guo Date: Thu, 26 Jul 2018 11:54:40 +0800 Message-Id: <20180726035440.15832-1-david.wei@intel.com> X-Mailer: git-send-email 2.14.1.windows.1 Subject: [Patch][edk2-platforms/devel-IntelAtomProcessorE3900] Add I2C Library. X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.27 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 26 Jul 2018 03:54:48 -0000 Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: David Wei CC: Kelly Steele CC: Mike Wu CC: Mang Guo --- .../SouthCluster/Include/Library/I2CLib.h | 4 +- .../SouthCluster/Include/ScRegs/RegsI2c.h | 31 +- .../SouthCluster/Library/I2CLib/I2CLib.c | 719 +++++++++++++++++++++ .../SouthCluster/Library/I2CLib/I2CLib.inf | 50 ++ .../SouthCluster/Library/I2CLibPei/I2CLibPei.c | 6 +- 5 files changed, 802 insertions(+), 8 deletions(-) create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Library/I2CLib/I2CLib.c create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Library/I2CLib/I2CLib.inf diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Include/Library/I2CLib.h b/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Include/Library/I2CLib.h index b897cb9dc4..0c50a3d9a5 100644 --- a/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Include/Library/I2CLib.h +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Include/Library/I2CLib.h @@ -1,7 +1,7 @@ /** @file Register Definitions for I2C Library. - Copyright (c) 1999 - 2017, Intel Corporation. All rights reserved.
+ Copyright (c) 1999 - 2018, 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 @@ -30,7 +30,7 @@ **/ EFI_STATUS ProgramPciLpssI2C ( - VOID + IN UINT8 BusNo ); /** diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Include/ScRegs/RegsI2c.h b/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Include/ScRegs/RegsI2c.h index 5fc758dd07..8a7463b538 100644 --- a/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Include/ScRegs/RegsI2c.h +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Include/ScRegs/RegsI2c.h @@ -106,10 +106,10 @@ #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 I2C_ENABLE_ABORT BIT1 +#define I2C_ENABLE_ENABLE BIT0 #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 @@ -118,7 +118,24 @@ #define R_IC_TXFLR (0x74) ///< Transmit FIFO Level Register #define R_IC_RXFLR (0x78) ///< Receive FIFO Level Register +#define R_IC_SDA_HOLD (0x7C) ///< I2C IC_DEFAULT_SDA_HOLD//16bits #define R_IC_TX_ABRT_SOURCE (0x80) ///< I2C Transmit Abort Status Register +#define I2C_ABRT_SLVRD_INTX BIT15 +#define I2C_ABRT_SLV_ARBLOST BIT14 +#define I2C_ABRT_SLVFLUSH_TXFIFO BIT13 +#define I2C_ARB_LOST BIT12 +#define I2C_ABRT_MASTER_DIS BIT11 +#define I2C_ABRT_10B_RD_NORSTRT BIT10 +#define I2C_ABRT_SBYTE_NORSTRT BIT9 +#define I2C_ABRT_HS_NORSTRT BIT8 +#define I2C_ABRT_SBYTE_ACKDET BIT7 +#define I2C_ABRT_HS_ACKDET BIT6 +#define I2C_ABRT_GCALL_READ BIT5 +#define I2C_ABRT_GCALL_NOACK BIT4 +#define I2C_ABRT_TXDATA_NOACK BIT3 +#define I2C_ABRT_10ADDR2_NOACK BIT2 +#define I2C_ABRT_10ADDR1_NOACK BIT1 +#define I2C_ABRT_7B_ADDR_NOACK BIT0 #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 @@ -130,7 +147,15 @@ #define R_IC_COMP_VERSION (0xF8) ///< Component Version ID #define R_IC_COMP_TYPE (0xFC) ///< Component Type -#define R_IC_CLK_GATE (0xC0) ///< Clock Gate +#define R_IC_RESET_CONTROL (0x204) ///< Reset control +#define I2C_RESET_CONTROLLER (BIT0 | BIT1) +#define I2C_RESET_IDMA (BIT2) + +#define R_IC_CLK_GATE (0x238) ///< Clock Gate +#define I2C_FORCE_CLOCK_ON (BIT0 | BIT1) +#define I2C_FORCE_CLOCK_OFF (BIT1) +#define I2C_FORCE_IDMA_CLOCK_ON (BIT2 | BIT3) +#define I2C_FORCE_IDMA_CLOCK_OFF (BIT3) #define I2C_SS_SCL_HCNT_VALUE_100M 0x1DD #define I2C_SS_SCL_LCNT_VALUE_100M 0x1E4 diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Library/I2CLib/I2CLib.c b/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Library/I2CLib/I2CLib.c new file mode 100644 index 0000000000..f205a6ce44 --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Library/I2CLib/I2CLib.c @@ -0,0 +1,719 @@ +/** @file + Dxe library for I2C bus driver. + +@copyright + Copyright (c) 1999 - 2018 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. +**/ + +#include +#include +#include +#include +#include +#include +#include + +#pragma pack(push, 1) +typedef struct _LPSS_PCI_DEVICE_INFO { + UINTN Segment; + UINTN BusNum; + UINTN DeviceNum; + UINTN FunctionNum; + UINTN Bar0; + UINTN Bar1; +} LPSS_PCI_DEVICE_INFO; + +typedef enum { + Standard_Speed = 1, + Fast_Speed = 2, + High_Speed = 3, +} I2C_SPEED; + +typedef struct _LPSS_I2C_CLOCK_SCL_INFO { + UINT8 I2c_Speed; + UINT16 SS_SCL_HCNT; + UINT16 SS_SCL_LCNT; + UINT16 FS_SCL_HCNT; + UINT16 FS_SCL_LCNT; + UINT16 HS_SCL_HCNT; + UINT16 HS_SCL_LCNT; +} LPSS_I2C_CLOCK_SCL_INFO; +#pragma pack(pop) + +LPSS_PCI_DEVICE_INFO mLpssPciDeviceList[] = { + {0, DEFAULT_PCI_BUS_NUMBER_SC, PCI_DEVICE_NUMBER_LPSS_I2C0, PCI_FUNCTION_NUMBER_LPSS_I2C0, LPSS_I2C0_TMP_BAR0 + (LPSS_I2C_TMP_BAR0_DELTA*0), LPSS_I2C0_TMP_BAR0 + (LPSS_I2C_TMP_BAR0_DELTA*0) + LPSS_I2C_TMP_BAR1_OFFSET}, + {0, DEFAULT_PCI_BUS_NUMBER_SC, PCI_DEVICE_NUMBER_LPSS_I2C0, PCI_FUNCTION_NUMBER_LPSS_I2C1, LPSS_I2C0_TMP_BAR0 + (LPSS_I2C_TMP_BAR0_DELTA*1), LPSS_I2C0_TMP_BAR0 + (LPSS_I2C_TMP_BAR0_DELTA*1) + LPSS_I2C_TMP_BAR1_OFFSET}, + {0, DEFAULT_PCI_BUS_NUMBER_SC, PCI_DEVICE_NUMBER_LPSS_I2C0, PCI_FUNCTION_NUMBER_LPSS_I2C2, LPSS_I2C0_TMP_BAR0 + (LPSS_I2C_TMP_BAR0_DELTA*2), LPSS_I2C0_TMP_BAR0 + (LPSS_I2C_TMP_BAR0_DELTA*2) + LPSS_I2C_TMP_BAR1_OFFSET}, + {0, DEFAULT_PCI_BUS_NUMBER_SC, PCI_DEVICE_NUMBER_LPSS_I2C0, PCI_FUNCTION_NUMBER_LPSS_I2C3, LPSS_I2C0_TMP_BAR0 + (LPSS_I2C_TMP_BAR0_DELTA*3), LPSS_I2C0_TMP_BAR0 + (LPSS_I2C_TMP_BAR0_DELTA*3) + LPSS_I2C_TMP_BAR1_OFFSET}, + {0, DEFAULT_PCI_BUS_NUMBER_SC, PCI_DEVICE_NUMBER_LPSS_I2C1, PCI_FUNCTION_NUMBER_LPSS_I2C4, LPSS_I2C0_TMP_BAR0 + (LPSS_I2C_TMP_BAR0_DELTA*4), LPSS_I2C0_TMP_BAR0 + (LPSS_I2C_TMP_BAR0_DELTA*4) + LPSS_I2C_TMP_BAR1_OFFSET}, + {0, DEFAULT_PCI_BUS_NUMBER_SC, PCI_DEVICE_NUMBER_LPSS_I2C1, PCI_FUNCTION_NUMBER_LPSS_I2C5, LPSS_I2C0_TMP_BAR0 + (LPSS_I2C_TMP_BAR0_DELTA*5), LPSS_I2C0_TMP_BAR0 + (LPSS_I2C_TMP_BAR0_DELTA*5) + LPSS_I2C_TMP_BAR1_OFFSET}, + {0, DEFAULT_PCI_BUS_NUMBER_SC, PCI_DEVICE_NUMBER_LPSS_I2C1, PCI_FUNCTION_NUMBER_LPSS_I2C6, LPSS_I2C0_TMP_BAR0 + (LPSS_I2C_TMP_BAR0_DELTA*6), LPSS_I2C0_TMP_BAR0 + (LPSS_I2C_TMP_BAR0_DELTA*6) + LPSS_I2C_TMP_BAR1_OFFSET}, + {0, DEFAULT_PCI_BUS_NUMBER_SC, PCI_DEVICE_NUMBER_LPSS_I2C1, PCI_FUNCTION_NUMBER_LPSS_I2C7, LPSS_I2C0_TMP_BAR0 + (LPSS_I2C_TMP_BAR0_DELTA*7), LPSS_I2C0_TMP_BAR0 + (LPSS_I2C_TMP_BAR0_DELTA*7) + LPSS_I2C_TMP_BAR1_OFFSET}, +}; + +#define LPSS_PCI_DEVICE_NUMBER sizeof(mLpssPciDeviceList)/sizeof(LPSS_PCI_DEVICE_INFO) + +LPSS_I2C_CLOCK_SCL_INFO mLPSS_I2C_CLOCK_SCL_INFO[] = { + {Fast_Speed, 0x244, 0x2D0, 0x64, 0xC8, 0x06, 0x13}, + {Fast_Speed, 0x244, 0x2D0, 0x64, 0xC8, 0x06, 0x13}, + {Fast_Speed, 0x244, 0x2D0, 0x64, 0xC8, 0x06, 0x13}, + {High_Speed, 0x244, 0x2DA, 0x1E, 0x3C, 0x06, 0x13}, + {High_Speed, 0x244, 0x2DA, 0x1E, 0x50, 0x06, 0x13}, + {Fast_Speed, 0x244, 0x2D0, 0x69, 0xC8, 0x06, 0x13}, + {Fast_Speed, 0x244, 0x2D0, 0x69, 0xC8, 0x06, 0x13}, + {Fast_Speed, 0x244, 0x2D0, 0x70, 0xC8, 0x06, 0x13}, +}; + +#define LPSS_I2C_CLOCK_SCL_INFO_NUMBER sizeof(mLPSS_I2C_CLOCK_SCL_INFO)/sizeof(LPSS_I2C_CLOCK_SCL_INFO) + + +/** + Program LPSS I2C PCI controller's BAR0 and enable memory decode. + + @param[in] BusNo - I2C Bus number to which the I2C device has been connected + + @retval EFI_SUCCESS - I2C controller's BAR0 is programmed and memory decode enabled. + @retval EFI_NOT_READY - I2C controller's is not exist or its function has been disabled. +**/ +EFI_STATUS +ProgramPciLpssI2C ( + IN UINT8 BusNo + ) +{ + UINTN PciMmBase=0; + UINT32 I2CBar0; + UINT32 I2CBar1; + UINT32 PmcBase; + UINT32 D32; + UINT32 I2cPortDisable[] = { + B_PMC_FUNC_DIS_LPSS_I2C0, + B_PMC_FUNC_DIS_LPSS_I2C1, + B_PMC_FUNC_DIS_LPSS_I2C2, + B_PMC_FUNC_DIS_LPSS_I2C3, + B_PMC_FUNC_DIS_LPSS_I2C4, + B_PMC_FUNC_DIS_LPSS_I2C5, + B_PMC_FUNC_DIS_LPSS_I2C6, + B_PMC_FUNC_DIS_LPSS_I2C7 + }; + + DEBUG ((DEBUG_INFO, "ProgramPciLpssI2C() Start\n")); + + // + // Check PMC disable register + // + PmcBase = PMC_BASE_ADDRESS; + D32 = MmioRead32 (PmcBase + R_PMC_FUNC_DIS); + + if (D32 == 0xFFFFFFFF) { + DEBUG ((DEBUG_INFO, "ProgramPciLpssI2C() PMC disable register not available. [%08x]\n", PMC_BASE_ADDRESS)); + } else { + if ((D32 & I2cPortDisable[BusNo]) != 0) { + // This I2C port is disabled. Turn it on. + D32 &= ~I2cPortDisable[BusNo]; + MmioWrite32 (PmcBase + R_PMC_FUNC_DIS, D32); + DEBUG ((DEBUG_INFO, "ProgramPciLpssI2C() enable I2C controller #%x\n", BusNo)); + // Make sure it took. + if (D32 != MmioRead32 (PmcBase + R_PMC_FUNC_DIS)) { + DEBUG ((DEBUG_ERROR, "ProgramPciLpssI2C() failed to enable I2C controller #%x [%08x:%08x]\n", BusNo, D32, MmioRead32 (PmcBase + R_PMC_FUNC_DIS))); + return EFI_DEVICE_ERROR; + } + } + } + + DEBUG ((DEBUG_INFO, "ProgramPciLpssI2C()------------BusNo=%x\n", BusNo)); + + PciMmBase = MmPciAddress ( + mLpssPciDeviceList[BusNo].Segment, + mLpssPciDeviceList[BusNo].BusNum, + mLpssPciDeviceList[BusNo].DeviceNum, + mLpssPciDeviceList[BusNo].FunctionNum, + 0 + ); + DEBUG ((DEBUG_INFO, "Program Pci Lpss I2C Device %x %x %x PciMmBase:%x\n", \ + mLpssPciDeviceList[BusNo].BusNum, \ + mLpssPciDeviceList[BusNo].DeviceNum, \ + mLpssPciDeviceList[BusNo].FunctionNum, PciMmBase)); + + // + // Check if device present + // + if (MmioRead32 (PciMmBase) != 0xFFFFFFFF) { + if ((MmioRead32 (PciMmBase + R_LPSS_IO_STSCMD) & B_LPSS_IO_STSCMD_MSE)) { + // + // In Pei stage, we always disable Bus master, and memory space enabling for BAR re-programming + // In DXE stage, will read existing BAR value instead of re-programming + // + I2CBar0 = MmioRead32 (PciMmBase + R_LPSS_IO_BAR) & B_LPSS_IO_BAR_BA; + I2CBar1 = MmioRead32 (PciMmBase + R_LPSS_IO_BAR1) & B_LPSS_IO_BAR_BA; + if ((I2CBar0 != (UINT32)mLpssPciDeviceList[BusNo].Bar0) || (I2CBar1 != (UINT32)mLpssPciDeviceList[BusNo].Bar1)) { + mLpssPciDeviceList[BusNo].Bar0 = MmioRead32 (PciMmBase + R_LPSS_IO_BAR) & B_LPSS_IO_BAR_BA; // get the address allocated. + mLpssPciDeviceList[BusNo].Bar1 = MmioRead32 (PciMmBase + R_LPSS_IO_BAR1) & B_LPSS_IO_BAR_BA; + DEBUG ((DEBUG_INFO, "Get bar0:0x%x bar1:0x%x\n", mLpssPciDeviceList[BusNo].Bar0, mLpssPciDeviceList[BusNo].Bar1)); + } + } else { + // + // Program BAR 0 + // + ASSERT (((mLpssPciDeviceList[BusNo].Bar0 & B_LPSS_IO_BAR_BA) == mLpssPciDeviceList[BusNo].Bar0) && (mLpssPciDeviceList[BusNo].Bar0 != 0)); + MmioWrite32 ((UINTN) (PciMmBase + R_LPSS_IO_BAR), (UINT32) (mLpssPciDeviceList[BusNo].Bar0 & B_LPSS_IO_BAR_BA)); + // + // Program BAR 1 + // + ASSERT (((mLpssPciDeviceList[BusNo].Bar1 & B_LPSS_IO_BAR1_BA) == mLpssPciDeviceList[BusNo].Bar1) && (mLpssPciDeviceList[BusNo].Bar1 != 0)); + MmioWrite32 ((UINTN) (PciMmBase + R_LPSS_IO_BAR1), (UINT32) (mLpssPciDeviceList[BusNo].Bar1 & B_LPSS_IO_BAR1_BA)); + // + // Bus Master Enable & Memory Space Enable + // + MmioOr32 ((UINTN) (PciMmBase + R_LPSS_IO_STSCMD), (UINT32) (B_LPSS_IO_STSCMD_BME | B_LPSS_IO_STSCMD_MSE)); + ASSERT (MmioRead32 (mLpssPciDeviceList[BusNo].Bar0) != 0xFFFFFFFF); + } + + // + // Release Resets + // + MmioWrite32 (mLpssPciDeviceList[BusNo].Bar0 + R_LPSS_IO_MEM_RESETS, B_LPSS_IO_MEM_HC_RESET_REL | B_LPSS_IO_MEM_iDMA_RESET_REL); + + DEBUG ((DEBUG_INFO, "ProgramPciLpssI2C() Programmed()\n")); + return EFI_SUCCESS; + } else { + DEBUG ((DEBUG_ERROR, "Pci Lpss I2C Device %x %x %x is not existing!\n", + mLpssPciDeviceList[BusNo].BusNum, + mLpssPciDeviceList[BusNo].DeviceNum, + mLpssPciDeviceList[BusNo].FunctionNum)); + + return EFI_NOT_READY; + } +} + +/** + Disable I2C host controller + + @param[in] I2CBaseAddress - BAR0 address of I2C host controller + + @retval EFI_SUCCESS - I2C host controller is completely inactive. + @retval EFI_NOT_READY - I2C host controller is still in an enabled state. +**/ +EFI_STATUS +I2cDisable ( + IN UINTN I2CBaseAddress + ) +{ + UINT32 NumTries = 10000; // 0.1 seconds + + MmioWrite32 (I2CBaseAddress + R_IC_ENABLE, 0); + while (0 != ( MmioRead32 ( I2CBaseAddress + R_IC_ENABLE_STATUS) & 1)) { + MicroSecondDelay (10); + NumTries --; + if (0 == NumTries) return EFI_NOT_READY; + } + return EFI_SUCCESS; +} + +/** + Enable I2C host controller + + @param[in] I2CBaseAddress - BAR0 address of I2C host controller + + @retval EFI_SUCCESS - I2C host controller is in an enabled state. + @retval EFI_NOT_READY - I2C host controller is still inactive. +**/ +EFI_STATUS +I2cEnable ( + IN UINTN I2CBaseAddress + ) +{ + UINT32 NumTries = 10000; // 0.1 seconds + + MmioWrite32 (I2CBaseAddress + R_IC_ENABLE, 1); + while (0 == (MmioRead32 (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. + + The software and controller do a best case effort of using the specified + frequency for the I2C bus. If the frequency does not match exactly then + the controller will use a slightly lower frequency for the I2C to avoid + exceeding the operating conditions for any of the I2C devices on the bus. + For example if 400 KHz was specified and the controller's divide network + only supports 402 KHz or 398 KHz then the controller would be set to 398 + KHz. However if the desired frequency is 400 KHz and the controller only + supports 1 MHz and 100 KHz then this routine would return EFI_UNSUPPORTED. + + @param[in] BusNo - I2C Bus number to which the I2C device has been connected + @param[in] I2CBaseAddress - BAR0 address of I2C host controller + @param[out] I2cMode - I2C operation mode. + Standard Speed: 100 KHz + Fast Speed : 400 KHz + High Speed : 3.4 MHz + + @retval EFI_SUCCESS - The bus frequency was set successfully. +**/ +EFI_STATUS +I2cBusFrequencySet ( + IN UINT8 BusNo, + IN UINTN I2CBaseAddress, + OUT UINT16 *I2cMode + ) +{ + DEBUG ((DEBUG_INFO, "I2cBusFrequencySet bus: %d\r\n", BusNo)); + ASSERT ((BusNo < LPSS_I2C_CLOCK_SCL_INFO_NUMBER)); + // + // Set the 100 KHz clock divider according to SV result and I2C spec + // + MmioWrite32 (I2CBaseAddress + R_IC_SS_SCL_HCNT, (UINT16)mLPSS_I2C_CLOCK_SCL_INFO[BusNo].SS_SCL_HCNT); + MmioWrite32 (I2CBaseAddress + R_IC_SS_SCL_LCNT, (UINT16)mLPSS_I2C_CLOCK_SCL_INFO[BusNo].SS_SCL_LCNT); + DEBUG ((DEBUG_INFO, "I2cBusFrequencySet R_IC_SS_SCL_HCNT: 0x%08X, R_IC_SS_SCL_LCNT: 0x%08X\r\n",\ + MmioRead32 (I2CBaseAddress + R_IC_SS_SCL_HCNT), MmioRead32 (I2CBaseAddress + R_IC_SS_SCL_LCNT))); + // + // Set the 400 KHz clock divider according to SV result and I2C spec + // + MmioWrite32 (I2CBaseAddress + R_IC_FS_SCL_HCNT, (UINT16)mLPSS_I2C_CLOCK_SCL_INFO[BusNo].FS_SCL_HCNT); + MmioWrite32 (I2CBaseAddress + R_IC_FS_SCL_LCNT, (UINT16)mLPSS_I2C_CLOCK_SCL_INFO[BusNo].FS_SCL_LCNT); + DEBUG ((DEBUG_INFO, "I2cBusFrequencySet R_IC_FS_SCL_HCNT: 0x%08X, R_IC_FS_SCL_LCNT: 0x%08X\r\n",\ + MmioRead32 (I2CBaseAddress + R_IC_FS_SCL_HCNT), MmioRead32 (I2CBaseAddress + R_IC_FS_SCL_LCNT))); + // + // Set the 3.4MHz clock divider according to SV result and I2C spec + // + MmioWrite32 (I2CBaseAddress + R_IC_HS_SCL_HCNT, (UINT16)mLPSS_I2C_CLOCK_SCL_INFO[BusNo].HS_SCL_HCNT); + MmioWrite32 (I2CBaseAddress + R_IC_HS_SCL_LCNT, (UINT16)mLPSS_I2C_CLOCK_SCL_INFO[BusNo].HS_SCL_LCNT); + DEBUG ((DEBUG_INFO, "I2cBusFrequencySet R_IC_HS_SCL_HCNT: 0x%08X, R_IC_HS_SCL_LCNT: 0x%08X\r\n",\ + MmioRead32 (I2CBaseAddress + R_IC_HS_SCL_HCNT), MmioRead32 (I2CBaseAddress + R_IC_HS_SCL_LCNT))); + + switch (mLPSS_I2C_CLOCK_SCL_INFO[BusNo].I2c_Speed) { + case Standard_Speed: + MmioWrite32 ( I2CBaseAddress + R_IC_SDA_HOLD, (UINT16)0x06); //100K + *I2cMode = V_SPEED_STANDARD; + DEBUG ((DEBUG_INFO, "I2cBusFrequencySet I2cMode: 0x%04X\r\n", *I2cMode)); + break; + + case Fast_Speed: + MmioWrite32 ( I2CBaseAddress + R_IC_SDA_HOLD, (UINT16)0x06); //400K + *I2cMode = V_SPEED_FAST; + DEBUG ((DEBUG_INFO, "I2cBusFrequencySet I2cMode: 0x%04X\r\n", *I2cMode)); + break; + + case High_Speed: + MmioWrite32 ( I2CBaseAddress + R_IC_SDA_HOLD, (UINT16)0x06); //3.4M + *I2cMode = V_SPEED_HIGH; + DEBUG ((DEBUG_INFO, "I2cBusFrequencySet I2cMode: 0x%04X\r\n", *I2cMode)); + break; + + default: + MmioWrite32 ( I2CBaseAddress + R_IC_SDA_HOLD, (UINT16)0x06); //400K + *I2cMode = V_SPEED_FAST; + DEBUG ((DEBUG_INFO, "I2cBusFrequencySet I2cMode: 0x%04X\r\n", *I2cMode)); + } + + // + // Select the frequency counter + // Enable restart condition, + // Enable master FSM, disable slave FSM + // + *I2cMode |= B_IC_RESTART_EN | B_IC_SLAVE_DISABLE | B_MASTER_MODE; + DEBUG ((DEBUG_INFO, "I2cBusFrequencySet R_IC_SDA_HOLD: 0x%08X\r\n", MmioRead32 (I2CBaseAddress + R_IC_SDA_HOLD))); + DEBUG ((DEBUG_INFO, "I2cBusFrequencySet I2cMode: 0x%04X\r\n", *I2cMode)); + + return EFI_SUCCESS; +} + +/** + Initializes the host controller to execute I2C commands. + + @param[in] BusNo - I2C Bus number to which the I2C device has been connected + @param[in] SlaveAddress - Slave address of the I2C device + @param[out] I2CBaseAddress - Return BAR0 address of I2C host controller + + @retval EFI_SUCCESS - Initialization on the I2C host controller completed. + @retval EFI_INVALID_PARAMETER - Invalid slave address + @retval EFI_DEVICE_ERROR - Operation failed, device error + @retval Others - Failed to initialize I2C host controller +**/ +EFI_STATUS +I2CInit ( + IN UINT8 BusNo, + IN UINT16 SlaveAddress, + OUT UINTN *I2CBaseAddress + ) +{ + EFI_STATUS Status; + UINT32 NumTries; + UINT16 I2cMode; + UINTN PciMmBase; + UINTN BaseAddress; + + // + // Verify the parameters + // + if (1023 < SlaveAddress) { + Status = EFI_INVALID_PARAMETER; + DEBUG ((DEBUG_INFO, "I2cStartRequest Exit with Status %r\r\n", Status)); + return Status; + } + + PciMmBase = MmPciAddress ( + mLpssPciDeviceList[BusNo].Segment, + mLpssPciDeviceList[BusNo].BusNum, + mLpssPciDeviceList[BusNo].DeviceNum, + mLpssPciDeviceList[BusNo].FunctionNum, + 0 + ); + + BaseAddress = MmioRead32 (PciMmBase + R_LPSS_IO_BAR) & B_LPSS_IO_BAR_BA; + + // + // Skip reinit if targeting the same I2C bus + // + if (BaseAddress == mLpssPciDeviceList[BusNo].Bar0) { + MmioWrite32 (BaseAddress + R_IC_TAR, SlaveAddress); + *I2CBaseAddress = BaseAddress; + return EFI_SUCCESS; + } + + Status = ProgramPciLpssI2C (BusNo); + if (Status != EFI_SUCCESS) { + DEBUG((DEBUG_ERROR, "ProgramPciLpssI2C failed ! %r\r\n", Status)); + return Status; + } + + BaseAddress = (UINT32) mLpssPciDeviceList[BusNo].Bar0; + DEBUG ((DEBUG_INFO, "I2CBaseAddress = 0x%x \n", BaseAddress)); + + NumTries = 10000; // 1 seconds + while ((STAT_MST_ACTIVITY == (MmioRead32 (BaseAddress + R_IC_STATUS) & STAT_MST_ACTIVITY))) { + MicroSecondDelay (10); + NumTries --; + if (0 == NumTries) { + DEBUG ((DEBUG_ERROR, "Try timeout\r\n")); + return EFI_DEVICE_ERROR; + } + } + + Status = I2cDisable(BaseAddress); + DEBUG ((DEBUG_INFO, "I2cDisable Status = %r\r\n", Status)); + I2cBusFrequencySet(BusNo, BaseAddress, &I2cMode); // Set I2cMode + + MmioWrite32 (BaseAddress + R_IC_INTR_MASK, 0x0); + if (0x7f < SlaveAddress) { + SlaveAddress = (SlaveAddress & 0x3ff) | IC_TAR_10BITADDR_MASTER; + } + MmioWrite32 (BaseAddress + R_IC_TAR, SlaveAddress); + MmioWrite32 (BaseAddress + R_IC_RX_TL, 0); + MmioWrite32 (BaseAddress + R_IC_TX_TL, 0); + MmioWrite32 (BaseAddress + R_IC_CON, I2cMode); + Status = I2cEnable(BaseAddress); + + DEBUG((DEBUG_INFO, "I2cEnable Status = %r\r\n", Status)); + MmioRead32 (BaseAddress + R_IC_CLR_TX_ABRT); + *I2CBaseAddress = BaseAddress; + return EFI_SUCCESS; +} + +/** + Read bytes from I2C Device + This is actual I2C hardware operation function. + + @param[in] BusNo - I2C Bus number to which the I2C device has been connected + @param[in] SlaveAddress - Slave address of the I2C device (7-bit) + @param[in] ReadBytes - Number of bytes to be read + @param[out] ReadBuffer - Address to which the value read has to be stored + @param[in] Start - It controls whether a RESTART is issued before the byte is sent or received. + @param[in] End - It controls whether a STOP is issued after the byte is sent or received. + + @retval EFI_SUCCESS - The byte value read successfully + @retval EFI_DEVICE_ERROR - Operation failed + @retval EFI_TIMEOUT - Hardware retry timeout + @retval Others - Failed to read a byte via I2C +**/ +EFI_STATUS +ByteReadI2C_Basic ( + IN UINT8 BusNo, + 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 raw_intr_stat; + UINT32 Count = 0; + UINTN I2CBaseAddress; + UINT8 *ReadPtr; + + // + // Read should always after write, so, base address should already be initialized, then get base address directly + // + I2CBaseAddress = (UINT32) mLpssPciDeviceList[BusNo].Bar0; + DEBUG ((DEBUG_INFO, "mLpssPciDeviceList returned base address = 0x%08x\n", I2CBaseAddress)); + + Status = EFI_SUCCESS; + + ReceiveDataEnd = &ReadBuffer [ReadBytes]; + ReadPtr = ReadBuffer; + if (ReadBytes) { + ReceiveRequest = ReadBuffer; + //DEBUG((DEBUG_INFO,"Read: ---------------%d bytes to RX\r\n", ReceiveDataEnd - ReceiveRequest)); + + while ((ReceiveDataEnd > ReceiveRequest) || (ReceiveDataEnd > ReadPtr)) { + // + // Check for NACK + // + raw_intr_stat = (UINT16)MmioRead32 (I2CBaseAddress + R_IC_RAW_INTR_STAT); + if (0 != (raw_intr_stat & I2C_INTR_TX_ABRT)) { + MmioRead32 (I2CBaseAddress + R_IC_CLR_TX_ABRT); + Status = EFI_DEVICE_ERROR; + DEBUG ((DEBUG_ERROR, "TX ABRT ,%d bytes hasn't been transferred\r\n", ReceiveDataEnd - ReceiveRequest)); + break; + } + + // + // Determine if another byte was received + // + I2cStatus = (UINT16)MmioRead32 (I2CBaseAddress + R_IC_STATUS); + if (0 != (I2cStatus & STAT_RFNE)) { + ReceiveData = (UINT16)MmioRead32 (I2CBaseAddress + R_IC_DATA_CMD); + *ReadPtr++ = (UINT8)ReceiveData; + DEBUG ((DEBUG_INFO, "MmioRead32 ,1 byte 0x:%x is received\r\n", ReceiveData)); + } + + if (ReceiveDataEnd == ReceiveRequest) { + MicroSecondDelay (FIFO_WRITE_DELAY); + Count++; + if (Count < 1024) { // sys hung avoid no ul-pmc device + continue; // Waiting the last request to get data and make (ReceiveDataEnd > ReadBuffer) =TRUE. + } else { + break; + } + } + // + // Wait until a read request will fit + // + if (0 == (I2cStatus & STAT_TFNF)) { + MicroSecondDelay (10); + continue; + } + // + // Issue the next read request + // + if (End && Start) { + MmioWrite32 (I2CBaseAddress + R_IC_DATA_CMD, B_READ_CMD|B_CMD_RESTART|B_CMD_STOP); + } else if (!End && Start) { + MmioWrite32 (I2CBaseAddress + R_IC_DATA_CMD, B_READ_CMD|B_CMD_RESTART); + } else if (End && !Start) { + MmioWrite32 (I2CBaseAddress + R_IC_DATA_CMD, B_READ_CMD|B_CMD_STOP); + } else if (!End && !Start) { + MmioWrite32 (I2CBaseAddress + R_IC_DATA_CMD, B_READ_CMD); + } + MicroSecondDelay (FIFO_WRITE_DELAY); //wait after send cmd + + ReceiveRequest += 1; + } + } + return Status; + +} + +/** + Write bytes to I2C Device + This is actual I2C hardware operation function. + + @param[in] BusNo - I2C Bus number to which the I2C device has been connected + @param[in] SlaveAddress - Slave address of the I2C device (7-bit) + @param[in] WriteBytes - Number of bytes to be written + @param[in] WriteBuffer - Address to which the byte value has to be written + @param[in] Start - It controls whether a RESTART is issued before the byte is sent or received. + @param[in] End - It controls whether a STOP is issued after the byte is sent or received. + + @retval EFI_SUCCESS - The byte value written successfully + @retval EFI_DEVICE_ERROR - Operation failed + @retval EFI_TIMEOUT - Hardware retry timeout + @retval Others - Failed to write a byte via I2C +**/ +EFI_STATUS +ByteWriteI2C_Basic ( + IN UINT8 BusNo, + IN UINT8 SlaveAddress, + IN UINTN WriteBytes, + IN UINT8 *WriteBuffer, + IN UINT8 Start, + IN UINT8 End + ) +{ + UINT16 Data16; + EFI_STATUS Status; + UINT32 I2cStatus; + UINT8 *TransmitPtr; + UINT8 *TransmitEnd; + UINT16 raw_intr_stat; + UINT32 Count=0; + UINTN I2CBaseAddress; + + Status = I2CInit (BusNo, SlaveAddress, &I2CBaseAddress); + if (Status != EFI_SUCCESS) { + DEBUG ((DEBUG_ERROR, "I2CInit failed ! %r\r\n", Status)); + return Status; + } + DEBUG ((DEBUG_INFO, "I2CInit returned base address = 0x%08x\n", I2CBaseAddress)); + + TransmitPtr = WriteBuffer; + TransmitEnd = &WriteBuffer [WriteBytes]; + if (WriteBytes) { + raw_intr_stat = (UINT16)MmioRead32 (I2CBaseAddress + R_IC_RAW_INTR_STAT); + if (0 != (raw_intr_stat & I2C_INTR_TX_ABRT)) { + MmioRead32 (I2CBaseAddress + R_IC_CLR_TX_ABRT); + DEBUG ((DEBUG_ERROR, "%a(#%d) - raw_intr_stat = %04x\n", __FUNCTION__, __LINE__, TransmitEnd, TransmitPtr, raw_intr_stat)); + } + + //DEBUG ((DEBUG_INFO, "Write: --------------%d bytes to TX\r\n", TransmitEnd - WriteBuffer)); + while (TransmitEnd > TransmitPtr) { + I2cStatus = MmioRead32 (I2CBaseAddress + R_IC_STATUS); + raw_intr_stat = (UINT16)MmioRead32 (I2CBaseAddress + R_IC_RAW_INTR_STAT); + if (0 != (raw_intr_stat & I2C_INTR_TX_ABRT)) { + MmioRead32 (I2CBaseAddress + R_IC_CLR_TX_ABRT); + Status = EFI_DEVICE_ERROR; + DEBUG ((DEBUG_ERROR, "%a(#%d) - TX ABRT TransmitEnd:0x%x WritePtr:0x%x\r\n", __FUNCTION__, __LINE__, TransmitEnd, TransmitPtr)); + break; + } + if (0 == (I2cStatus & STAT_TFNF)) { + MicroSecondDelay (FIFO_WRITE_DELAY); + continue; + } + + Data16 = (UINT16) *TransmitPtr; + if (End && Start) { + Data16 |= (B_CMD_RESTART | B_CMD_STOP); + } else if (!End && Start) { + Data16 |= B_CMD_RESTART; + } else if (End && !Start) { + Data16 |= B_CMD_STOP; + } + Data16 = MmioWrite16 (I2CBaseAddress + R_IC_DATA_CMD, Data16); + TransmitPtr++; + + // + // Add a small delay to work around some odd behavior being seen. Without this delay bytes get dropped. + // + MicroSecondDelay (FIFO_WRITE_DELAY); + // + // Time out + // + while (1) { + raw_intr_stat = MmioRead16 (I2CBaseAddress + R_IC_RAW_INTR_STAT); + if (0 != ( raw_intr_stat & I2C_INTR_TX_ABRT)) { + MmioRead16 (I2CBaseAddress + R_IC_CLR_TX_ABRT); + Status = EFI_DEVICE_ERROR; + DEBUG ((DEBUG_ERROR, "TX ABRT TransmitEnd:0x%x WriteBuffer:0x%x\r\n", TransmitEnd, WriteBuffer)); + } + if (0 == MmioRead16(I2CBaseAddress + R_IC_TXFLR)) break; + + MicroSecondDelay (FIFO_WRITE_DELAY); + Count++; + if (Count < 1024) { //to avoid sys hung without ul-pmc device on RVP + continue; //Waiting the last request to get data and make (ReceiveDataEnd > ReadBuffer) =TRUE. + } else { + DEBUG ((DEBUG_ERROR, "hardware timeout, 1024 times try!\r\n")); + Status = EFI_TIMEOUT; + break; + } + }//while( 1 ) + + } + + } + if (EFI_ERROR (Status)) + DEBUG ((DEBUG_ERROR, "I2cStartRequest Exit with Status %r\r\n", Status)); + + return Status; +} + +/** + Read bytes from I2C Device + + @param[in] BusNo - I2C Bus number to which the I2C device has been connected + @param[in] SlaveAddress - Slave address of the I2C device (7-bit) + @param[in] Offset - Register offset from which the data has to be read + @param[in] ReadBytes - Number of bytes to be read + @param[out] ReadBuffer - Address to which the value read has to be stored + + @retval EFI_SUCCESS - Read bytes from I2C device successfully + @retval Others - Return status depends on ByteReadI2C_Basic +**/ +EFI_STATUS +ByteReadI2C ( + IN UINT8 BusNo, + 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 = ByteWriteI2C_Basic (BusNo, SlaveAddress, 1, &Offset, TRUE, FALSE); + if (!EFI_ERROR (Status)) { + Status = ByteReadI2C_Basic (BusNo, SlaveAddress, ReadBytes, ReadBuffer, TRUE, TRUE); + } else { + DEBUG ((DEBUG_ERROR, "ByteReadI2C/ByteWriteI2C_Basic: %r\n", Status)); + } + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "ByteReadI2C: %r\n", Status)); + } + + return Status; +} + +/** + Write bytes to I2C Device + + @param[in] BusNo - I2C Bus number to which the I2C device has been connected + @param[in] SlaveAddress - Slave address of the I2C device (7-bit) + @param[in] Offset - Register offset from which the data has to be read + @param[in] WriteBytes - Number of bytes to be written + @param[in] WriteBuffer - Address to which the byte value has to be written + + @retval EFI_SUCCESS - Write bytes to I2C device successfully + @retval Others - Return status depends on ByteWriteI2C_Basic +**/ +EFI_STATUS +ByteWriteI2C ( + IN UINT8 BusNo, + 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 = ByteWriteI2C_Basic (BusNo, SlaveAddress, 1, &Offset, TRUE, FALSE); + if (!EFI_ERROR (Status)) { + Status = ByteWriteI2C_Basic (BusNo, SlaveAddress, WriteBytes, WriteBuffer, FALSE, TRUE); + } else { + DEBUG ((DEBUG_ERROR, "ByteWriteI2C/ByteWriteI2C_Basic: %r\n", Status)); + } + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "ByteWriteI2C: %r\n", Status)); + } + + return Status; +} diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Library/I2CLib/I2CLib.inf b/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Library/I2CLib/I2CLib.inf new file mode 100644 index 0000000000..81fe55a400 --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Library/I2CLib/I2CLib.inf @@ -0,0 +1,50 @@ +### @file +# Dxe library for I2C bus driver. +# +#@copyright +# Copyright (c) 2010 - 2018 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 = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = I2CLib +# CONSTRUCTOR = IntelI2CLibConstructor + +# +# 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 + BroxtonSiPkg/BroxtonSiPkg.dec + +[Protocols] + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress ## SOMETIMES_CONSUMES + gEfiBxtTokenSpaceGuid.PcdPmcGcrBaseAddress ## SOMETIMES_CONSUMES + + + diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Library/I2CLibPei/I2CLibPei.c b/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Library/I2CLibPei/I2CLibPei.c index d15170a35c..ad154d57e0 100644 --- a/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Library/I2CLibPei/I2CLibPei.c +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Library/I2CLibPei/I2CLibPei.c @@ -1,7 +1,7 @@ /** @file Pei library for I2C bus driver. - Copyright (c) 2014 - 2017, Intel Corporation. All rights reserved.
+ Copyright (c) 2014 - 2018, 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 @@ -114,7 +114,7 @@ IntelI2CPeiLibConstructor ( **/ EFI_STATUS ProgramPciLpssI2C ( - VOID + IN UINT8 BusNo ) { UINT32 PmcBase; @@ -387,7 +387,7 @@ I2CInit ( // // Need to enable the I2C PCI device // - ProgramPciLpssI2C (); + ProgramPciLpssI2C (BusNo); I2CBaseAddress = (UINT32) (LPSS_I2C0_TMP_BAR0 + (BusNo * LPSS_I2C_TMP_BAR0_DELTA)); if (DebugFlag) DEBUG ((DEBUG_INFO, "I2CBaseAddress = 0x%x \n", I2CBaseAddress)); -- 2.14.1.windows.1