On Fri, Sep 21, 2018 at 08:25:57AM +0000, Chris Co wrote:
> 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 | 162 +++++++
> Silicon/NXP/iMXPlatformPkg/Library/iMXI2cLib/iMXI2cLib.c | 487
> ++++++++++++++++++++
> Silicon/NXP/iMXPlatformPkg/Library/iMXI2cLib/iMXI2cLib.inf | 35 ++
> 3 files changed, 684 insertions(+)
>
> diff --git a/Silicon/NXP/iMXPlatformPkg/Include/iMXI2cLib.h
> b/Silicon/NXP/iMXPlatformPkg/Include/iMXI2cLib.h
> new file mode 100644
> index 000000000000..de8a1616fe1b
> --- /dev/null
> +++ b/Silicon/NXP/iMXPlatformPkg/Include/iMXI2cLib.h
> @@ -0,0 +1,162 @@
> +/** @file
> +*
> +* Copyright (c) 2018 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)
I don't see where below this has any effect.
If not required, please drop.
> +
> +typedef union {
> + UINT16 AsUint16;
Not super happy with this name.
Could it be just "Raw"? (That pattern is used frequently in EDK2.)
Same applies to similar situations below.
> + struct {
> + UINT16 Reserved0 : 1;
> + UINT16 ADR : 7;
> + UINT16 Reserved1 : 8;
> + };
> +} IMX_I2C_IADR_REG;
IADR is the name I guess, but REG should ideally be fully written out
as REGISTER.
> +
> +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;
And here, REGISTERS.
> +
> +#pragma pack(pop)
> +
> +typedef struct {
> + UINT32 ControllerAddress;
> + UINT32 ControllerSlaveAddress;
> + UINT32 ReferenceFreq;
> + UINT32 TargetFreq;
> + UINT32 SlaveAddress;
> + UINT32 TimeoutInUs;
> +} IMX_I2C_CONFIG;
> +
> +typedef struct {
> + UINT32 Divider;
> + UINT32 IC;
Please expand to CamelCase.
It's not obvious what IC stands for.
> +} IMX_I2C_DIVIDER;
> +
> +/**
> + Perform 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 Pointer to structure containing the
> targeted
> + I2C controller to be used for I2C
> operation.
> + @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 RETURN_SUCCESS I2C Read operation succeeded.
> + @retval RETURN_DEVICE_ERROR The I2C device is not functioning
> correctly.
> +
> +**/
> +RETURN_STATUS
> +iMXI2cRead (
> + IN IMX_I2C_CONFIG *I2cConfigPtr,
> + IN UINT8 RegisterAddress,
> + OUT UINT8 *ReadBufferPtr,
> + IN UINT32 ReadBufferSize
> + );
> +
> +/**
> + Perform 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 Pointer to structure containing the
> targeted
> + I2C controller to be used for I2C
> operation.
> + @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 RETURN_SUCCESS I2C Write operation succeeded.
> + @retval RETURN_DEVICE_ERROR The I2C device is not functioning
> correctly.
> +
> +**/
> +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..6cfe04dba571
> --- /dev/null
> +++ b/Silicon/NXP/iMXPlatformPkg/Library/iMXI2cLib/iMXI2cLib.c
> @@ -0,0 +1,487 @@
> +/** @file
> +*
> +* Copyright (c) 2018 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 <Base.h>
> +#include <Uefi.h>
> +
> +#include <Library/BaseLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/IoLib.h>
> +#include <Library/TimerLib.h>
> +
> +#include <iMXI2cLib.h>
> +
> +IMX_I2C_DIVIDER DividerValue[] = {
Global variable should have 'm' prefix if scope is this module or 'g'
if external visibility. If only this file, please also make it STATIC.
I'm not super happy about these live-coded values, but I guess adding
a bunch of defines wouldn't help much. But could you add a comment
block explaining roughly what the fields mean?
> + { 22, 0x20, },
Please drop the second ',' on all lines - IMX_I2C_DIVIDER only has two
structure members.
> + { 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]))
Can use BaseLib ARRAY_SIZE macro instead of adding this custom one.
> +
> +BOOLEAN
> +iMXI2cWaitStatusSet (
> + IN IMX_I2C_CONFIG *I2cConfigPtr,
> + IN UINT16 StatusBits
> + )
> +{
> + IMX_I2C_REGS *I2cRegsPtr;
> + UINT32 Counter;
> + IMX_I2C_I2SR_REG I2srReg;
Same as for previous patch - no need to name the variable after the
register it's initialised from.
> +
> + Counter = I2cConfigPtr->TimeoutInUs;
> + I2cRegsPtr = (IMX_I2C_REGS*)I2cConfigPtr->ControllerAddress;
> + while (Counter) {
> + I2srReg = (IMX_I2C_I2SR_REG)MmioRead16 ((UINTN)&I2cRegsPtr->I2SR);
> + if ((I2srReg.AsUint16 & StatusBits) == StatusBits) {
> + return TRUE;
> + }
> + MicroSecondDelay (1);
> + --Counter;
> + }
> +
> + return FALSE;
> +}
> +
> +BOOLEAN
> +iMXI2cWaitStatusUnSet (
> + IN IMX_I2C_CONFIG *I2cConfigPtr,
> + IN UINT16 StatusBits
> + )
> +{
> + IMX_I2C_REGS *I2cRegsPtr;
> + UINT32 Counter;
> + IMX_I2C_I2SR_REG I2srReg;
> +
> + Counter = I2cConfigPtr->TimeoutInUs;
> + I2cRegsPtr = (IMX_I2C_REGS*)I2cConfigPtr->ControllerAddress;
> + while (Counter) {
> + I2srReg = (IMX_I2C_I2SR_REG)MmioRead16 ((UINTN)&I2cRegsPtr->I2SR);
> + if ((I2srReg.AsUint16 & StatusBits) == 0) {
> + return TRUE;
> + }
> + MicroSecondDelay (1);
> + --Counter;
> + }
> +
> + return FALSE;
> +}
> +
> +BOOLEAN
> +iMXI2cSendByte (
> + IN IMX_I2C_CONFIG *I2cConfigPtr,
> + IN UINT8 Data
> + )
> +{
> + IMX_I2C_REGS *I2cRegsPtr;
> + UINT32 Counter;
> + IMX_I2C_I2SR_REG I2srReg;
> + UINT16 SendData;
> +
> + SendData = Data;
> + Counter = I2cConfigPtr->TimeoutInUs;
> + I2cRegsPtr = (IMX_I2C_REGS*)I2cConfigPtr->ControllerAddress;
> +
> + // Clear status and transfer byte
> + MmioWrite16 ((UINTN)&I2cRegsPtr->I2SR, 0);
> + MmioWrite16 ((UINTN)&I2cRegsPtr->I2DR, SendData);
> +
> + while (Counter) {
> + I2srReg = (IMX_I2C_I2SR_REG)MmioRead16 ((UINTN)&I2cRegsPtr->I2SR);
> + if (I2srReg.IIF == 1) {
What does it mean if it's == 1? Can that 1 be given a descriptively
named #define?
> + return TRUE;
> + } else if (I2srReg.IAL == 1) {
What does it mean if it's == 1? Can that 1 be given a descriptively
named #define?
> + 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 (
> + IN IMX_I2C_CONFIG *I2cConfigPtr
> + )
> +{
> + IMX_I2C_REGS *I2cRegsPtr;
> + UINT32 Divider;
> + UINT32 DividerCount;
> + UINT32 IfdrDiv;
> + IMX_I2C_I2CR_REG I2crReg;
> +
> + I2cRegsPtr = (IMX_I2C_REGS *)I2cConfigPtr->ControllerAddress;
> +
> + // Disable controller and clear any pending interrupt
> + MmioWrite16 ((UINTN)&I2cRegsPtr->I2CR, 0);
> + MmioWrite16 ((UINTN)&I2cRegsPtr->I2SR, 0);
A comment as to which operation does which?
> +
> + // Setup Divider if reference freq is provided. If no, use value setup by
> + // 1st boot loader
first
> + if (I2cConfigPtr->ReferenceFreq != 0) {
> + IfdrDiv = 0;
> + 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));
Why the shift by 1? Could this use a macro?
> +
> + // Enable controller and set to master mode.
> + I2crReg = (IMX_I2C_I2CR_REG)MmioRead16 ((UINTN)&I2cRegsPtr->I2CR);
> +
> + // This bit must be set before any other I2C_I2CR bits have an effect
Why?
> + I2crReg.IEN = 1;
> + MmioWrite16 ((UINTN)&I2cRegsPtr->I2CR, I2crReg.AsUint16);
What does this write do?
> + MicroSecondDelay (100);
> +
> + MmioWrite16 ((UINTN)&I2cRegsPtr->I2SR, 0);
Why this write of 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;
What does 1 mean?
> + MmioWrite16 ((UINTN)&I2cRegsPtr->I2CR, I2crReg.AsUint16);
What does this write do?
> +
> + // 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 (
> + IN IMX_I2C_CONFIG *I2cConfigPtr,
> + IN UINT8 RegisterAddress
> + )
> +{
> + IMX_I2C_REGS *I2cRegsPtr;
> + IMX_I2C_I2CR_REG I2crReg;
> + BOOLEAN Result;
> + RETURN_STATUS Status;
> +
> + I2cRegsPtr = (IMX_I2C_REGS*)I2cConfigPtr->ControllerAddress;
> + Status = iMXI2cSetupController (I2cConfigPtr);
> + if (RETURN_ERROR (Status)) {
> + DEBUG ((DEBUG_ERROR, "iMXI2cGenerateStart: Fail to setup controller
> %r\n",
> + Status));
> + return Status;
> + }
> +
> + // Set controller to transmit mode
> + I2crReg = (IMX_I2C_I2CR_REG)MmioRead16 ((UINTN)&I2cRegsPtr->I2CR);
> + I2crReg.MTX = 1;
> + MmioWrite16 ((UINTN)&I2cRegsPtr->I2CR, I2crReg.AsUint16);
> +
> + Result = iMXI2cSendByte (I2cConfigPtr, (I2cConfigPtr->SlaveAddress << 1));
Shift? Macro?
> + 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 address
> + 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 (
> + IN IMX_I2C_CONFIG *I2cConfigPtr
> + )
> +{
> + IMX_I2C_REGS *I2cRegsPtr;
> + IMX_I2C_I2CR_REG I2crReg;
> +
> + I2cRegsPtr = (IMX_I2C_REGS*)I2cConfigPtr->ControllerAddress;
> + I2crReg = (IMX_I2C_I2CR_REG)MmioRead16 ((UINTN)&I2cRegsPtr->I2CR);
> + I2crReg.MSTA = 0;
I'm getting to the point where I am wanting these fields to be given
useful names. An awful lot of #defines and comments will be necessary
otherwise.
> + 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;
> +}
> +
> +/**
> + Perform 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 Pointer to structure containing the
> targeted
> + I2C controller to be used for I2C
> operation.
> + @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 RETURN_SUCCESS I2C Read operation succeeded.
> + @retval RETURN_DEVICE_ERROR The I2C device is not functioning
> correctly.
> +
> +**/
> +RETURN_STATUS
> +iMXI2cRead (
> + IN IMX_I2C_CONFIG *I2cConfigPtr,
> + IN UINT8 RegisterAddress,
> + OUT UINT8 *ReadBufferPtr,
> + IN UINT32 ReadBufferSize
> + )
> +{
> + IMX_I2C_REGS *I2cRegsPtr;
> + IMX_I2C_I2CR_REG I2crReg;
> + BOOLEAN Result;
> + RETURN_STATUS Status;
> +
> + I2cRegsPtr = (IMX_I2C_REGS*)I2cConfigPtr->ControllerAddress;
> + Status = iMXI2cGenerateStart (I2cConfigPtr, RegisterAddress);
> + if (RETURN_ERROR (Status)) {
> + DEBUG ((DEBUG_ERROR, "iMXI2cRead: iMXI2cGenerateStart failed %r\n",
> Status));
> + goto Exit;
> + }
> +
> + // Send slave address again to begin read
> + I2crReg = (IMX_I2C_I2CR_REG)MmioRead16 ((UINTN)&I2cRegsPtr->I2CR);
> + I2crReg.RSTA = 1; // Repeated start
> + MmioWrite16 ((UINTN)&I2cRegsPtr->I2CR, I2crReg.AsUint16);
> + Result = iMXI2cSendByte (
> + I2cConfigPtr,
> + (I2cConfigPtr->SlaveAddress << 1 | 1));
| 1 - the plot thickens.
Macro please.
> + 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
> + I2crReg = (IMX_I2C_I2CR_REG)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;
> + }
> +
> + // Before the last byte is read, a Stop signal must be generated
> + if (ReadBufferSize == 1) {
> + Status = iMXI2cGenerateStop (
> + I2cConfigPtr);
No need to wrap this line.
> + if (RETURN_ERROR (Status)) {
> + DEBUG ((DEBUG_ERROR, "iMXI2cRead: iMXI2cGenerateStop fail %r\n",
> Status));
> + goto Exit;
> + }
> + }
> +
> + if (ReadBufferSize == 2) {
For second to last byte?
Could use a comment. And possibly comment above at == 1 could be
expanded to explain why.
> + I2crReg = (IMX_I2C_I2CR_REG)MmioRead16 ((UINTN)&I2cRegsPtr->I2CR);
> + I2crReg.TXAK = 1;
What does this 1 mean? #define?
> + MmioWrite16 ((UINTN)&I2cRegsPtr->I2CR, I2crReg.AsUint16);
> + }
> +
> + MmioWrite16 ((UINTN)&I2cRegsPtr->I2SR, 0);
Why write 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;
> +}
> +
> +/**
> + Perform 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 Pointer to structure containing the
> targeted
> + I2C controller to be used for I2C
> operation.
> + @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 RETURN_SUCCESS I2C Write operation succeeded.
> + @retval RETURN_DEVICE_ERROR The I2C device is not functioning
> correctly.
> +
> +**/
> +RETURN_STATUS
> +iMXI2cWrite (
> + IN IMX_I2C_CONFIG *I2cConfigPtr,
> + IN UINT8 RegisterAddress,
> + IN UINT8 *WriteBufferPtr,
> + IN UINT32 WriteBufferSize
> + )
> +{
> + IMX_I2C_REGS *I2cRegsPtr;
> + BOOLEAN Result;
> + RETURN_STATUS Status;
> +
> + I2cRegsPtr = (IMX_I2C_REGS*)I2cConfigPtr->ControllerAddress;
> + Status = iMXI2cGenerateStart (I2cConfigPtr, RegisterAddress);
> + if (RETURN_ERROR (Status)) {
> + DEBUG ((DEBUG_ERROR, "iMXI2cWrite: iMXI2cGenerateStart fail %r\n",
> Status));
> + goto Exit;
> + }
> +
> + while (WriteBufferSize > 0) {
> + Result = iMXI2cSendByte (I2cConfigPtr, *WriteBufferPtr);
> + if (Result == FALSE) {
> + DEBUG ((
> + DEBUG_ERROR,
Could skip that line break.
/
Leif
> + "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..ad968e88b1da
> --- /dev/null
> +++ b/Silicon/NXP/iMXPlatformPkg/Library/iMXI2cLib/iMXI2cLib.inf
> @@ -0,0 +1,35 @@
> +## @file
> +#
> +# Copyright (c) 2018 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 = 0x0001001A
> + BASE_NAME = iMXI2cLib
> + FILE_GUID = C4E4A003-8AEB-4C9B-8E72-D2BAD2134BDE
> + MODULE_TYPE = BASE
> + VERSION_STRING = 1.0
> + LIBRARY_CLASS = iMXI2cLib
> +
> +[Packages]
> + EmbeddedPkg/EmbeddedPkg.dec
> + MdePkg/MdePkg.dec
> + Silicon/NXP/iMXPlatformPkg/iMXPlatformPkg.dec
> +
> +[LibraryClasses]
> + BaseMemoryLib
> + DebugLib
> + IoLib
> + TimerLib
> +
> +[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