This adds support for I2C controller on NXP i.MX platforms. Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Christopher Co <[email protected]> Cc: Ard Biesheuvel <[email protected]> Cc: Leif Lindholm <[email protected]> Cc: Michael D Kinney <[email protected]> --- Silicon/NXP/iMXPlatformPkg/Include/iMXI2cLib.h | 158 ++++++ Silicon/NXP/iMXPlatformPkg/Library/iMXI2cLib/iMXI2cLib.c | 524 ++++++++++++++++++++ Silicon/NXP/iMXPlatformPkg/Library/iMXI2cLib/iMXI2cLib.inf | 35 ++ 3 files changed, 717 insertions(+)
diff --git a/Silicon/NXP/iMXPlatformPkg/Include/iMXI2cLib.h b/Silicon/NXP/iMXPlatformPkg/Include/iMXI2cLib.h new file mode 100644 index 000000000000..e0688d7aa89f --- /dev/null +++ b/Silicon/NXP/iMXPlatformPkg/Include/iMXI2cLib.h @@ -0,0 +1,158 @@ +/** @file +* +* Copyright (c) Microsoft 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. +* +**/ + +#ifndef _IMX_I2C_H_ +#define _IMX_I2C_H_ + +#pragma pack(push, 1) + +typedef union { + UINT16 AsUint16; + struct { + UINT16 Reserved0 : 1; + UINT16 ADR : 7; + UINT16 Reserved1 : 8; + }; +} IMX_I2C_IADR_REG; + +typedef union { + UINT16 AsUint16; + struct { + UINT16 IC : 6; + UINT16 Reserved0 : 10; + }; +} IMX_I2C_IFDR_REG; + + +typedef union { + UINT16 AsUint16; + struct { + UINT16 Reserved0 : 2; + UINT16 RSTA : 1; + UINT16 TXAK : 1; + UINT16 MTX : 1; + UINT16 MSTA : 1; + UINT16 IIEN : 1; + UINT16 IEN : 1; + UINT16 Reserved1 : 8; + }; +} IMX_I2C_I2CR_REG; + +#define IMX_I2C_I2SR_RXAK 0x0001 +#define IMX_I2C_I2SR_IIF 0x0002 +#define IMX_I2C_I2SR_SRW 0x0004 +#define IMX_I2C_I2SR_IAL 0x0010 +#define IMX_I2C_I2SR_IBB 0x0020 +#define IMX_I2C_I2SR_IAAS 0x0040 +#define IMX_I2C_I2SR_ICF 0x0080 + +typedef union { + UINT16 AsUint16; + struct { + UINT16 RXAK : 1; + UINT16 IIF : 1; + UINT16 SRW : 1; + UINT16 Reserved0 : 1; + UINT16 IAL : 1; + UINT16 IBB : 1; + UINT16 IAAS : 1; + UINT16 ICF : 1; + UINT16 Reserved1 : 8; + }; +} IMX_I2C_I2SR_REG; + +typedef union { + UINT16 AsUint16; + struct { + UINT16 DATA : 8; + UINT16 Reserved0 : 8; + }; +} IMX_I2C_I2DR_REG; + +typedef struct { + IMX_I2C_IADR_REG IADR; + UINT16 Pad0; + IMX_I2C_IFDR_REG IFDR; + UINT16 Pad1; + IMX_I2C_I2CR_REG I2CR; + UINT16 Pad2; + IMX_I2C_I2DR_REG I2SR; + UINT16 Pad3; + IMX_I2C_I2DR_REG I2DR; + UINT16 Pad4; +} IMX_I2C_REGS; + +#pragma pack(pop) + +typedef struct { + UINT32 ControllerAddress; + UINT32 ControllerSlaveAddress; + UINT32 ReferenceFreq; + UINT32 TargetFreq; + UINT32 SlaveAddress; + UINT32 TimeoutInUs; +} IMX_I2C_CONFIG; + +/** Performs i2c read operation. + + The iMXI2cRead perform i2c read operation by programming the i2c controller. + The caller is responsible to provide i2c controller configuration. + + @param[in] IMX_I2C_CONFIG* Structure containing the targeted i2c controller + to be used for i2c operation. The structure contains important information + such as the controller base address, reference frequency. + @param[in] RegisterAddress Targeted device register address to start read. + @param[out] ReadBufferPtr Caller supplied buffer that would be written into + with data from the read operation. + @param[in] ReadBufferSize Size of caller supplied buffer. + + @retval EFI_SUCCESS i2c Read operation succeed. + @retval !EFI_SUCCESS Failure. + +--*/ +RETURN_STATUS +iMXI2cRead ( + IN IMX_I2C_CONFIG* I2cConfigPtr, + IN UINT8 RegisterAddress, + OUT UINT8* ReadBufferPtr, + IN UINT32 ReadBufferSize + ); + +/** Performs i2c write operation. + + The iMXI2cWrite perform i2c write operation by programming the i2c + controller. The caller is responsible to provide i2c controller + configuration. + + @param[in] IMX_I2C_CONFIG* Structure containing the targeted i2c controller + to be used for i2c operation. The structure contains important information + such as the controller base address, reference frequency. + @param[in] RegisterAddress Targeted device register address to start write. + @param[out] WriteBufferPtr Caller supplied buffer that contained data that + would be read from for i2c write operation.. + @param[in] WriteBufferSize Size of caller supplied buffer. + + @retval EFI_SUCCESS i2c Write operation succeed. + @retval !EFI_SUCCESS Failure. + +--*/ +RETURN_STATUS +iMXI2cWrite ( + IN IMX_I2C_CONFIG* I2cConfigPtr, + IN UINT8 RegisterAddress, + IN UINT8* WriteBufferPtr, + IN UINT32 WriteBufferSize + ); + +#endif diff --git a/Silicon/NXP/iMXPlatformPkg/Library/iMXI2cLib/iMXI2cLib.c b/Silicon/NXP/iMXPlatformPkg/Library/iMXI2cLib/iMXI2cLib.c new file mode 100644 index 000000000000..00da831ae6b6 --- /dev/null +++ b/Silicon/NXP/iMXPlatformPkg/Library/iMXI2cLib/iMXI2cLib.c @@ -0,0 +1,524 @@ +/** @file +* +* Copyright (c) Microsoft 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 <Uefi.h> +#include <Base.h> +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/IoLib.h> +#include <Library/TimerLib.h> +#include <Library/DebugLib.h> + +#include <iMXI2cLib.h> + +typedef struct { + UINT32 Divider; + UINT32 IC; +} IMX_I2C_DIVIDER; + +IMX_I2C_DIVIDER DividerValue[] = { + { 22, 0x20, }, + { 24, 0x21, }, + { 26, 0x22, }, + { 28, 0x23, }, + { 30, 0x00, }, + { 32, 0x24, }, + { 36, 0x25, }, + { 40, 0x26, }, + { 42, 0x03, }, + { 44, 0x27, }, + { 48, 0x28, }, + { 52, 0x05, }, + { 56, 0x29, }, + { 60, 0x06, }, + { 64, 0x2A, }, + { 72, 0x2B, }, + { 80, 0x2C, }, + { 88, 0x09, }, + { 96, 0x2D, }, + { 104, 0x0A, }, + { 112, 0x2E, }, + { 128, 0x2F, }, + { 144, 0x0C, }, + { 160, 0x30, }, + { 192, 0x31, }, + { 224, 0x32, }, + { 240, 0x0F, }, + { 256, 0x33, }, + { 288, 0x10, }, + { 320, 0x34, }, + { 384, 0x35, }, + { 448, 0x36, }, + { 480, 0x13, }, + { 512, 0x37, }, + { 576, 0x14, }, + { 640, 0x38, }, + { 768, 0x39, }, + { 896, 0x3A, }, + { 960, 0x17, }, + { 1024, 0x3B, }, + { 1152, 0x18, }, + { 1280, 0x3C, }, + { 1536, 0x3D, }, + { 1792, 0x3E, }, + { 1920, 0x1B, }, + { 2048, 0x3F, }, + { 2304, 0x1C, }, + { 2560, 0x1D, }, + { 3072, 0x1E, }, + { 3840, 0x1F, }, +}; + +#define DIVIDER_VALUE_TOTAL (sizeof(DividerValue) / sizeof(DividerValue[0])) + +BOOLEAN +iMXI2cWaitStatusSet ( + IMX_I2C_CONFIG* I2cConfigPtr, + UINT16 StatusBits + ) +{ + UINT32 counter = I2cConfigPtr->TimeoutInUs; + IMX_I2C_REGS* i2cRegsPtr = (IMX_I2C_REGS*)I2cConfigPtr->ControllerAddress; + + while (counter) { + IMX_I2C_I2SR_REG i2srReg = { MmioRead16((UINTN)&i2cRegsPtr->I2SR) }; + + if ((i2srReg.AsUint16 & StatusBits) == StatusBits) { + return TRUE; + } + + MicroSecondDelay(1); + --counter; + } + + return FALSE; +} + +BOOLEAN +iMXI2cWaitStatusUnSet ( + IMX_I2C_CONFIG* I2cConfigPtr, + UINT16 StatusBits +) +{ + UINT32 counter = I2cConfigPtr->TimeoutInUs; + IMX_I2C_REGS* i2cRegsPtr = (IMX_I2C_REGS*)I2cConfigPtr->ControllerAddress; + + while (counter) { + IMX_I2C_I2SR_REG i2srReg = { MmioRead16((UINTN)&i2cRegsPtr->I2SR) }; + + if ((i2srReg.AsUint16 & StatusBits) == 0) { + return TRUE; + } + + MicroSecondDelay(1); + --counter; + } + + return FALSE;; +} + +BOOLEAN +iMXI2cSendByte ( + IMX_I2C_CONFIG* I2cConfigPtr, + UINT8 Data +) +{ + UINT16 sendData = Data; + UINT32 counter = I2cConfigPtr->TimeoutInUs; + IMX_I2C_REGS* i2cRegsPtr = (IMX_I2C_REGS*)I2cConfigPtr->ControllerAddress; + + // + // Clear status + // + MmioWrite16((UINTN)&i2cRegsPtr->I2SR, 0); + + // + // Transfer byte + // + MmioWrite16((UINTN)&i2cRegsPtr->I2DR, sendData); + + while (counter) { + IMX_I2C_I2SR_REG i2srReg = { MmioRead16((UINTN)&i2cRegsPtr->I2SR) }; + + if (i2srReg.IIF == 1) { + return TRUE; + } + else if (i2srReg.IAL == 1) { + + DEBUG(( + DEBUG_ERROR, + "iMXI2cSendByte: fail 0x%04x\n", + i2srReg.AsUint16)); + return FALSE; + } + + MicroSecondDelay(1); + --counter; + } + + DEBUG((DEBUG_ERROR, "iMXI2cSendByte: Fail timeout\n")); + + return FALSE; +} + +RETURN_STATUS +iMXI2cSetupController ( + IMX_I2C_CONFIG* I2cConfigPtr, + UINT8 RegisterAddress + ) +{ + IMX_I2C_REGS* i2cRegsPtr = (IMX_I2C_REGS*)I2cConfigPtr->ControllerAddress; + + // + // Disable controller and clear any pending interrupt + // + MmioWrite16((UINTN)&i2cRegsPtr->I2CR, 0); + MmioWrite16((UINTN)&i2cRegsPtr->I2SR, 0); + + // + // Setup divider if reference freq is provided. If no use value setup by + // 1st boot loader + // + if (I2cConfigPtr->ReferenceFreq != 0) { + + UINT32 ifdrDiv = 0; + UINT32 dividerCount; + UINT32 divider = I2cConfigPtr->ReferenceFreq / I2cConfigPtr->TargetFreq; + + for (dividerCount = 0; + dividerCount < DIVIDER_VALUE_TOTAL; + ++dividerCount) { + + if (DividerValue[dividerCount].Divider >= divider) { + + DEBUG(( + DEBUG_INFO, + "iMXI2cSetupController: divider %d IC 0x%02x\n", + DividerValue[dividerCount].Divider, + DividerValue[dividerCount].IC)); + + ifdrDiv = DividerValue[dividerCount].IC; + break; + } + } + + if (ifdrDiv == 0) { + DEBUG(( + DEBUG_ERROR, + "iMXI2cSetupController: could not find divider for %d\n", + divider)); + return RETURN_INVALID_PARAMETER; + } + + MmioWrite16((UINTN)&i2cRegsPtr->IFDR, ifdrDiv); + } + + // + // Setup slave address + // + MmioWrite16((UINTN)&i2cRegsPtr->IADR, (I2cConfigPtr->ControllerSlaveAddress << 1)); + + // + // Enable controller and set to master mode. + // + { + IMX_I2C_I2CR_REG i2crReg = { MmioRead16((UINTN)&i2cRegsPtr->I2CR) }; + + // + // This bit must be set before any other I2C_I2CR bits have an effect + // + i2crReg.IEN = 1; + MmioWrite16((UINTN)&i2cRegsPtr->I2CR, i2crReg.AsUint16); + MicroSecondDelay(100); + + MmioWrite16((UINTN)&i2cRegsPtr->I2SR, 0); + + // + // Wait for bus to be idle + // + if (iMXI2cWaitStatusUnSet(I2cConfigPtr, IMX_I2C_I2SR_IBB) == FALSE) { + DEBUG((DEBUG_ERROR, "iMXI2cGenerateStart: Controller remains busy\n")); + return RETURN_DEVICE_ERROR; + } + + i2crReg.MSTA = 1; + MmioWrite16((UINTN)&i2cRegsPtr->I2CR, i2crReg.AsUint16); + + // + // Now wait for bus to be busy + // + if (iMXI2cWaitStatusSet(I2cConfigPtr, IMX_I2C_I2SR_IBB) == FALSE) { + DEBUG((DEBUG_ERROR, "iMXI2cGenerateStart: Controller remains idle\n")); + return RETURN_DEVICE_ERROR; + } + } + + return RETURN_SUCCESS; +} + +RETURN_STATUS +iMXI2cGenerateStart ( + IMX_I2C_CONFIG* I2cConfigPtr, + UINT8 RegisterAddress, + BOOLEAN Read + ) +{ + BOOLEAN result; + RETURN_STATUS status; + IMX_I2C_REGS* i2cRegsPtr = (IMX_I2C_REGS*)I2cConfigPtr->ControllerAddress; + + status = iMXI2cSetupController( + I2cConfigPtr, + RegisterAddress); + if (RETURN_ERROR(status)) { + DEBUG((DEBUG_ERROR, "iMXI2cGenerateStart: Fail to setup controller %r\n", status)); + return status; + } + + // + // Send slave address so set controller to transmit mode + // + { + IMX_I2C_I2CR_REG i2crReg = { MmioRead16((UINTN)&i2cRegsPtr->I2CR) }; + + i2crReg.MTX = 1; + MmioWrite16((UINTN)&i2cRegsPtr->I2CR, i2crReg.AsUint16); + } + + result = iMXI2cSendByte( + I2cConfigPtr, + (I2cConfigPtr->SlaveAddress << 1)); + if (result == FALSE) { + DEBUG(( + DEBUG_ERROR, + "iMXI2cGenerateStart: Slave address transfer fail 0x%04x\n", + MmioRead16((UINTN)&i2cRegsPtr->I2SR))); + return RETURN_DEVICE_ERROR; + } + + // + // Send slave register + // + result = iMXI2cSendByte( + I2cConfigPtr, + RegisterAddress); + if (result == FALSE) { + DEBUG(( + DEBUG_ERROR, + "iMXI2cGenerateStart: Slave register address transfer fail 0x%04x\n", + MmioRead16((UINTN)&i2cRegsPtr->I2SR))); + return RETURN_DEVICE_ERROR; + } + + return RETURN_SUCCESS; +} + +RETURN_STATUS +iMXI2cGenerateStop ( + IMX_I2C_CONFIG* I2cConfigPtr + ) +{ + IMX_I2C_REGS* i2cRegsPtr = (IMX_I2C_REGS*)I2cConfigPtr->ControllerAddress; + IMX_I2C_I2CR_REG i2crReg = { MmioRead16((UINTN)&i2cRegsPtr->I2CR) }; + + i2crReg.MSTA = 0; + i2crReg.MTX = 0; + MmioWrite16((UINTN)&i2cRegsPtr->I2CR, i2crReg.AsUint16); + + // + // Bus should go idle + // + if (iMXI2cWaitStatusUnSet(I2cConfigPtr, IMX_I2C_I2SR_IBB) == FALSE) { + DEBUG((DEBUG_ERROR, "iMXI2cGenerateStop: Controller remains busy\n")); + return RETURN_DEVICE_ERROR; + } + + return RETURN_SUCCESS; +} + +RETURN_STATUS +iMXI2cRead ( + IN IMX_I2C_CONFIG* I2cConfigPtr, + IN UINT8 RegisterAddress, + OUT UINT8* ReadBufferPtr, + IN UINT32 ReadBufferSize + ) +{ + RETURN_STATUS status; + IMX_I2C_REGS* i2cRegsPtr = (IMX_I2C_REGS*)I2cConfigPtr->ControllerAddress; + + status = iMXI2cGenerateStart( + I2cConfigPtr, + RegisterAddress, + TRUE); + if (RETURN_ERROR(status)) { + DEBUG((DEBUG_ERROR, "iMXI2cRead: iMXI2cGenerateStart failed %r\n", status)); + goto Exit; + } + + // + // Send slave address again to begin read + // + { + BOOLEAN result; + IMX_I2C_I2CR_REG i2crReg = { MmioRead16((UINTN)&i2cRegsPtr->I2CR) }; + + i2crReg.RSTA = 1; // Repeated start + MmioWrite16((UINTN)&i2cRegsPtr->I2CR, i2crReg.AsUint16); + + result = iMXI2cSendByte( + I2cConfigPtr, + (I2cConfigPtr->SlaveAddress << 1 | 1)); + if (result == FALSE) { + DEBUG(( + DEBUG_ERROR, + "iMXI2cRead: 2nd Slave address transfer failed 0x%04x\n", + MmioRead16((UINTN)&i2cRegsPtr->I2SR))); + status = RETURN_DEVICE_ERROR; + goto Exit; + } + } + + // + // Disable master mode + // + { + IMX_I2C_I2CR_REG i2crReg = { MmioRead16((UINTN)&i2cRegsPtr->I2CR) }; + + // + // NXP application note AN4481 + // Only one byte so do not send acknowledge + // + if (ReadBufferSize == 1) { + i2crReg.TXAK = 1; + } + else { + i2crReg.TXAK = 0; + } + + i2crReg.MTX = 0; + MmioWrite16((UINTN)&i2cRegsPtr->I2CR, i2crReg.AsUint16); + } + + // + // A data transfer can now be initiated by a read from I2C_I2DR in Slave + // Receive mode. + // + { + MmioWrite16((UINTN)&i2cRegsPtr->I2SR, 0); + MmioRead16((UINTN)&i2cRegsPtr->I2DR); + } + + do { + // + // Wait for transfer to complete + // + if (iMXI2cWaitStatusSet(I2cConfigPtr, IMX_I2C_I2SR_IIF) == FALSE) { + DEBUG((DEBUG_ERROR, "iMXI2cRead: waiting for read fail\n")); + status = RETURN_DEVICE_ERROR; + goto Exit; + } + + if (iMXI2cWaitStatusSet(I2cConfigPtr, IMX_I2C_I2SR_ICF) == FALSE) { + DEBUG((DEBUG_ERROR, "iMXI2cRead: waiting for read fail\n")); + status = RETURN_DEVICE_ERROR; + goto Exit; + } + + // + // Per spec. Before the last byte is read, a Stop signal must be generated + // + if (ReadBufferSize == 1) { + + status = iMXI2cGenerateStop( + I2cConfigPtr); + if (RETURN_ERROR(status)) { + DEBUG((DEBUG_ERROR, "iMXI2cRead: iMXI2cGenerateStop fail %r\n", status)); + goto Exit; + } + } + + if (ReadBufferSize == 2) { + IMX_I2C_I2CR_REG i2crReg = { MmioRead16((UINTN)&i2cRegsPtr->I2CR) }; + + i2crReg.TXAK = 1; + MmioWrite16((UINTN)&i2cRegsPtr->I2CR, i2crReg.AsUint16); + } + + MmioWrite16((UINTN)&i2cRegsPtr->I2SR, 0); + + *ReadBufferPtr = MmioRead8((UINTN)&i2cRegsPtr->I2DR); + + ++ReadBufferPtr; + --ReadBufferSize; + + } while (ReadBufferSize > 0); + +Exit: + status = iMXI2cGenerateStop( + I2cConfigPtr); + if (RETURN_ERROR(status)) { + DEBUG((DEBUG_ERROR, "iMXI2cRead: Final iMXI2cGenerateStop fail %r\n", status)); + } + + return status; +} + +RETURN_STATUS +iMXI2cWrite ( + IN IMX_I2C_CONFIG* I2cConfigPtr, + IN UINT8 RegisterAddress, + IN UINT8* WriteBufferPtr, + IN UINT32 WriteBufferSize + ) +{ + RETURN_STATUS status; + IMX_I2C_REGS* i2cRegsPtr = (IMX_I2C_REGS*)I2cConfigPtr->ControllerAddress; + + status = iMXI2cGenerateStart( + I2cConfigPtr, + RegisterAddress, + FALSE); + if (RETURN_ERROR(status)) { + DEBUG((DEBUG_ERROR, "iMXI2cWrite: iMXI2cGenerateStart fail %r\n", status)); + goto Exit; + } + + while (WriteBufferSize > 0) { + + BOOLEAN result = iMXI2cSendByte( + I2cConfigPtr, + *WriteBufferPtr); + if (result == FALSE) { + DEBUG(( + DEBUG_ERROR, + "iMXI2cWrite: Slave address transfer fail 0x%04x\n", + MmioRead16((UINTN)&i2cRegsPtr->I2SR))); + status = RETURN_DEVICE_ERROR; + goto Exit; + } + + ++WriteBufferPtr; + --WriteBufferSize; + } + +Exit: + status = iMXI2cGenerateStop( + I2cConfigPtr); + if (RETURN_ERROR(status)) { + DEBUG((DEBUG_ERROR, "iMXI2cWrite: iMXI2cGenerateStop fail %r\n", status)); + } + + return status; +} diff --git a/Silicon/NXP/iMXPlatformPkg/Library/iMXI2cLib/iMXI2cLib.inf b/Silicon/NXP/iMXPlatformPkg/Library/iMXI2cLib/iMXI2cLib.inf new file mode 100644 index 000000000000..409fc8f14166 --- /dev/null +++ b/Silicon/NXP/iMXPlatformPkg/Library/iMXI2cLib/iMXI2cLib.inf @@ -0,0 +1,35 @@ +## @file +# +# Copyright (c) Microsoft 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 = iMXI2cLib + FILE_GUID = C4E4A003-8AEB-4C9B-8E72-D2BAD2134BDE + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = iMXI2cLib + +[Packages] + MdePkg/MdePkg.dec + EmbeddedPkg/EmbeddedPkg.dec + Silicon/NXP/iMXPlatformPkg/iMXPlatformPkg.dec + +[LibraryClasses] + DebugLib + IoLib + TimerLib + BaseMemoryLib + +[Sources.common] + iMXI2cLib.c -- 2.16.2.gvfs.1.33.gf5370f1 _______________________________________________ edk2-devel mailing list [email protected] https://lists.01.org/mailman/listinfo/edk2-devel

