[edk2-devel] [edk2-platforms][PATCH v2 05/32] AmpereAltraPkg: Add DwI2cLib library

Leif Lindholm leif at nuviainc.com
Fri Jun 4 23:21:41 UTC 2021


On Wed, May 26, 2021 at 17:06:57 +0700, Nhi Pham wrote:
> From: Vu Nguyen <vunguyen at os.amperecomputing.com>
> 
> The DwI2cLib library provides basic functions to control the I2C
> controller on Ampere Altra processor.
> 
> Cc: Thang Nguyen <thang at os.amperecomputing.com>
> Cc: Chuong Tran <chuong at os.amperecomputing.com>
> Cc: Phong Vo <phong at os.amperecomputing.com>
> Cc: Leif Lindholm <leif at nuviainc.com>
> Cc: Michael D Kinney <michael.d.kinney at intel.com>
> Cc: Ard Biesheuvel <ardb+tianocore at kernel.org>
> Cc: Nate DeSimone <nathaniel.l.desimone at intel.com>
> 
> Signed-off-by: Vu Nguyen <vunguyen at os.amperecomputing.com>

Reviewed-by: Leif Lindholm <leif at nuviainc.com>
(but I think this was one of the ones that have issues that should be
addressed - please ensure build with CLANG38 before v3)

/
    Leif

> ---
>  Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec            |   3 +
>  Silicon/Ampere/AmpereAltraPkg/Library/DwI2cLib/DwI2cLib.inf |  38 +
>  Silicon/Ampere/AmpereAltraPkg/Include/Library/I2cLib.h      | 100 +++
>  Silicon/Ampere/AmpereAltraPkg/Library/DwI2cLib/DwI2cLib.c   | 883 ++++++++++++++++++++
>  4 files changed, 1024 insertions(+)
> 
> diff --git a/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec b/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec
> index 73097afaf841..8be6a329bb26 100644
> --- a/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec
> +++ b/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec
> @@ -28,6 +28,9 @@ [LibraryClasses]
>    ##  @libraryclass  Defines a set of methods to communicate with SCP.
>    SystemFirmwareInterfaceLib|Silicon/Ampere/AmpereAltraPkg/Include/Library/SystemFirmwareInterfaceLib.h
>  
> +  ##  @libraryclass  Defines a set of methods to read/write to I2C devices.
> +  I2cLib|Silicon/Ampere/AmpereAltraPkg/Include/Library/I2cLib.h
> +
>    ##  @libraryclass  Defines a set of methods to communicate with secure parition over MM interface.
>    MmCommunicationLib|Silicon/Ampere/AmpereAltraPkg/Include/Library/MmCommunicationLib.h
>  
> diff --git a/Silicon/Ampere/AmpereAltraPkg/Library/DwI2cLib/DwI2cLib.inf b/Silicon/Ampere/AmpereAltraPkg/Library/DwI2cLib/DwI2cLib.inf
> new file mode 100644
> index 000000000000..091f5f9b310c
> --- /dev/null
> +++ b/Silicon/Ampere/AmpereAltraPkg/Library/DwI2cLib/DwI2cLib.inf
> @@ -0,0 +1,38 @@
> +## @file
> +# Component description for DwI2cLib library for the Designware I2C controller.
> +#
> +# Copyright (c) 2020 - 2021, Ampere Computing LLC. All rights reserved.<BR>
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> +  INF_VERSION                    = 0x0001001B
> +  BASE_NAME                      = DwI2cLib
> +  FILE_GUID                      = 222609E2-C181-11E6-A4A6-CEC0C932CE01
> +  MODULE_TYPE                    = BASE
> +  VERSION_STRING                 = 1.0
> +  LIBRARY_CLASS                  = I2cLib
> +  CONSTRUCTOR                    = I2cLibConstructor
> +
> +[Sources]
> +  DwI2cLib.c
> +
> +[Packages]
> +  ArmPkg/ArmPkg.dec
> +  ArmPlatformPkg/ArmPlatformPkg.dec
> +  MdePkg/MdePkg.dec
> +  Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec
> +
> +[LibraryClasses]
> +  BaseLib
> +  BaseMemoryLib
> +  DebugLib
> +  HobLib
> +  IoLib
> +  TimerLib
> +
> +[Guids]
> +  gEfiEventVirtualAddressChangeGuid
> +  gPlatformHobGuid
> diff --git a/Silicon/Ampere/AmpereAltraPkg/Include/Library/I2cLib.h b/Silicon/Ampere/AmpereAltraPkg/Include/Library/I2cLib.h
> new file mode 100644
> index 000000000000..f13794171029
> --- /dev/null
> +++ b/Silicon/Ampere/AmpereAltraPkg/Include/Library/I2cLib.h
> @@ -0,0 +1,100 @@
> +/** @file
> +  Library implementation for the Designware I2C controller.
> +
> +  Copyright (c) 2020 - 2021, Ampere Computing LLC. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef I2C_LIB_H_
> +#define I2C_LIB_H_
> +
> +#include <Uefi/UefiBaseType.h>
> +
> +/**
> +  Write to I2C bus.
> +
> +  @param[in]     Bus          I2C bus Id.
> +  @param[in]     SlaveAddr    The address of slave device on the bus.
> +  @param[in,out] Buf          Buffer that holds data to write.
> +  @param[in,out] WriteLength  Pointer to length of buffer.
> +
> +  @return EFI_SUCCESS            Write successfully.
> +  @return EFI_INVALID_PARAMETER  A parameter is invalid.
> +  @return EFI_UNSUPPORTED        The bus is not supported.
> +  @return EFI_NOT_READY          The device/bus is not ready.
> +  @return EFI_TIMEOUT            Timeout why transferring data.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +I2cWrite (
> +  IN     UINT32 Bus,
> +  IN     UINT32 SlaveAddr,
> +  IN OUT UINT8  *Buf,
> +  IN OUT UINT32 *WriteLength
> +  );
> +
> +/**
> +  Read data from I2C bus.
> +
> +  @param[in]     Bus          I2C bus Id.
> +  @param[in]     SlaveAddr    The address of slave device on the bus.
> +  @param[in]     BufCmd       Buffer where to send the command.
> +  @param[in]     CmdLength    Pointer to length of BufCmd.
> +  @param[in,out] Buf          Buffer where to put the read data to.
> +  @param[in,out] ReadLength   Pointer to length of buffer.
> +
> +  @return EFI_SUCCESS            Read successfully.
> +  @return EFI_INVALID_PARAMETER  A parameter is invalid.
> +  @return EFI_UNSUPPORTED        The bus is not supported.
> +  @return EFI_NOT_READY          The device/bus is not ready.
> +  @return EFI_TIMEOUT            Timeout why transferring data.
> +  @return EFI_CRC_ERROR          There are errors on receiving data.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +I2cRead (
> +  IN     UINT32 Bus,
> +  IN     UINT32 SlaveAddr,
> +  IN     UINT8  *BufCmd,
> +  IN     UINT32 CmdLength,
> +  IN OUT UINT8  *Buf,
> +  IN OUT UINT32 *ReadLength
> +  );
> +
> +/**
> + Setup new transaction with I2C slave device.
> +
> +  @param[in] Bus      I2C bus Id.
> +  @param[in] BusSpeed I2C bus speed in Hz.
> +
> +  @retval EFI_SUCCESS           Success.
> +  @retval EFI_INVALID_PARAMETER A parameter is invalid.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +I2cProbe (
> +  IN UINT32 Bus,
> +  IN UINTN  BusSpeed
> +  );
> +
> +/**
> + Setup a bus that to be used in runtime service.
> +
> +  @param[in] Bus I2C bus Id.
> +
> +  @retval EFI_SUCCESS  Success.
> +  @retval Otherwise    Error code.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +I2cSetupRuntime (
> +  IN UINT32 Bus
> +  );
> +
> +#endif /* I2C_LIB_H_ */
> diff --git a/Silicon/Ampere/AmpereAltraPkg/Library/DwI2cLib/DwI2cLib.c b/Silicon/Ampere/AmpereAltraPkg/Library/DwI2cLib/DwI2cLib.c
> new file mode 100644
> index 000000000000..5e7cd020223c
> --- /dev/null
> +++ b/Silicon/Ampere/AmpereAltraPkg/Library/DwI2cLib/DwI2cLib.c
> @@ -0,0 +1,883 @@
> +/** @file
> +
> +  Copyright (c) 2020 - 2021, Ampere Computing LLC. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <PiDxe.h>
> +#include <Uefi.h>
> +
> +#include <Guid/PlatformInfoHobGuid.h>
> +#include <Library/DebugLib.h>
> +#include <Library/DxeServicesTableLib.h>
> +#include <Library/HobLib.h>
> +#include <Library/I2cLib.h>
> +#include <Library/IoLib.h>
> +#include <Library/TimerLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/UefiRuntimeLib.h>
> +#include <PlatformInfoHob.h>
> +
> +#define I2cSync() { asm volatile ("dmb ish" : : : "memory"); }
> +
> +//
> +// Runtime needs to be 64K alignment
> +//
> +#define RUNTIME_ADDRESS_MASK           (~(SIZE_64KB - 1))
> +#define RUNTIME_ADDRESS_LENGTH         SIZE_64KB
> +
> +//
> +// Private I2C bus data
> +//
> +typedef struct {
> +  UINTN  Base;
> +  UINT32 BusSpeed;
> +  UINT32 RxFifo;
> +  UINT32 TxFifo;
> +  UINT32 PollingTime;
> +  UINT32 Enabled;
> +} DW_I2C_CONTEXT_T;
> +
> +//
> +// I2C SCL counter macros
> +//
> +typedef enum {
> +  I2cSpeedModeStandard = 0,
> +  I2cSpeedModeFast,
> +} I2C_SPEED_MODE;
> +
> +#define DW_I2C_MAXIMUM_SPEED_HZ 400000
> +
> +typedef enum {
> +  I2cSclSpkLen = 0,
> +  I2cSclHcnt,
> +  I2cSclLcnt,
> +} I2C_SCL_PARAM;
> +
> +STATIC UINT32 I2cSclParam[][3] = {
> +  /* SPK_LEN, HCNT, LCNT */
> +  [I2cSpeedModeStandard]   = { 10, 0x3E2, 0x47D }, // SS (Standard Speed)
> +  [I2cSpeedModeFast]       = { 10, 0xA4,  0x13F }, // FS (Fast Speed)
> +};
> +
> +STATIC BOOLEAN          mI2cRuntimeEnableArray[MAX_PLATFORM_I2C_BUS_NUM] = {FALSE};
> +STATIC UINTN            mI2cBaseArray[MAX_PLATFORM_I2C_BUS_NUM] = {PLATFORM_I2C_REGISTER_BASE};
> +STATIC DW_I2C_CONTEXT_T mI2cBusList[MAX_PLATFORM_I2C_BUS_NUM];
> +STATIC UINTN            mI2cClock = 0;
> +STATIC EFI_EVENT        mVirtualAddressChangeEvent = NULL;
> +
> +//
> +// Registers
> +//
> +#define DW_IC_CON                       0x0
> +#define DW_IC_CON_MASTER                BIT0
> +#define DW_IC_CON_SPEED_STD             BIT1
> +#define DW_IC_CON_SPEED_FAST            BIT2
> +#define DW_IC_CON_10BITADDR_MASTER      BIT4
> +#define DW_IC_CON_RESTART_EN            BIT5
> +#define DW_IC_CON_SLAVE_DISABLE         BIT6
> +#define DW_IC_TAR                       0x4
> +#define DW_IC_TAR_10BITS                BIT12
> +#define DW_IC_SAR                       0x8
> +#define DW_IC_DATA_CMD                  0x10
> +#define DW_IC_DATA_CMD_RESTART          BIT10
> +#define DW_IC_DATA_CMD_STOP             BIT9
> +#define DW_IC_DATA_CMD_CMD              BIT8
> +#define DW_IC_DATA_CMD_DAT_MASK         0xFF
> +#define DW_IC_SS_SCL_HCNT               0x14
> +#define DW_IC_SS_SCL_LCNT               0x18
> +#define DW_IC_FS_SCL_HCNT               0x1c
> +#define DW_IC_FS_SCL_LCNT               0x20
> +#define DW_IC_HS_SCL_HCNT               0x24
> +#define DW_IC_HS_SCL_LCNT               0x28
> +#define DW_IC_INTR_STAT                 0x2c
> +#define DW_IC_INTR_MASK                 0x30
> +#define DW_IC_INTR_RX_UNDER             BIT0
> +#define DW_IC_INTR_RX_OVER              BIT1
> +#define DW_IC_INTR_RX_FULL              BIT2
> +#define DW_IC_INTR_TX_EMPTY             BIT4
> +#define DW_IC_INTR_TX_ABRT              BIT6
> +#define DW_IC_INTR_ACTIVITY             BIT8
> +#define DW_IC_INTR_STOP_DET             BIT9
> +#define DW_IC_INTR_START_DET            BIT10
> +#define DW_IC_ERR_CONDITION \
> +                (DW_IC_INTR_RX_UNDER | DW_IC_INTR_RX_OVER | DW_IC_INTR_TX_ABRT)
> +#define DW_IC_RAW_INTR_STAT             0x34
> +#define DW_IC_CLR_INTR                  0x40
> +#define DW_IC_CLR_RX_UNDER              0x44
> +#define DW_IC_CLR_RX_OVER               0x48
> +#define DW_IC_CLR_TX_ABRT               0x54
> +#define DW_IC_CLR_ACTIVITY              0x5c
> +#define DW_IC_CLR_STOP_DET              0x60
> +#define DW_IC_CLR_START_DET             0x64
> +#define DW_IC_ENABLE                    0x6c
> +#define DW_IC_STATUS                    0x70
> +#define DW_IC_STATUS_ACTIVITY           BIT0
> +#define DW_IC_STATUS_TFE                BIT2
> +#define DW_IC_STATUS_RFNE               BIT3
> +#define DW_IC_STATUS_MST_ACTIVITY       BIT5
> +#define DW_IC_TXFLR                     0x74
> +#define DW_IC_RXFLR                     0x78
> +#define DW_IC_SDA_HOLD                  0x7c
> +#define DW_IC_TX_ABRT_SOURCE            0x80
> +#define DW_IC_ENABLE_STATUS             0x9c
> +#define DW_IC_COMP_PARAM_1              0xf4
> +#define  DW_IC_COMP_PARAM_1_RX_BUFFER_DEPTH(x) \
> +           ((((x) >> 8) & 0xFF) + 1)
> +#define  DW_IC_COMP_PARAM_1_TX_BUFFER_DEPTH(x) \
> +           ((((x) >> 16) & 0xFF) + 1)
> +#define DW_IC_COMP_TYPE                 0xfc
> +#define SB_DW_IC_CON                    0xa8
> +#define SB_DW_IC_SCL_TMO_CNT            0xac
> +#define SB_DW_IC_RX_PEC                 0xb0
> +#define SB_DW_IC_ACK                    0xb4
> +#define SB_DW_IC_FLG                    0xb8
> +#define SB_DW_IC_FLG_CLR                0xbc
> +#define SB_DW_IC_INTR_STAT              0xc0
> +#define SB_DW_IC_INTR_STAT_MASK         0xc4
> +#define SB_DW_IC_DEBUG_SEL              0xec
> +#define SB_DW_IC_ACK_DEBUG              0xf0
> +#define DW_IC_FS_SPKLEN                 0xa0
> +#define DW_IC_HS_SPKLEN                 0xa4
> +
> +//
> +// Timeout interval
> +//
> +// The interval is equal to the 10 times the signaling period
> +// for the highest I2C transfer speed used in the system.
> +//
> +#define DW_POLL_INTERVAL_US(x) (10 * (1000000 / (x)))
> +
> +//
> +// Maximum timeout count
> +//
> +#define DW_MAX_TRANSFER_POLL_COUNT 100000 // Maximum timeout: 10s
> +#define DW_MAX_STATUS_POLL_COUNT   100
> +
> +#define DW_POLL_MST_ACTIVITY_INTERVAL_US 1000 // 1ms
> +#define DW_MAX_MST_ACTIVITY_POLL_COUNT   20
> +
> +/**
> + Initialize I2C Bus
> + **/
> +VOID
> +I2cHWInit (
> +  UINT32 Bus
> +  )
> +{
> +  UINT32 Param;
> +
> +  mI2cBusList[Bus].Base = mI2cBaseArray[Bus];
> +
> +  Param = MmioRead32 (mI2cBusList[Bus].Base + DW_IC_COMP_PARAM_1);
> +
> +  mI2cBusList[Bus].PollingTime = DW_POLL_INTERVAL_US (mI2cBusList[Bus].BusSpeed);
> +  mI2cBusList[Bus].RxFifo = DW_IC_COMP_PARAM_1_RX_BUFFER_DEPTH (Param);
> +  mI2cBusList[Bus].TxFifo = DW_IC_COMP_PARAM_1_TX_BUFFER_DEPTH (Param);
> +  mI2cBusList[Bus].Enabled = 0;
> +
> +  DEBUG ((DEBUG_VERBOSE, "%a: Bus %d, Rx_Buffer %d, Tx_Buffer %d\n",
> +    __FUNCTION__,
> +    Bus,
> +    mI2cBusList[Bus].RxFifo,
> +    mI2cBusList[Bus].TxFifo
> +    ));
> +}
> +
> +/**
> + Enable or disable I2C Bus
> + */
> +VOID
> +I2cEnable (
> +  UINT32 Bus,
> +  UINT32 Enable
> +  )
> +{
> +  UINT32 I2cStatusCnt;
> +  UINTN  Base;
> +
> +  Base = mI2cBusList[Bus].Base;
> +  I2cStatusCnt = DW_MAX_STATUS_POLL_COUNT;
> +  mI2cBusList[Bus].Enabled = Enable;
> +
> +  MmioWrite32 (Base + DW_IC_ENABLE, Enable);
> +
> +  do {
> +    if ((MmioRead32 (Base + DW_IC_ENABLE_STATUS) & 0x01) == Enable) {
> +      break;
> +    }
> +    MicroSecondDelay (mI2cBusList[Bus].PollingTime);
> +  } while (I2cStatusCnt-- != 0);
> +
> +  if (I2cStatusCnt == 0) {
> +    DEBUG ((DEBUG_ERROR, "%a: Enable/disable timeout\n", __FUNCTION__));
> +  }
> +
> +  if ((Enable == 0) || (I2cStatusCnt == 0)) {
> +    /* Unset the target adddress */
> +    MmioWrite32 (Base + DW_IC_TAR, 0);
> +    mI2cBusList[Bus].Enabled = 0;
> +  }
> +}
> +
> +/**
> + Setup Slave address
> + **/
> +VOID
> +I2cSetSlaveAddr (
> +  UINT32 Bus,
> +  UINT32 SlaveAddr
> +  )
> +{
> +  UINTN  Base;
> +  UINT32 OldEnableStatus;
> +
> +  Base = mI2cBusList[Bus].Base;
> +  OldEnableStatus = mI2cBusList[Bus].Enabled;
> +
> +  I2cEnable (Bus, 0);
> +  MmioWrite32 (Base + DW_IC_TAR, SlaveAddr);
> +  if (OldEnableStatus != 0) {
> +    I2cEnable (Bus, 1);
> +  }
> +}
> +
> +/**
> + Check for errors on I2C Bus
> + **/
> +UINT32
> +I2cCheckErrors (
> +  UINT32 Bus
> +  )
> +{
> +  UINTN  Base;
> +  UINT32 ErrorStatus;
> +
> +  Base = mI2cBusList[Bus].Base;
> +
> +  ErrorStatus = MmioRead32 (Base + DW_IC_RAW_INTR_STAT) & DW_IC_ERR_CONDITION;
> +
> +  if ((ErrorStatus & DW_IC_INTR_RX_UNDER) != 0) {
> +    DEBUG ((DEBUG_ERROR, "%a: RX_UNDER error on i2c bus %d error status %08x\n",
> +      __FUNCTION__,
> +      Bus,
> +      ErrorStatus
> +      ));
> +    MmioRead32 (Base + DW_IC_CLR_RX_UNDER);
> +  }
> +
> +  if ((ErrorStatus & DW_IC_INTR_RX_OVER) != 0) {
> +    DEBUG ((DEBUG_ERROR, "%a: RX_OVER error on i2c bus %d error status %08x\n",
> +      __FUNCTION__,
> +      Bus,
> +      ErrorStatus
> +      ));
> +    MmioRead32 (Base + DW_IC_CLR_RX_OVER);
> +  }
> +
> +  if ((ErrorStatus & DW_IC_INTR_TX_ABRT) != 0) {
> +    DEBUG ((DEBUG_VERBOSE, "%a: TX_ABORT at source %08x\n",
> +      __FUNCTION__,
> +      MmioRead32 (Base + DW_IC_TX_ABRT_SOURCE)
> +      ));
> +    MmioRead32 (Base + DW_IC_CLR_TX_ABRT);
> +  }
> +
> +  return ErrorStatus;
> +}
> +
> +/**
> + Waiting for bus to not be busy
> + **/
> +BOOLEAN
> +I2cWaitBusNotBusy (
> +  UINT32 Bus
> +  )
> +{
> +  UINTN Base;
> +  UINTN PollCount;
> +
> +  Base = mI2cBusList[Bus].Base;
> +  PollCount = DW_MAX_MST_ACTIVITY_POLL_COUNT;
> +
> +  while ((MmioRead32 (Base + DW_IC_STATUS) & DW_IC_STATUS_MST_ACTIVITY) != 0) {
> +    if (PollCount == 0) {
> +      DEBUG ((DEBUG_VERBOSE, "%a: Timeout while waiting for bus ready\n", __FUNCTION__));
> +      return FALSE;
> +    }
> +    PollCount--;
> +    /*
> +     * A delay isn't absolutely necessary.
> +     * But to ensure that we don't hammer the bus constantly,
> +     * delay for DW_POLL_MST_ACTIVITY_INTERVAL_US as with other implementation.
> +     */
> +    MicroSecondDelay (DW_POLL_MST_ACTIVITY_INTERVAL_US);
> +  }
> +
> +  return TRUE;
> +}
> +
> +/**
> + Waiting for TX FIFO buffer available
> + **/
> +EFI_STATUS
> +I2cWaitTxData (
> +  UINT32 Bus
> +  )
> +{
> +  UINTN Base;
> +  UINTN PollCount;
> +
> +  Base = mI2cBusList[Bus].Base;
> +  PollCount = 0;
> +
> +  while (MmioRead32 (Base + DW_IC_TXFLR) == mI2cBusList[Bus].TxFifo) {
> +    if (PollCount++ >= DW_MAX_TRANSFER_POLL_COUNT) {
> +      DEBUG ((DEBUG_ERROR, "%a: Timeout waiting for TX buffer available\n", __FUNCTION__));
> +      return EFI_TIMEOUT;
> +    }
> +    MicroSecondDelay (mI2cBusList[Bus].PollingTime);
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> + Waiting for RX FIFO buffer available
> + **/
> +EFI_STATUS
> +I2cWaitRxData (
> +  UINT32 Bus
> +  )
> +{
> +  UINTN Base;
> +  UINTN PollCount;
> +
> +  Base = mI2cBusList[Bus].Base;
> +  PollCount = 0;
> +
> +  while ((MmioRead32 (Base + DW_IC_STATUS) & DW_IC_STATUS_RFNE) == 0) {
> +    if (PollCount++ >= DW_MAX_TRANSFER_POLL_COUNT) {
> +      DEBUG ((DEBUG_ERROR, "%a: Timeout waiting for RX buffer available\n", __FUNCTION__));
> +      return EFI_TIMEOUT;
> +    }
> +
> +    if ((I2cCheckErrors (Bus) & DW_IC_INTR_TX_ABRT) != 0) {
> +      return EFI_ABORTED;
> +    }
> +
> +    MicroSecondDelay (mI2cBusList[Bus].PollingTime);
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> + Initialize the Designware I2C SCL Counts
> +
> + This functions configures SCL clock Count for Standard Speed (SS) and Fast Speed (FS) mode.
> + **/
> +VOID
> +I2cSclInit (
> +  UINT32 Bus,
> +  UINT32 I2cClkFreq,
> +  UINT32 I2cSpeed
> +  )
> +{
> +  UINT16 IcCon;
> +  UINTN  Base;
> +  UINT32 I2cSpeedKhz;
> +
> +  Base = mI2cBusList[Bus].Base;
> +  I2cSpeedKhz = I2cSpeed / 1000;
> +
> +  DEBUG ((DEBUG_VERBOSE, "%a: Bus %d I2cClkFreq %d I2cSpeed %d\n",
> +    __FUNCTION__,
> +    Bus,
> +    I2cClkFreq,
> +    I2cSpeed
> +    ));
> +
> +  IcCon = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | DW_IC_CON_RESTART_EN;
> +
> +  if (I2cSpeedKhz <= 100) {
> +    IcCon |= DW_IC_CON_SPEED_STD;
> +    // Standard speed mode
> +    MmioWrite32 (Base + DW_IC_FS_SPKLEN, I2cSclParam[I2cSpeedModeStandard][I2cSclSpkLen]);
> +    MmioWrite32 (Base + DW_IC_SS_SCL_HCNT, I2cSclParam[I2cSpeedModeStandard][I2cSclHcnt]);
> +    MmioWrite32 (Base + DW_IC_SS_SCL_LCNT, I2cSclParam[I2cSpeedModeStandard][I2cSclLcnt]);
> +  } else if (I2cSpeedKhz > 100 && I2cSpeedKhz <= 400) {
> +    IcCon |= DW_IC_CON_SPEED_FAST;
> +    // Fast speed mode
> +    MmioWrite32 (Base + DW_IC_FS_SPKLEN, I2cSclParam[I2cSpeedModeFast][I2cSclSpkLen]);
> +    MmioWrite32 (Base + DW_IC_FS_SCL_HCNT, I2cSclParam[I2cSpeedModeFast][I2cSclHcnt]);
> +    MmioWrite32 (Base + DW_IC_FS_SCL_LCNT, I2cSclParam[I2cSpeedModeFast][I2cSclLcnt]);
> +  }
> +  MmioWrite32 (Base + DW_IC_CON, IcCon);
> +}
> +
> +/**
> + Initialize the designware i2c master hardware
> + **/
> +EFI_STATUS
> +I2cInit (
> +  UINT32 Bus,
> +  UINTN  BusSpeed
> +  )
> +{
> +  UINTN Base;
> +
> +  ASSERT (mI2cClock != 0);
> +
> +  mI2cBusList[Bus].BusSpeed = BusSpeed;
> +  I2cHWInit (Bus);
> +
> +  Base = mI2cBusList[Bus].Base;
> +
> +  /* Disable the adapter and interrupt */
> +  I2cEnable (Bus, 0);
> +  MmioWrite32 (Base + DW_IC_INTR_MASK, 0);
> +
> +  /* Set standard and fast speed divider for high/low periods */
> +  I2cSclInit (Bus, mI2cClock, BusSpeed);
> +  MmioWrite32 (Base + DW_IC_SDA_HOLD, 0x4b);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> + Wait the transaction finished
> + **/
> +EFI_STATUS
> +I2cFinish (
> +  UINT32 Bus
> +  )
> +{
> +  UINTN Base;
> +  UINTN PollCount;
> +
> +  Base = mI2cBusList[Bus].Base;
> +  PollCount = 0;
> +
> +  /* Wait for TX FIFO empty */
> +  do {
> +    if ((MmioRead32 (Base + DW_IC_STATUS) & DW_IC_STATUS_TFE) != 0) {
> +      break;
> +    }
> +    MicroSecondDelay (mI2cBusList[Bus].PollingTime);
> +  } while (PollCount++ < DW_MAX_TRANSFER_POLL_COUNT);
> +
> +  if (PollCount >= DW_MAX_TRANSFER_POLL_COUNT) {
> +    DEBUG ((DEBUG_ERROR, "%a: Timeout waiting for TX FIFO empty\n", __FUNCTION__));
> +    return EFI_TIMEOUT;
> +  }
> +
> +  /* Wait for STOP signal detected on the bus */
> +  PollCount = 0;
> +  do {
> +    if ((MmioRead32 (Base + DW_IC_RAW_INTR_STAT) & DW_IC_INTR_STOP_DET) != 0) {
> +      MmioRead32 (Base + DW_IC_CLR_STOP_DET);
> +      return EFI_SUCCESS;
> +    }
> +    MicroSecondDelay (mI2cBusList[Bus].PollingTime);
> +  } while (PollCount++ < DW_MAX_TRANSFER_POLL_COUNT);
> +
> +  DEBUG ((DEBUG_ERROR, "%a: Timeout waiting for transaction finished\n", __FUNCTION__));
> +  return EFI_TIMEOUT;
> +}
> +
> +EFI_STATUS
> +InternalI2cWrite (
> +  UINT32 Bus,
> +  UINT8  *Buf,
> +  UINT32 *Length
> +  )
> +{
> +  EFI_STATUS Status;
> +  UINTN      WriteCount;
> +  UINTN      Base;
> +
> +  Status = EFI_SUCCESS;
> +  Base = mI2cBusList[Bus].Base;
> +
> +  DEBUG ((DEBUG_VERBOSE, "%a: Write Bus %d Buf %p Length %d\n",
> +    __FUNCTION__,
> +    Bus,
> +    Buf,
> +    *Length
> +    ));
> +  I2cEnable (Bus, 1);
> +
> +  WriteCount = 0;
> +  while ((*Length - WriteCount) != 0) {
> +    Status = I2cWaitTxData (Bus);
> +    if (EFI_ERROR (Status)) {
> +      MmioWrite32 (Base + DW_IC_DATA_CMD, DW_IC_DATA_CMD_STOP);
> +      I2cSync ();
> +      goto Exit;
> +    }
> +
> +    if (WriteCount == *Length - 1) {
> +      MmioWrite32 (
> +        Base + DW_IC_DATA_CMD,
> +        (Buf[WriteCount] & DW_IC_DATA_CMD_DAT_MASK) | DW_IC_DATA_CMD_STOP
> +        );
> +    } else {
> +      MmioWrite32 (
> +        Base + DW_IC_DATA_CMD,
> +        Buf[WriteCount] & DW_IC_DATA_CMD_DAT_MASK
> +        );
> +    }
> +    I2cSync ();
> +    WriteCount++;
> +  }
> +
> +Exit:
> +  *Length = WriteCount;
> +  I2cFinish (Bus);
> +  I2cWaitBusNotBusy (Bus);
> +  I2cEnable (Bus, 0);
> +
> +  return Status;
> +}
> +
> +EFI_STATUS
> +InternalI2cRead (
> +  UINT32  Bus,
> +  UINT8  *BufCmd,
> +  UINT32 CmdLength,
> +  UINT8  *Buf,
> +  UINT32 *Length
> +  )
> +{
> +  EFI_STATUS Status;
> +  UINTN      Base;
> +  UINT32     CmdSend;
> +  UINT32     TxLimit, RxLimit;
> +  UINTN      Idx;
> +  UINTN      Count;
> +  UINTN      ReadCount;
> +  UINTN      WriteCount;
> +
> +  Status = EFI_SUCCESS;
> +  Base = mI2cBusList[Bus].Base;
> +  Count = 0;
> +
> +  DEBUG ((DEBUG_VERBOSE, "%a: Read Bus %d Buf %p Length:%d\n",
> +    __FUNCTION__,
> +    Bus,
> +    Buf,
> +    *Length
> +    ));
> +
> +  I2cEnable (Bus, 1);
> +
> +  /* Write command data */
> +  WriteCount = 0;
> +  while (CmdLength != 0) {
> +    TxLimit = mI2cBusList[Bus].TxFifo - MmioRead32 (Base + DW_IC_TXFLR);
> +    Count = CmdLength > TxLimit ? TxLimit : CmdLength;
> +
> +    for (Idx = 0; Idx < Count; Idx++ ) {
> +      CmdSend = BufCmd[WriteCount++] & DW_IC_DATA_CMD_DAT_MASK;
> +      MmioWrite32 (Base + DW_IC_DATA_CMD, CmdSend);
> +      I2cSync ();
> +
> +      if (I2cCheckErrors (Bus) != 0) {
> +        Status = EFI_CRC_ERROR;
> +        goto Exit;
> +      }
> +      CmdLength--;
> +    }
> +
> +    Status = I2cWaitTxData (Bus);
> +    if (EFI_ERROR (Status)) {
> +      MmioWrite32 (Base + DW_IC_DATA_CMD, DW_IC_DATA_CMD_STOP);
> +      I2cSync ();
> +      goto Exit;
> +    }
> +  }
> +
> +  ReadCount = 0;
> +  WriteCount = 0;
> +  while ((*Length - ReadCount) != 0) {
> +    TxLimit = mI2cBusList[Bus].TxFifo - MmioRead32 (Base + DW_IC_TXFLR);
> +    RxLimit = mI2cBusList[Bus].RxFifo - MmioRead32 (Base + DW_IC_RXFLR);
> +    Count = *Length - ReadCount;
> +    Count = Count > RxLimit ? RxLimit : Count;
> +    Count = Count > TxLimit ? TxLimit : Count;
> +
> +    for (Idx = 0; Idx < Count; Idx++ ) {
> +      CmdSend = DW_IC_DATA_CMD_CMD;
> +      if (WriteCount == *Length - 1) {
> +        CmdSend |= DW_IC_DATA_CMD_STOP;
> +      }
> +      MmioWrite32 (Base + DW_IC_DATA_CMD, CmdSend);
> +      I2cSync ();
> +      WriteCount++;
> +
> +      if (I2cCheckErrors (Bus) != 0) {
> +        DEBUG ((DEBUG_VERBOSE,
> +          "%a: Sending reading command remaining length %d CRC error\n",
> +          __FUNCTION__,
> +          *Length
> +          ));
> +        Status = EFI_CRC_ERROR;
> +        goto Exit;
> +      }
> +    }
> +
> +    for (Idx = 0; Idx < Count; Idx++ ) {
> +      Status = I2cWaitRxData (Bus);
> +      if (EFI_ERROR (Status)) {
> +        DEBUG ((DEBUG_VERBOSE,
> +          "%a: Reading remaining length %d failed to wait data\n",
> +          __FUNCTION__,
> +          *Length
> +          ));
> +
> +        if (Status != EFI_ABORTED) {
> +          MmioWrite32 (Base + DW_IC_DATA_CMD, DW_IC_DATA_CMD_STOP);
> +          I2cSync ();
> +        }
> +
> +        goto Exit;
> +      }
> +
> +      Buf[ReadCount++] = MmioRead32 (Base + DW_IC_DATA_CMD) & DW_IC_DATA_CMD_DAT_MASK;
> +      I2cSync ();
> +
> +      if (I2cCheckErrors (Bus) != 0) {
> +        DEBUG ((DEBUG_VERBOSE, "%a: Reading remaining length %d CRC error\n",
> +          __FUNCTION__,
> +          *Length
> +          ));
> +        Status = EFI_CRC_ERROR;
> +        goto Exit;
> +      }
> +    }
> +  }
> +
> +Exit:
> +  *Length = ReadCount;
> +  I2cFinish (Bus);
> +  I2cWaitBusNotBusy (Bus);
> +  I2cEnable (Bus, 0);
> +
> +  return Status;
> +}
> +
> +/**
> +  Write to I2C bus.
> +
> +  @param[in]     Bus          I2C bus Id.
> +  @param[in]     SlaveAddr    The address of slave device on the bus.
> +  @param[in,out] Buf          Buffer that holds data to write.
> +  @param[in,out] WriteLength  Pointer to length of buffer.
> +
> +  @return EFI_SUCCESS            Write successfully.
> +  @return EFI_INVALID_PARAMETER  A parameter is invalid.
> +  @return EFI_UNSUPPORTED        The bus is not supported.
> +  @return EFI_NOT_READY          The device/bus is not ready.
> +  @return EFI_TIMEOUT            Timeout why transferring data.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +I2cWrite (
> +  IN     UINT32 Bus,
> +  IN     UINT32 SlaveAddr,
> +  IN OUT UINT8  *Buf,
> +  IN OUT UINT32 *WriteLength
> +  )
> +{
> +  if (Bus >= MAX_PLATFORM_I2C_BUS_NUM
> +      || Buf == NULL
> +      || WriteLength == NULL)
> +  {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  I2cSetSlaveAddr (Bus, SlaveAddr);
> +
> +  return InternalI2cWrite (Bus, Buf, WriteLength);
> +}
> +
> +/**
> +  Read data from I2C bus.
> +
> +  @param[in]     Bus          I2C bus Id.
> +  @param[in]     SlaveAddr    The address of slave device on the bus.
> +  @param[in]     BufCmd       Buffer where to send the command.
> +  @param[in]     CmdLength    Pointer to length of BufCmd.
> +  @param[in,out] Buf          Buffer where to put the read data to.
> +  @param[in,out] ReadLength   Pointer to length of buffer.
> +
> +  @return EFI_SUCCESS            Read successfully.
> +  @return EFI_INVALID_PARAMETER  A parameter is invalid.
> +  @return EFI_UNSUPPORTED        The bus is not supported.
> +  @return EFI_NOT_READY          The device/bus is not ready.
> +  @return EFI_TIMEOUT            Timeout why transferring data.
> +  @return EFI_CRC_ERROR          There are errors on receiving data.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +I2cRead (
> +  IN     UINT32 Bus,
> +  IN     UINT32 SlaveAddr,
> +  IN     UINT8  *BufCmd,
> +  IN     UINT32 CmdLength,
> +  IN OUT UINT8  *Buf,
> +  IN OUT UINT32 *ReadLength
> +  )
> +{
> +  if (Bus >= MAX_PLATFORM_I2C_BUS_NUM
> +      || Buf == NULL
> +      || ReadLength == NULL)
> +  {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  I2cSetSlaveAddr (Bus, SlaveAddr);
> +
> +  return InternalI2cRead (Bus, BufCmd, CmdLength, Buf, ReadLength);
> +}
> +
> +/**
> + Setup new transaction with I2C slave device.
> +
> +  @param[in] Bus      I2C bus Id.
> +  @param[in] BusSpeed I2C bus speed in Hz.
> +
> +  @retval EFI_SUCCESS           Success.
> +  @retval EFI_INVALID_PARAMETER A parameter is invalid.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +I2cProbe (
> +  IN UINT32 Bus,
> +  IN UINTN  BusSpeed
> +  )
> +{
> +  if (Bus >= MAX_PLATFORM_I2C_BUS_NUM
> +      || BusSpeed > DW_I2C_MAXIMUM_SPEED_HZ)
> +  {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  return I2cInit (Bus, BusSpeed);
> +}
> +
> +/**
> + * Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
> + *
> + * This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
> + * It convers pointer to new virtual address.
> + *
> + * @param  Event        Event whose notification function is being invoked.
> + * @param  Context      Pointer to the notification function's context.
> + */
> +VOID
> +EFIAPI
> +I2cVirtualAddressChangeEvent (
> +  IN EFI_EVENT Event,
> +  IN VOID      *Context
> +  )
> +{
> +  UINTN Count;
> +
> +  EfiConvertPointer (0x0, (VOID **)&mI2cBusList);
> +  EfiConvertPointer (0x0, (VOID **)&mI2cBaseArray);
> +  EfiConvertPointer (0x0, (VOID **)&mI2cClock);
> +  for (Count = 0; Count < MAX_PLATFORM_I2C_BUS_NUM; Count++) {
> +    if (!mI2cRuntimeEnableArray[Count]) {
> +      continue;
> +    }
> +    EfiConvertPointer (0x0, (VOID **)&mI2cBaseArray[Count]);
> +    EfiConvertPointer (0x0, (VOID **)&mI2cBusList[Count].Base);
> +  }
> +}
> +
> +/**
> + Setup a bus that to be used in runtime service.
> +
> +  @param[in] Bus I2C bus Id.
> +
> +  @retval EFI_SUCCESS  Success.
> +  @retval Otherwise    Error code.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +I2cSetupRuntime (
> +  IN UINT32 Bus
> +  )
> +{
> +  EFI_STATUS                      Status;
> +  EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;
> +
> +  if (Bus >= MAX_PLATFORM_I2C_BUS_NUM) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (mVirtualAddressChangeEvent == NULL) {
> +    /*
> +     * Register for the virtual address change event
> +     */
> +    Status = gBS->CreateEventEx (
> +                    EVT_NOTIFY_SIGNAL,
> +                    TPL_NOTIFY,
> +                    I2cVirtualAddressChangeEvent,
> +                    NULL,
> +                    &gEfiEventVirtualAddressChangeGuid,
> +                    &mVirtualAddressChangeEvent
> +                    );
> +    ASSERT_EFI_ERROR (Status);
> +  }
> +
> +  Status = gDS->GetMemorySpaceDescriptor (
> +                  mI2cBaseArray[Bus] & RUNTIME_ADDRESS_MASK,
> +                  &Descriptor
> +                  );
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  Status = gDS->SetMemorySpaceAttributes (
> +                  mI2cBaseArray[Bus] & RUNTIME_ADDRESS_MASK,
> +                  RUNTIME_ADDRESS_LENGTH,
> +                  Descriptor.Attributes | EFI_MEMORY_RUNTIME
> +                  );
> +  if (EFI_ERROR (Status)) {
> +    return Status;
> +  }
> +
> +  mI2cRuntimeEnableArray[Bus] = TRUE;
> +
> +  return Status;
> +}
> +
> +EFI_STATUS
> +EFIAPI
> +I2cLibConstructor (
> +  VOID
> +  )
> +{
> +  VOID               *Hob;
> +  PLATFORM_INFO_HOB  *PlatformHob;
> +
> +  /* Get I2C Clock from the Platform HOB */
> +  Hob = GetFirstGuidHob (&gPlatformHobGuid);
> +  if (Hob == NULL) {
> +    return EFI_NOT_FOUND;
> +  }
> +  PlatformHob = (PLATFORM_INFO_HOB *)GET_GUID_HOB_DATA (Hob);
> +  mI2cClock = PlatformHob->AhbClk;
> +  ASSERT (mI2cClock != 0);
> +
> +  return EFI_SUCCESS;
> +}
> -- 
> 2.17.1
> 


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#76086): https://edk2.groups.io/g/devel/message/76086
Mute This Topic: https://groups.io/mt/83097099/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-





More information about the edk2-devel-archive mailing list