public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Nhi Pham" <nhi@os.amperecomputing.com>
To: devel@edk2.groups.io
Cc: Vu Nguyen <vunguyen@os.amperecomputing.com>
Subject: [edk2-platforms][PATCH 04/34] AmpereAltraPkg: Implement GpioLib and I2cLib modules
Date: Wed,  9 Dec 2020 16:25:01 +0700	[thread overview]
Message-ID: <20201209092531.30867-5-nhi@os.amperecomputing.com> (raw)
In-Reply-To: <20201209092531.30867-1-nhi@os.amperecomputing.com>

From: Vu Nguyen <vunguyen@os.amperecomputing.com>

Provide basic functions for GPIO and I2C on Ampere Altra.

Signed-off-by: Vu Nguyen <vunguyen@os.amperecomputing.com>
---
 Silicon/Ampere/AmpereAltraPkg/Ac01Pkg.dec                           |   6 +
 Silicon/Ampere/AmpereAltraPkg/Library/DWI2CLib/I2CLib.inf           |  36 +
 Silicon/Ampere/AmpereAltraPkg/Library/DwapbGpioLib/DwapbGpioLib.inf |  32 +
 Silicon/Ampere/AmpereAltraPkg/Include/Library/DwapbGpioLib.h        |  75 ++
 Silicon/Ampere/AmpereAltraPkg/Include/Library/I2CLib.h              |  92 ++
 Silicon/Ampere/AmpereAltraPkg/Library/DWI2CLib/I2CLib.c             | 937 ++++++++++++++++++++
 Silicon/Ampere/AmpereAltraPkg/Library/DwapbGpioLib/DwapbGpioLib.c   | 313 +++++++
 7 files changed, 1491 insertions(+)

diff --git a/Silicon/Ampere/AmpereAltraPkg/Ac01Pkg.dec b/Silicon/Ampere/AmpereAltraPkg/Ac01Pkg.dec
index acf5a9621a4c..c34d580255b9 100644
--- a/Silicon/Ampere/AmpereAltraPkg/Ac01Pkg.dec
+++ b/Silicon/Ampere/AmpereAltraPkg/Ac01Pkg.dec
@@ -25,6 +25,12 @@ [LibraryClasses]
   ##  @libraryclass  Defines a set of methods to communicate with SMPro.
   SMProLib|Silicon/Ampere/AmpereAltraPkg/Include/Library/SMProLib.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 get/set GPIO.
+  DwapbGpioLib|Silicon/Ampere/AmpereAltraPkg/Include/Library/DwapbGpioLib.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/I2CLib.inf b/Silicon/Ampere/AmpereAltraPkg/Library/DWI2CLib/I2CLib.inf
new file mode 100644
index 000000000000..c41571f53c18
--- /dev/null
+++ b/Silicon/Ampere/AmpereAltraPkg/Library/DWI2CLib/I2CLib.inf
@@ -0,0 +1,36 @@
+## @file
+#
+# Copyright (c) 2020, Ampere Computing LLC. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x0001001B
+  BASE_NAME                      = I2CLib
+  FILE_GUID                      = 222609E2-C181-11E6-A4A6-CEC0C932CE01
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = I2CLib
+  CONSTRUCTOR                    = I2CLibConstructor
+
+[Sources]
+  I2CLib.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  ArmPkg/ArmPkg.dec
+  ArmPlatformPkg/ArmPlatformPkg.dec
+  Silicon/Ampere/AmpereAltraPkg/Ac01Pkg.dec
+
+[LibraryClasses]
+  IoLib
+  DebugLib
+  BaseLib
+  BaseMemoryLib
+  TimerLib
+  HobLib
+
+[Guids]
+  gEfiEventVirtualAddressChangeGuid
diff --git a/Silicon/Ampere/AmpereAltraPkg/Library/DwapbGpioLib/DwapbGpioLib.inf b/Silicon/Ampere/AmpereAltraPkg/Library/DwapbGpioLib/DwapbGpioLib.inf
new file mode 100644
index 000000000000..95ebf861e358
--- /dev/null
+++ b/Silicon/Ampere/AmpereAltraPkg/Library/DwapbGpioLib/DwapbGpioLib.inf
@@ -0,0 +1,32 @@
+## @file
+#
+# Copyright (c) 2020, Ampere Computing LLC. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x0001001B
+  BASE_NAME                      = DwGpioLib
+  FILE_GUID                      = E7D9CAE1-6930-46E3-BDF9-0027446E7DF2
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = DwGpioSocLib
+
+[Sources.common]
+  DwapbGpioLib.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  ArmPkg/ArmPkg.dec
+  ArmPlatformPkg/ArmPlatformPkg.dec
+  Silicon/Ampere/AmpereAltraPkg/Ac01Pkg.dec
+
+[LibraryClasses]
+  IoLib
+  BaseLib
+  BaseMemoryLib
+
+[Guids]
+  gEfiEventVirtualAddressChangeGuid
diff --git a/Silicon/Ampere/AmpereAltraPkg/Include/Library/DwapbGpioLib.h b/Silicon/Ampere/AmpereAltraPkg/Include/Library/DwapbGpioLib.h
new file mode 100755
index 000000000000..9c151da55600
--- /dev/null
+++ b/Silicon/Ampere/AmpereAltraPkg/Include/Library/DwapbGpioLib.h
@@ -0,0 +1,75 @@
+/** @file
+
+  Copyright (c) 2020, Ampere Computing LLC. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _DWAPB_GPIO_LIB_H_
+#define _DWAPB_GPIO_LIB_H_
+
+enum SocGpioConfigMode {
+  GPIO_CONFIG_OUT_LOW = 0,
+  GPIO_CONFIG_OUT_HI,
+  GPIO_CONFIG_OUT_LOW_TO_HIGH,
+  GPIO_CONFIG_OUT_HIGH_TO_LOW,
+  GPIO_CONFIG_IN,
+  MAX_GPIO_CONFIG_MODE
+};
+
+/*
+ *  DwapbGpioWriteBit: Use to Set/Clear GPIOs
+ *  Input:
+ *              Pin : Pin Identification
+ *              Val : 1 to Set, 0 to Clear
+ */
+VOID
+EFIAPI
+DwapbGpioWriteBit (
+  IN UINT32 Pin,
+  IN UINT32 Val
+  );
+
+/*
+ *   DwapbGpioReadBit:
+ *   Input:
+ *              Pin : Pin Identification
+ *   Return:
+ *              1 : On/High
+ *              0 : Off/Low
+ */
+UINTN
+EFIAPI
+DwapbGpioReadBit (
+  IN UINT32 Pin
+  );
+
+/*
+ *  DwapbGPIOModeConfig: Use to configure GPIOs as Input/Output
+ *  Input:
+ *              Pin : Pin Identification
+ *              InOut : GPIO_OUT/1 as Output
+ *                      GPIO_IN/0  as Input
+ */
+EFI_STATUS
+EFIAPI
+DwapbGPIOModeConfig (
+  UINT8 Pin,
+  UINTN Mode
+  );
+
+/*
+ *  Setup a controller that to be used in runtime service.
+ *  Input:
+ *              Pin: Pin belongs to the controller.
+ *  return:     0 for success.
+ *              Otherwise, error code.
+ */
+EFI_STATUS
+EFIAPI
+DwapbGPIOSetupRuntime (
+  IN UINT32 Pin
+  );
+
+#endif /* _DWAPB_GPIO_LIB_H_ */
diff --git a/Silicon/Ampere/AmpereAltraPkg/Include/Library/I2CLib.h b/Silicon/Ampere/AmpereAltraPkg/Include/Library/I2CLib.h
new file mode 100644
index 000000000000..587f6814f5b9
--- /dev/null
+++ b/Silicon/Ampere/AmpereAltraPkg/Include/Library/I2CLib.h
@@ -0,0 +1,92 @@
+/** @file
+
+  Copyright (c) 2020, Ampere Computing LLC. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _I2CLIB_H_
+#define _I2CLIB_H_
+
+#include <Base.h>
+#include <Uefi/UefiBaseType.h>
+
+/**
+ Write to I2C bus.
+
+ @Bus:          Bus ID.
+ @SlaveAddr:    The address of slave device in the bus.
+ @Buf:          Buffer that holds data to write.
+ @WriteLength:  Pointer to length of buffer.
+ @return:       EFI_INVALID_PARAMETER if parameter is invalid.
+                EFI_UNSUPPORTED if the bus is not supported.
+                EFI_NOT_READY if the device/bus is not ready.
+                EFI_TIMEOUT if timeout why transferring data.
+                Otherwise, 0 for success.
+ **/
+EFI_STATUS
+EFIAPI
+I2CWrite (
+  IN     UINT32 Bus,
+  IN     UINT32 SlaveAddr,
+  IN OUT UINT8  *Buf,
+  IN OUT UINT32 *WriteLength
+  );
+
+/**
+ Read data from I2C bus.
+
+ @Bus:          Bus ID.
+ @SlaveAddr:    The address of slave device in the bus.
+ @BufCmd:       Buffer where to send the command.
+ @CmdLength:    Pointer to length of BufCmd.
+ @Buf:          Buffer where to put the read data to.
+ @ReadLength:   Pointer to length of buffer.
+ @return:       EFI_INVALID_PARAMETER if parameter is invalid.
+                EFI_UNSUPPORTED if the bus is not supported.
+                EFI_NOT_READY if the device/bus is not ready.
+                EFI_TIMEOUT if timeout why transferring data.
+                EFI_CRC_ERROR if there are errors on receiving data.
+                Otherwise, 0 for success.
+ **/
+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.
+
+ @Bus:      Bus ID.
+ @BusSpeed: Bus speed in Hz.
+ @return:   EFI_INVALID_PARAMETER if parameter is invalid.
+            Otherwise, 0 for success.
+ **/
+EFI_STATUS
+EFIAPI
+I2CProbe (
+  IN UINT32 Bus,
+  IN UINTN  BusSpeed
+  );
+
+/**
+ Setup a bus that to be used in runtime service.
+
+ @Bus:      Bus ID.
+ @return:   0 for success.
+            Otherwise, error code.
+ **/
+EFI_STATUS
+EFIAPI
+I2CSetupRuntime (
+  IN UINT32 Bus
+  );
+
+#endif /* _I2CLIB_H_ */
diff --git a/Silicon/Ampere/AmpereAltraPkg/Library/DWI2CLib/I2CLib.c b/Silicon/Ampere/AmpereAltraPkg/Library/DWI2CLib/I2CLib.c
new file mode 100755
index 000000000000..37a7463549df
--- /dev/null
+++ b/Silicon/Ampere/AmpereAltraPkg/Library/DWI2CLib/I2CLib.c
@@ -0,0 +1,937 @@
+/** @file
+
+  Copyright (c) 2020, Ampere Computing LLC. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+#include <Base.h>
+#include <Uefi/UefiBaseType.h>
+#include <Library/UefiRuntimeLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/TimerLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PcdLib.h>
+#include <Library/IoLib.h>
+#include <Library/I2CLib.h>
+#include <Library/HobLib.h>
+#include <PlatformInfoHob.h>
+
+#undef I2C_DBG
+#undef I2C_PRINT
+
+#ifdef I2C_DBG
+#define DBG(arg...) DEBUG ((DEBUG_ERROR, "DW_I2C(DBG): "));DEBUG ((DEBUG_ERROR,## arg))
+#else
+#define DBG(arg...)
+#endif
+
+#define ERROR(arg...) DEBUG ((DEBUG_ERROR, "DW_I2C(ERROR): "));DEBUG ((DEBUG_ERROR,## arg))
+
+#ifdef I2C_PRINT
+#define PRINT(arg...) DEBUG ((DEBUG_INFO, "DW_I2C(INFO): "));DEBUG ((DEBUG_INFO,## arg))
+#else
+#define PRINT(arg...)
+#endif
+
+/* Runtime needs to be 64K alignment */
+#define RUNTIME_ADDRESS_MASK           (~(SIZE_64KB - 1))
+#define RUNTIME_ADDRESS_LENGTH         SIZE_64KB
+
+/* Bus specific values */
+typedef struct DW_I2C_CONTEXT {
+  UINTN  Base;
+  UINT32 BusSpeed;
+  UINT32 RxFifo;
+  UINT32 TxFifo;
+  UINT32 PollingTime;
+  UINT32 Enabled;
+} DW_I2C_CONTEXT_T;
+
+/* I2C SCL counter macros */
+enum {
+  I2C_SS = 0,
+  I2C_FS,
+  I2C_PS,
+  I2C_HS_400PF,
+  I2C_HS_100PF,
+};
+#define I2C_HS I2C_HS_400PF
+
+enum {
+  I2C_SCL_HIGH = 0,
+  I2C_SCL_LOW,
+  I2C_SCL_TF,
+};
+
+enum {
+  I2C_SPKLEN = 0,
+  I2C_OFFSET,
+};
+
+#define SS_SCL_HCNT  250
+#define SS_SCL_LCNT  250
+#define FS_SCL_HCNT  62
+#define FS_SCL_LCNT  63
+
+#define I2CSync() { asm volatile ("dmb ish" : : : "memory"); }
+
+STATIC UINT32 I2CSclMin[][3] = {  /* in nano seconds */
+  /* High, Low, tf */
+  [I2C_SS] =   {4000, 4700, 300},  /* SS (Standard Speed) */
+  [I2C_FS] =   { 600, 1300, 300},  /* FS (Fast Speed) */
+  [I2C_PS] =   { 260, 500, 120},   /* PS (Fast Plus Speed) */
+  [I2C_HS_400PF] = { 160,  320, 300},  /* HS (High Speed) 400pf */
+  [I2C_HS_100PF] = {  60,  120, 300},  /* HS (High Speed) 100pf */
+};
+
+STATIC UINT32 I2CSclParam[][2] = {
+  /* Spklen, offset */
+  [I2C_SS] =   {10, 300},  /* SS (Standard Speed) */
+  [I2C_FS] =   {10, 0},    /* FS (Fast Speed) */
+  [I2C_PS] =   {10, 0},    /* PS (Fast Plus Speed) */
+  [I2C_HS_400PF] = {0, 0}, /* HS (High Speed) 400pf */
+  [I2C_HS_100PF] = {0, 0}, /* HS (High Speed) 100pf */
+};
+
+STATIC BOOLEAN            I2CRuntimeEnableArray[MAX_PLATFORM_I2C_BUS_NUM] = {FALSE};
+STATIC UINTN              I2CBaseArray[MAX_PLATFORM_I2C_BUS_NUM] = {PLATFORM_I2C_REGISTER_BASE};
+STATIC DW_I2C_CONTEXT_T   I2CBusList[MAX_PLATFORM_I2C_BUS_NUM];
+STATIC UINTN              I2CClock = 0;
+STATIC EFI_EVENT          mVirtualAddressChangeEvent = NULL;
+
+#ifndef BIT
+#define BIT(nr)                         (1 << (nr))
+#endif
+
+#define DW_SIGNATURE                    0x44570000  /* 'D' 'W' */
+
+/*
+ * Registers
+ */
+#define DW_IC_CON                       0x0
+#define  DW_IC_CON_MASTER               BIT(0)
+#define  DW_IC_CON_SPEED_STD            BIT(1)
+#define  DW_IC_CON_SPEED_FAST           BIT(2)
+#define  DW_IC_CON_10BITADDR_MASTER     BIT(4)
+#define  DW_IC_CON_RESTART_EN           BIT(5)
+#define  DW_IC_CON_SLAVE_DISABLE        BIT(6)
+#define DW_IC_TAR                       0x4
+#define  DW_IC_TAR_10BITS               BIT(12)
+#define DW_IC_SAR                       0x8
+#define DW_IC_DATA_CMD                  0x10
+#define  DW_IC_DATA_CMD_RESTART         BIT(10)
+#define  DW_IC_DATA_CMD_STOP            BIT(9)
+#define  DW_IC_DATA_CMD_CMD             BIT(8)
+#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            BIT(0)
+#define  DW_IC_INTR_RX_OVER             BIT(1)
+#define  DW_IC_INTR_RX_FULL             BIT(2)
+#define  DW_IC_INTR_TX_EMPTY            BIT(4)
+#define  DW_IC_INTR_TX_ABRT             BIT(6)
+#define  DW_IC_INTR_ACTIVITY            BIT(8)
+#define  DW_IC_INTR_STOP_DET            BIT(9)
+#define  DW_IC_INTR_START_DET           BIT(10)
+#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          BIT(0)
+#define  DW_IC_STATUS_TFE               BIT(2)
+#define  DW_IC_STATUS_RFNE              BIT(3)
+#define  DW_IC_STATUS_MST_ACTIVITY      BIT(5)
+#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_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
+
+#define DW_BUS_WAIT_SLEEP               1000 /* 1ms */
+#define DW_BUS_WAIT_TIMEOUT_RETRY       20
+#define DW_TRANSFER_DATA_TIMEOUT        10000000 /* Max 10s */
+#define DW_STATUS_WAIT_RETRY            100
+
+UINT32
+Read32 (
+  UINTN Addr
+  )
+{
+  return MmioRead32 (Addr);
+}
+
+VOID
+Write32 (
+  UINTN Addr,
+  UINT32 Val
+  )
+{
+  MmioWrite32 (Addr, Val);
+}
+
+/**
+ Initialize I2C Bus
+ **/
+VOID
+I2CHWInit (
+  UINT32 Bus
+  )
+{
+  UINT32    Param;
+
+  I2CBusList[Bus].Base = I2CBaseArray[Bus];
+  Param = Read32 (I2CBusList[Bus].Base + DW_IC_COMP_PARAM_1);
+  I2CBusList[Bus].PollingTime = (10 * 1000000) / I2CBusList[Bus].BusSpeed;
+  I2CBusList[Bus].RxFifo = ((Param >> 8) & 0xff) + 1;
+  I2CBusList[Bus].TxFifo = ((Param >> 16) & 0xff) + 1;
+  I2CBusList[Bus].Enabled = 0;
+  DBG ("Bus %d Rx_Buffer %d Tx_Buffer %d\n",
+      Bus, I2CBusList[Bus].RxFifo, I2CBusList[Bus].TxFifo);
+}
+
+/**
+ Enable or disable I2C Bus
+ */
+VOID
+I2CEnable (
+  UINT32 Bus,
+  UINT32 Enable
+  )
+{
+  UINT32 I2CStatusCnt = DW_STATUS_WAIT_RETRY;
+  UINTN  Base         = I2CBusList[Bus].Base;
+
+  I2CBusList[Bus].Enabled = Enable;
+
+  Write32 (Base + DW_IC_ENABLE, Enable);
+  do {
+    if ((Read32 (Base + DW_IC_ENABLE_STATUS) & 0x01) == Enable) {
+      break;
+    }
+    MicroSecondDelay (I2CBusList[Bus].PollingTime);
+  } while (I2CStatusCnt-- != 0);
+
+  if (I2CStatusCnt == 0) {
+    ERROR ("Enable/disable timeout\n");
+  }
+
+  if ((Enable == 0) || (I2CStatusCnt == 0)) {
+    /* Unset the target adddress */
+    Write32 (Base + DW_IC_TAR, 0);
+    I2CBusList[Bus].Enabled = 0;
+  }
+}
+
+/**
+ Setup Slave address
+ **/
+VOID
+I2CSetSlaveAddr (
+  UINT32 Bus,
+  UINT32 SlaveAddr
+  )
+{
+  UINTN  Base = I2CBusList[Bus].Base;
+  UINT32 OldEnableStatus = I2CBusList[Bus].Enabled;
+
+  I2CEnable (Bus, 0);
+  Write32 (Base + DW_IC_TAR, SlaveAddr);
+  if (OldEnableStatus != 0) {
+    I2CEnable (Bus, 1);
+  }
+}
+
+/**
+ Check for errors on I2C Bus
+ **/
+UINT32
+I2CCheckErrors (
+  UINT32 Bus
+  )
+{
+  UINTN  Base = I2CBusList[Bus].Base;
+  UINT32 ErrorStatus;
+
+  ErrorStatus = Read32 (Base + DW_IC_RAW_INTR_STAT) & DW_IC_ERR_CONDITION;
+  if (ErrorStatus != 0) {
+    ERROR ("Errors on i2c bus %d error status %08x\n", Bus, ErrorStatus);
+  }
+
+  if ((ErrorStatus & DW_IC_INTR_RX_UNDER) != 0) {
+    Read32 (Base + DW_IC_CLR_RX_UNDER);
+  }
+
+  if ((ErrorStatus & DW_IC_INTR_RX_OVER) != 0) {
+    Read32 (Base + DW_IC_CLR_RX_OVER);
+  }
+
+  if ((ErrorStatus & DW_IC_INTR_TX_ABRT) != 0) {
+    DBG ("TX_ABORT at source %08x\n", Read32 (Base + DW_IC_TX_ABRT_SOURCE));
+    Read32 (Base + DW_IC_CLR_TX_ABRT);
+  }
+
+  return ErrorStatus;
+}
+
+/**
+ Waiting for bus to not be busy
+ **/
+BOOLEAN
+I2CWaitBusNotBusy (
+  UINT32 Bus
+  )
+{
+  UINTN Base    = I2CBusList[Bus].Base;
+  UINTN Timeout = DW_BUS_WAIT_TIMEOUT_RETRY;
+
+  while ((Read32 (Base + DW_IC_STATUS) & DW_IC_STATUS_MST_ACTIVITY) != 0) {
+    if (Timeout == 0) {
+      DBG ("Timeout while waiting for bus ready\n");
+      return FALSE;
+    }
+    Timeout--;
+    /*
+     * A delay isn't absolutely necessary.
+     * But to ensure that we don't hammer the bus constantly,
+     * delay for DW_BUS_WAIT_SLEEP as with other implementation.
+     */
+    MicroSecondDelay (DW_BUS_WAIT_SLEEP);
+  }
+
+  return TRUE;
+}
+
+/**
+ Waiting for TX FIFO buffer available
+ **/
+EFI_STATUS
+I2CWaitTxData (
+  UINT32 Bus
+  )
+{
+  UINTN Base    = I2CBusList[Bus].Base;
+  UINTN Timeout = DW_TRANSFER_DATA_TIMEOUT;
+
+  while (Read32 (Base + DW_IC_TXFLR) == I2CBusList[Bus].TxFifo) {
+    if (Timeout <= 0) {
+      ERROR ("Timeout waiting for TX buffer available\n");
+      return EFI_TIMEOUT;
+    }
+    MicroSecondDelay (I2CBusList[Bus].PollingTime);
+    Timeout -= MicroSecondDelay (I2CBusList[Bus].PollingTime);
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+ Waiting for RX FIFO buffer available
+ **/
+EFI_STATUS
+I2CWaitRxData (
+  UINT32 Bus
+  )
+{
+  UINTN Base    = I2CBusList[Bus].Base;
+  UINTN Timeout = DW_TRANSFER_DATA_TIMEOUT;
+
+  while ((Read32 (Base + DW_IC_STATUS) & DW_IC_STATUS_RFNE) == 0) {
+      if (Timeout <= 0) {
+          ERROR ("Timeout waiting for RX buffer available\n");
+          return EFI_TIMEOUT;
+      }
+
+      if ((I2CCheckErrors (Bus) & DW_IC_INTR_TX_ABRT) != 0) {
+          return EFI_ABORTED;
+      }
+
+      MicroSecondDelay (I2CBusList[Bus].PollingTime);
+      Timeout -= MicroSecondDelay (I2CBusList[Bus].PollingTime);
+  }
+
+  return EFI_SUCCESS;
+}
+
+UINT32
+I2CSclHcnt (
+  UINT32 IcClk,
+  UINT32 tSYMBOL,
+  UINT32 Tf,
+  UINT32 Spklen,
+  INTN   Cond,
+  INTN   Offset
+  )
+{
+  /*
+   * DesignWare I2C core doesn't seem to have solid strategy to meet
+   * the tHD;STA timing spec. Configuring _HCNT. Based on tHIGH spec
+   * will result in violation of the tHD;STA spec.
+   */
+  if (Cond != 0) {
+    /*
+     * Conditional expression:
+     *
+     *   IC_[FS]S_SCL_HCNT + (1+4+3) >= IC_CLK * tHIGH
+     *
+     * This is.Based on the DW manuals, and represents an ideal
+     * configuration.  The resulting I2C bus speed will be
+     * faster than any of the others.
+     *
+     * If your hardware is free from tHD;STA issue, try this one.
+     */
+    return (((IcClk * tSYMBOL + 500000) / 1000000) - 8 + Offset);
+  }
+
+  /*
+   * Conditional expression:
+   *
+   * IC_[FS]S_SCL_HCNT + IC_[FH]S_SPKLEN + 6 >= IC_CLK *
+   *                                            (tHD;STA + tf)
+   *
+   * This is just experimental rule; the tHD;STA period turned
+   * out to be proportinal to (_HCNT _SPKLEN + 6).
+   * With this setting, we could meet both tHIGH and tHD;STA
+   * timing specs.
+   *
+   * If unsure, you'd better to take this alternative.
+   *
+   * The reason why we need to take into account "tf" here,
+   * is the same as described in I2c_Dw_Scl_Lcnt().
+   */
+  return (((IcClk * (tSYMBOL + Tf) + 500000) / 1000000) -
+            Spklen - 6 + Offset);
+}
+
+UINT32
+I2CSclLcnt (
+  UINT32 IcClk,
+  UINT32 tLOW,
+  UINT32 Tf,
+  INTN   Offset
+  )
+{
+  /*
+   * Conditional expression:
+   *
+   *   IC_[FS]S_SCL_LCNT + 1 >= IC_CLK * (tLOW + tf)
+   *
+   * DW I2C core starts Counting the SCL CNTs for the LOW period
+   * of the SCL clock (tLOW) as soon as it pulls the SCL line.
+   * In order to meet the tLOW timing spec, we need to take INTNo
+   * acCount the fall time of SCL signal (tf).  Default tf value
+   * should be 0.3 us, for safety.
+   */
+  return (((IcClk * (tLOW + Tf) + 500000) / 1000000) - 1 + Offset);
+}
+
+/**
+ Initialize the designware i2c scl Counts
+
+ This functions configures scl clock Count for SS, FS, and HS.
+ **/
+VOID
+I2CSclInit (
+  UINT32 Bus,
+  UINT32 I2CClkFreq,
+  UINT32 I2CSpeed
+  )
+{
+  UINT32    Hcnt, Lcnt;
+  UINT16    IcCon;
+  UINTN     Base            = I2CBusList[Bus].Base;
+  UINT32    InputClockKhz   = I2CClkFreq / 1000;
+  UINT32    SsClockKhz      = InputClockKhz;
+  UINT32    FsClockKhz      = InputClockKhz;
+  UINT32    HsClockKhz      = InputClockKhz;
+  UINT32    PsClockKhz      = InputClockKhz;
+  UINT32    I2CSpeedKhz     = I2CSpeed / 1000;
+
+  DBG("Bus %d I2CClkFreq %d I2CSpeed %d\n", 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;
+    SsClockKhz = (SsClockKhz * 100) / I2CSpeedKhz;
+    /* Standard speed mode */
+    Hcnt = I2CSclHcnt (SsClockKhz,
+      I2CSclMin[I2C_SS][I2C_SCL_HIGH],  /* tHD;STA = tHIGH = 4.0 us */
+      I2CSclMin[I2C_SS][I2C_SCL_TF],    /* tf = 0.3 us */
+      I2CSclParam[I2C_SS][I2C_SPKLEN],  /* spklen = 10 */
+      0,                                /* 0: DW default, 1: Ideal */
+      I2CSclParam[I2C_SS][I2C_OFFSET]); /* offset = 300 */
+    Write32 (Base + DW_IC_FS_SPKLEN, I2CSclParam[I2C_SS][I2C_SPKLEN]);
+    Lcnt = I2CSclLcnt (SsClockKhz,
+      I2CSclMin[I2C_SS][I2C_SCL_LOW],   /* tLOW = 4.7 us */
+      I2CSclMin[I2C_SS][I2C_SCL_TF],    /* tf = 0.3 us */
+      0);                               /* No Offset */
+    Write32 (Base + DW_IC_SS_SCL_HCNT, Hcnt);
+    Write32 (Base + DW_IC_SS_SCL_LCNT, Lcnt);
+  } else if (I2CSpeedKhz > 100 && I2CSpeedKhz <= 400) {
+    IcCon |= DW_IC_CON_SPEED_FAST;
+    FsClockKhz = (FsClockKhz * 400) / I2CSpeedKhz;
+    /* Fast speed mode */
+    Hcnt = I2CSclHcnt (FsClockKhz,
+      I2CSclMin[I2C_FS][I2C_SCL_HIGH],  /* tHD;STA = tHIGH = 0.6 us */
+      I2CSclMin[I2C_FS][I2C_SCL_TF],    /* tf = 0.3 us */
+      I2CSclParam[I2C_FS][I2C_SPKLEN],  /* spklen = 0xA */
+      0,                                /* 0: DW default, 1: Ideal */
+     I2CSclParam[I2C_FS][I2C_OFFSET]);  /* No Offset */
+    Write32 (Base + DW_IC_FS_SPKLEN, I2CSclParam[I2C_FS][I2C_SPKLEN]);
+    Lcnt = I2CSclLcnt (FsClockKhz,
+      I2CSclMin[I2C_FS][I2C_SCL_LOW],   /* tLOW = 1.3 us */
+      I2CSclMin[I2C_FS][I2C_SCL_TF],    /* tf = 0.3 us */
+      0);                               /* No Offset */
+    Write32 (Base + DW_IC_FS_SCL_HCNT, Hcnt);
+    Write32 (Base + DW_IC_FS_SCL_LCNT, Lcnt);
+  } else if (I2CSpeedKhz > 400 && I2CSpeedKhz <= 1000) {
+    IcCon |= DW_IC_CON_SPEED_FAST;
+    PsClockKhz = (PsClockKhz * 1000) / I2CSpeedKhz;
+    /* Fast speed plus mode */
+    Hcnt = I2CSclHcnt (PsClockKhz,
+      I2CSclMin[I2C_PS][I2C_SCL_HIGH],  /* tHD;STA = tHIGH = 0.26 us */
+      I2CSclMin[I2C_PS][I2C_SCL_TF],    /* tf = 0.12 us */
+      I2CSclParam[I2C_PS][I2C_SPKLEN],  /* spklen = 0xA */
+      0,                                /* 0: DW default, 1: Ideal */
+      I2CSclParam[I2C_PS][I2C_OFFSET]); /* No Offset */
+    Lcnt = I2CSclLcnt (PsClockKhz,
+      I2CSclMin[I2C_PS][I2C_SCL_LOW],   /* tLOW = 0.5 us */
+      I2CSclMin[I2C_PS][I2C_SCL_TF],    /* tf = 0.12 us */
+      0);  /* No Offset */
+    Write32 (Base + DW_IC_FS_SCL_HCNT, Hcnt);
+    Write32 (Base + DW_IC_FS_SCL_LCNT, Lcnt);
+    Write32 (Base + DW_IC_FS_SPKLEN, I2CSclParam[I2C_PS][I2C_SPKLEN]);
+  } else if (I2CSpeedKhz > 1000 && I2CSpeedKhz <= 3400) {
+    IcCon |= (DW_IC_CON_SPEED_STD | DW_IC_CON_SPEED_FAST);
+    HsClockKhz = (HsClockKhz * 3400) / I2CSpeedKhz;
+    /* High speed mode */
+    Hcnt = I2CSclHcnt (HsClockKhz,
+      I2CSclMin[I2C_HS][I2C_SCL_HIGH],  /* tHD;STA = tHIGH = 0.06 us for 100pf 0.16 for 400pf */
+      I2CSclMin[I2C_HS][I2C_SCL_TF],    /* tf = 0.3 us */
+      I2CSclParam[I2C_HS][I2C_SPKLEN],  /* No spklen */
+      0,                                /* 0: DW default, 1: Ideal */
+      I2CSclParam[I2C_HS][I2C_OFFSET]); /* No Offset */
+    Lcnt = I2CSclLcnt (HsClockKhz,
+      I2CSclMin[I2C_HS][I2C_SCL_LOW],   /* tLOW = 0.12 us for 100pf 0.32 us for 400pf */
+      I2CSclMin[I2C_HS][I2C_SCL_TF],    /* tf = 0.3 us */
+      0);  /* No Offset */
+    Write32 (Base + DW_IC_HS_SCL_HCNT, Hcnt);
+    Write32 (Base + DW_IC_HS_SCL_LCNT, Lcnt);
+  }
+  Write32 (Base + DW_IC_CON, IcCon);
+}
+
+/**
+ Initialize the designware i2c master hardware
+ **/
+EFI_STATUS
+I2CInit (
+  UINT32 Bus,
+  UINTN  BusSpeed
+  )
+{
+  UINTN Base;
+
+  ASSERT (I2CClock != 0);
+
+  I2CBusList[Bus].BusSpeed = BusSpeed;
+  I2CHWInit (Bus);
+
+  Base = I2CBusList[Bus].Base;
+
+  /* Disable the adapter and interrupt */
+  I2CEnable (Bus, 0);
+  Write32 (Base + DW_IC_INTR_MASK, 0);
+
+  /* Set standard and fast speed divider for high/low periods */
+  I2CSclInit (Bus, I2CClock, BusSpeed);
+  Write32 (Base + DW_IC_SDA_HOLD, 0x4b);
+
+  return EFI_SUCCESS;
+}
+
+/**
+ Wait the transaction finished
+ **/
+EFI_STATUS
+I2CFinish (
+  UINT32 Bus
+  )
+{
+  UINTN Base    = I2CBusList[Bus].Base;
+  UINTN Timeout = DW_TRANSFER_DATA_TIMEOUT;
+
+  /* Wait for TX FIFO empty */
+  do {
+    if ((Read32 (Base + DW_IC_STATUS) & DW_IC_STATUS_TFE) != 0) {
+      break;
+    }
+    MicroSecondDelay (I2CBusList[Bus].PollingTime);
+    Timeout -= MicroSecondDelay (I2CBusList[Bus].PollingTime);
+  } while (Timeout > 0);
+
+  if (Timeout == 0) {
+    ERROR ("Timeout waiting for TX FIFO empty\n");
+    return EFI_TIMEOUT;
+  }
+
+  /* Wait for STOP signal detected on the bus */
+  Timeout = DW_TRANSFER_DATA_TIMEOUT;
+  do {
+    if ((Read32 (Base + DW_IC_RAW_INTR_STAT) & DW_IC_INTR_STOP_DET) != 0) {
+      Read32 (Base + DW_IC_CLR_STOP_DET);
+      return EFI_SUCCESS;
+    }
+    MicroSecondDelay (I2CBusList[Bus].PollingTime);
+    Timeout -= MicroSecondDelay (I2CBusList[Bus].PollingTime);
+  } while (Timeout > 0);
+
+  ERROR ("Timeout waiting for transaction finished\n");
+  return EFI_TIMEOUT;
+}
+
+EFI_STATUS
+InternalI2CWrite (
+  UINT32 Bus,
+  UINT8  *Buf,
+  UINT32 *Length
+  )
+{
+  EFI_STATUS Status = EFI_SUCCESS;
+  UINTN      WriteCount;
+  UINTN      Base = I2CBusList[Bus].Base;
+
+  DBG ("Write Bus %d Buf %p Length %d\n", Bus, Buf, *Length);
+  I2CEnable (Bus, 1);
+
+  WriteCount = 0;
+  while ((*Length - WriteCount) != 0) {
+    Status = I2CWaitTxData (Bus);
+    if (EFI_ERROR (Status)) {
+      Write32 (Base + DW_IC_DATA_CMD, DW_IC_DATA_CMD_STOP);
+      I2CSync ();
+      goto Exit;
+    }
+
+    if (WriteCount == *Length - 1) {
+      Write32 (Base + DW_IC_DATA_CMD,
+                (Buf[WriteCount] & DW_IC_DATA_CMD_DAT_MASK)
+                | DW_IC_DATA_CMD_STOP);
+    } else {
+      Write32 (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,
+  IN UINT32   CmdLength,
+  UINT8       *Buf,
+  UINT32      *Length
+  )
+{
+  UINTN         Base    = I2CBusList[Bus].Base;
+  UINT32        CmdSend;
+  UINT32        TxLimit, RxLimit;
+  UINTN         Idx = 0;
+  UINTN         Count = 0;
+  UINTN         ReadCount = 0;
+  UINTN         WriteCount = 0;
+  EFI_STATUS    Status = EFI_SUCCESS;
+
+  DBG ("Read Bus %d Buf %p Length:%d\n", Bus, Buf, *Length);
+  I2CEnable (Bus, 1);
+
+  /* Write command data */
+  WriteCount = 0;
+  while (CmdLength != 0) {
+    TxLimit = I2CBusList[Bus].TxFifo - Read32 (Base + DW_IC_TXFLR);
+    Count = CmdLength > TxLimit ? TxLimit : CmdLength;
+
+    for (Idx = 0; Idx < Count ; Idx++ ) {
+      CmdSend = BufCmd[WriteCount++] & DW_IC_DATA_CMD_DAT_MASK;
+      Write32 (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)) {
+      Write32 (Base + DW_IC_DATA_CMD, DW_IC_DATA_CMD_STOP);
+      I2CSync ();
+      goto Exit;
+    }
+  }
+
+  ReadCount = 0;
+  WriteCount = 0;
+  while ((*Length - ReadCount) != 0) {
+    TxLimit = I2CBusList[Bus].TxFifo - Read32 (Base + DW_IC_TXFLR);
+    RxLimit = I2CBusList[Bus].RxFifo - Read32 (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;
+      }
+      Write32 (Base + DW_IC_DATA_CMD, CmdSend);
+      I2CSync ();
+      WriteCount++;
+
+      if (I2CCheckErrors (Bus) != 0) {
+        DBG ("Sending reading command remaining length %d CRC error\n", *Length);
+        Status = EFI_CRC_ERROR;
+        goto Exit;
+      }
+    }
+
+    for (Idx = 0; Idx < Count ; Idx++ ) {
+      Status = I2CWaitRxData (Bus);
+      if (EFI_ERROR (Status)) {
+        DBG ("Reading remaining length %d failed to wait data\n", *Length);
+
+        if (Status != EFI_ABORTED) {
+          Write32 (Base + DW_IC_DATA_CMD, DW_IC_DATA_CMD_STOP);
+          I2CSync ();
+        }
+
+        goto Exit;
+      }
+
+      Buf[ReadCount++] = Read32 (Base + DW_IC_DATA_CMD) & DW_IC_DATA_CMD_DAT_MASK;
+      I2CSync ();
+
+      if (I2CCheckErrors (Bus) != 0) {
+       DBG ("Reading remaining length %d CRC error\n", *Length);
+       Status = EFI_CRC_ERROR;
+       goto Exit;
+      }
+    }
+  }
+
+Exit:
+  *Length = ReadCount;
+  I2CFinish (Bus);
+  I2CWaitBusNotBusy (Bus);
+  I2CEnable (Bus, 0);
+
+  return Status;
+}
+
+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) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  I2CSetSlaveAddr (Bus, SlaveAddr);
+
+  return InternalI2CWrite (Bus, Buf, WriteLength);
+}
+
+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) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  I2CSetSlaveAddr (Bus, SlaveAddr);
+
+  return InternalI2CRead (Bus, BufCmd, CmdLength, Buf, ReadLength);
+}
+
+EFI_STATUS
+EFIAPI
+I2CProbe (
+  IN UINT32 Bus,
+  IN UINTN BusSpeed
+  )
+{
+  if (Bus >= MAX_PLATFORM_I2C_BUS_NUM) {
+    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**) &I2CBusList);
+  EfiConvertPointer (0x0, (VOID**) &I2CBaseArray);
+  EfiConvertPointer (0x0, (VOID**) &I2CClock);
+  for (Count = 0; Count < MAX_PLATFORM_I2C_BUS_NUM; Count++) {
+    if (!I2CRuntimeEnableArray[Count]) {
+      continue;
+    }
+    EfiConvertPointer (0x0, (VOID**) &I2CBaseArray[Count]);
+    EfiConvertPointer (0x0, (VOID**) &I2CBusList[Count].Base);
+  }
+}
+
+/**
+ Setup a bus that to be used in runtime service.
+
+ @Bus:      Bus ID.
+ @return:   0 for success.
+            Otherwise, error code.
+ **/
+EFI_STATUS
+EFIAPI
+I2CSetupRuntime (
+  IN UINT32 Bus
+  )
+{
+  EFI_STATUS                        Status;
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR   Descriptor;
+
+  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 (
+                  I2CBaseArray[Bus] & RUNTIME_ADDRESS_MASK,
+                  &Descriptor
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gDS->SetMemorySpaceAttributes (
+                  I2CBaseArray[Bus] & RUNTIME_ADDRESS_MASK,
+                  RUNTIME_ADDRESS_LENGTH,
+                  Descriptor.Attributes | EFI_MEMORY_RUNTIME
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  I2CRuntimeEnableArray[Bus] = TRUE;
+
+  return Status;
+}
+
+EFI_STATUS
+EFIAPI
+I2CLibConstructor (
+  VOID
+  )
+{
+  VOID                *Hob;
+  PlatformInfoHob     *PlatformHob;
+  PlatformInfoHob_V2  *PlatformHob_V2;
+  CONST EFI_GUID      PlatformHobGuid = PLATFORM_INFO_HOB_GUID;
+  CONST EFI_GUID      PlatformHobGuid_V2 = PLATFORM_INFO_HOB_GUID_V2;
+
+  /* Get I2C Clock from the Platform HOB */
+  Hob = GetFirstGuidHob (&PlatformHobGuid);
+  if (Hob == NULL) {
+    Hob = GetFirstGuidHob (&PlatformHobGuid_V2);
+    if (Hob == NULL) {
+      return EFI_NOT_FOUND;
+    }
+    PlatformHob_V2 = (PlatformInfoHob_V2 *) GET_GUID_HOB_DATA (Hob);
+    I2CClock = PlatformHob_V2->AhbClk;
+  } else {
+    PlatformHob = (PlatformInfoHob *) GET_GUID_HOB_DATA (Hob);
+    I2CClock = PlatformHob->ApbClk;
+  }
+  ASSERT (I2CClock != 0);
+
+  return EFI_SUCCESS;
+}
diff --git a/Silicon/Ampere/AmpereAltraPkg/Library/DwapbGpioLib/DwapbGpioLib.c b/Silicon/Ampere/AmpereAltraPkg/Library/DwapbGpioLib/DwapbGpioLib.c
new file mode 100755
index 000000000000..ec316a70dce8
--- /dev/null
+++ b/Silicon/Ampere/AmpereAltraPkg/Library/DwapbGpioLib/DwapbGpioLib.c
@@ -0,0 +1,313 @@
+/** @file
+
+  Copyright (c) 2020, Ampere Computing LLC. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+#include <Base.h>
+#include <Uefi/UefiBaseType.h>
+#include <Library/UefiRuntimeLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/IoLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/DebugLib.h>
+#include <Library/TimerLib.h>
+#include <Library/DwapbGpioLib.h>
+#include <Platform/Ac01.h>
+
+/* Runtime needs to be 64K alignment */
+#define RUNTIME_ADDRESS_MASK           (~(SIZE_64KB - 1))
+#define RUNTIME_ADDRESS_LENGTH         SIZE_64KB
+
+#define GPIO_MUX_VAL(Gpio)              (0x00000001 << (Gpio))
+#define GPIO_IN                         0
+#define GPIO_OUT                        1
+
+/* Address GPIO_REG Registers */
+#define GPIO_SWPORTA_DR_ADDR            0x00000000
+#define GPIO_SWPORTA_DDR_ADDR           0x00000004
+#define GPIO_EXT_PORTA_ADDR             0x00000050
+
+STATIC UINT64       GpioBaseAddr[] = { GPIO_DWAPB_BASE_ADDR } ;
+STATIC UINT64       GpiBaseAddr[] = { GPI_DWAPB_BASE_ADDR } ;
+STATIC BOOLEAN      GpioRuntimeEnableArray[sizeof (GpioBaseAddr) / sizeof (GpioBaseAddr[0])] = { FALSE };
+STATIC EFI_EVENT    mVirtualAddressChangeEvent = NULL;
+
+UINT64
+GetBaseAddr (
+  IN UINT32 Pin
+  )
+{
+  UINT32 NumberOfControllers = sizeof (GpioBaseAddr) / sizeof (GpioBaseAddr[0]);
+  UINT32 TotalPins = GPIO_DWAPB_PINS_PER_CONTROLLER * NumberOfControllers;
+
+  if (NumberOfControllers == 0 || Pin >= TotalPins) {
+    return 0;
+  }
+
+  return GpioBaseAddr[Pin / GPIO_DWAPB_PINS_PER_CONTROLLER];
+}
+
+VOID
+DwapbGpioWrite (
+  IN UINT64 Base,
+  IN UINT32 Val
+  )
+{
+  MmioWrite32 ((UINTN) Base, Val);
+}
+
+VOID
+DwapbGpioRead (
+  IN UINT64 Base,
+  OUT UINT32 *Val
+  )
+{
+  ASSERT (Val != NULL);
+  *Val = MmioRead32 (Base);
+}
+
+VOID
+EFIAPI
+DwapbGpioWriteBit (
+  IN UINT32 Pin,
+  IN UINT32 Val
+  )
+{
+  UINT64 Reg;
+  UINT32 GpioPin;
+  UINT32 ReadVal;
+
+  Reg = GetBaseAddr (Pin);
+  if (Reg == 0) {
+    return;
+  }
+
+  GpioPin = Pin % GPIO_DWAPB_PINS_PER_CONTROLLER;
+
+  Reg += GPIO_SWPORTA_DR_ADDR;
+  DwapbGpioRead (Reg, &ReadVal);
+
+  if (Val != 0) {
+    DwapbGpioWrite (Reg, ReadVal | GPIO_MUX_VAL (GpioPin));
+  } else {
+    DwapbGpioWrite (Reg, ReadVal & ~GPIO_MUX_VAL (GpioPin));
+  }
+}
+
+UINTN
+EFIAPI
+DwapbGpioReadBit (
+  IN UINT32 Pin
+  )
+{
+  UINT64 Reg;
+  UINT32 Val;
+  UINT32 GpioPin;
+  UINT8  Index;
+  UINT32 MaxIndex;
+
+  Reg = GetBaseAddr (Pin);
+  if (Reg == 0) {
+    return 0;
+  }
+
+  GpioPin = Pin % GPIO_DWAPB_PINS_PER_CONTROLLER;
+
+  /* Check if a base address is GPI */
+  MaxIndex = sizeof (GpiBaseAddr) / sizeof (GpiBaseAddr[0]);
+  for (Index = 0; Index < MaxIndex; Index++) {
+    if (Reg == GpiBaseAddr[Index]) {
+      break;
+    }
+  }
+  if (Index == MaxIndex) {
+    /* Only GPIO has GPIO_EXT_PORTA register, not for GPI */
+    Reg +=  GPIO_EXT_PORTA_ADDR;
+  }
+
+  DwapbGpioRead (Reg, &Val);
+
+  return Val & GPIO_MUX_VAL (GpioPin) ? 1 : 0;
+}
+
+EFI_STATUS
+DwapbGpioConfig (
+  IN UINT32 Pin,
+  IN UINT32 InOut
+  )
+{
+  INTN    GpioPin;
+  UINT32  Val;
+  UINT64  Reg;
+
+  /*
+   * Caculate GPIO Pin Number for Direction Register
+   * GPIO_SWPORTA_DDR for GPIO[31...0]
+   * GPIO_SWPORTB_DDR for GPIO[51...32]
+   */
+
+  Reg = GetBaseAddr (Pin);
+  if (Reg == 0) {
+    return EFI_UNSUPPORTED;
+  }
+
+  Reg += GPIO_SWPORTA_DDR_ADDR;
+  GpioPin = Pin % GPIO_DWAPB_PINS_PER_CONTROLLER;
+  DwapbGpioRead (Reg, &Val);
+
+  if (InOut == GPIO_OUT) {
+    Val |= GPIO_MUX_VAL (GpioPin);
+  } else {
+    Val &= ~GPIO_MUX_VAL (GpioPin);
+  }
+  DwapbGpioWrite (Reg, Val);
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+DwapbGPIOModeConfig (
+  UINT8 Pin,
+  UINTN Mode
+  )
+{
+  UINT32 NumberOfControllers = sizeof (GpioBaseAddr) / sizeof (UINT64);
+  UINT32 NumersOfPins = NumberOfControllers * GPIO_DWAPB_PINS_PER_CONTROLLER;
+  UINT32 Delay = 10;
+
+  if (Mode < GPIO_CONFIG_OUT_LOW
+      || Mode >= MAX_GPIO_CONFIG_MODE
+      || Pin > NumersOfPins - 1
+      || Pin < 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  switch (Mode) {
+  case GPIO_CONFIG_OUT_LOW:
+    DwapbGpioConfig (Pin, GPIO_OUT);
+    DwapbGpioWriteBit (Pin, 0);
+    DEBUG ((DEBUG_INFO, "GPIO pin %d configured as output low\n", Pin));
+    break;
+
+  case GPIO_CONFIG_OUT_HI:
+    DwapbGpioConfig (Pin, GPIO_OUT);
+    DwapbGpioWriteBit (Pin, 1);
+    DEBUG ((DEBUG_INFO, "GPIO pin %d configured as output high\n", Pin));
+    break;
+
+  case GPIO_CONFIG_OUT_LOW_TO_HIGH:
+    DwapbGpioConfig (Pin, GPIO_OUT);
+    DwapbGpioWriteBit (Pin, 0);
+    MicroSecondDelay (1000 * Delay);
+    DwapbGpioWriteBit (Pin, 1);
+    DEBUG ((DEBUG_INFO, "GPIO pin %d configured as output low->high\n", Pin));
+    break;
+
+  case GPIO_CONFIG_OUT_HIGH_TO_LOW:
+    DwapbGpioConfig (Pin, GPIO_OUT);
+    DwapbGpioWriteBit (Pin, 1);
+    MicroSecondDelay (1000 * Delay);
+    DwapbGpioWriteBit (Pin, 0);
+    DEBUG ((DEBUG_INFO, "GPIO pin %d configured as output high->low\n", Pin));
+    break;
+
+  case GPIO_CONFIG_IN:
+    DwapbGpioConfig (Pin, GPIO_IN);
+    DEBUG ((DEBUG_INFO, "GPIO pin %d configured as input\n", Pin));
+    break;
+
+  default:
+    break;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+ * 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
+GpioVirtualAddressChangeEvent (
+  IN EFI_EVENT                            Event,
+  IN VOID                                 *Context
+  )
+{
+  UINTN Count;
+
+  EfiConvertPointer (0x0, (VOID**) &GpioBaseAddr);
+  for (Count = 0; Count < sizeof (GpioBaseAddr) / sizeof (GpioBaseAddr[0]); Count++) {
+    if (!GpioRuntimeEnableArray[Count]) {
+      continue;
+    }
+    EfiConvertPointer (0x0, (VOID**) &GpioBaseAddr[Count]);
+  }
+}
+
+/**
+ Setup a controller that to be used in runtime service.
+
+ @Bus:      Bus ID.
+ @return:   0 for success.
+            Otherwise, error code.
+ **/
+EFI_STATUS
+EFIAPI
+DwapbGPIOSetupRuntime (
+  IN UINT32 Pin
+  )
+{
+  EFI_STATUS                        Status;
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR   Descriptor;
+
+  if (GetBaseAddr (Pin) == 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (mVirtualAddressChangeEvent == NULL) {
+    /*
+    * Register for the virtual address change event
+    */
+    Status = gBS->CreateEventEx (
+                    EVT_NOTIFY_SIGNAL,
+                    TPL_NOTIFY,
+                    GpioVirtualAddressChangeEvent,
+                    NULL,
+                    &gEfiEventVirtualAddressChangeGuid,
+                    &mVirtualAddressChangeEvent
+                    );
+    ASSERT_EFI_ERROR (Status);
+  }
+
+  Status = gDS->GetMemorySpaceDescriptor (
+                  GetBaseAddr (Pin) & RUNTIME_ADDRESS_MASK,
+                  &Descriptor
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gDS->SetMemorySpaceAttributes (
+                  GetBaseAddr (Pin) & RUNTIME_ADDRESS_MASK,
+                  RUNTIME_ADDRESS_LENGTH,
+                  Descriptor.Attributes | EFI_MEMORY_RUNTIME
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  GpioRuntimeEnableArray[Pin / GPIO_DWAPB_PINS_PER_CONTROLLER] = TRUE;
+
+  return Status;
+}
-- 
2.17.1


  parent reply	other threads:[~2020-12-09  9:24 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-12-09  9:24 [edk2-platforms][PATCH 00/34] Add new Ampere Mt. Jade platform Nhi Pham
2020-12-09  9:24 ` [edk2-platforms][PATCH 01/34] Initial support for Ampere Altra and " Nhi Pham
2021-01-07 23:57   ` [edk2-devel] " Leif Lindholm
2021-01-15  4:59     ` Vu Nguyen
2020-12-09  9:24 ` [edk2-platforms][PATCH 02/34] Platform/Ampere: Implement FailSafe library Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 03/34] Platform/Ampere: Add FailSafe and WDT support Nhi Pham
2020-12-09  9:25 ` Nhi Pham [this message]
2020-12-09  9:25 ` [edk2-platforms][PATCH 05/34] JadePkg: Implement RealTimeClockLib for PCF85063 Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 06/34] Platform/Ampere: Add AcpiPccLib to support ACPI PCCT Table Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 07/34] Platform/Ampere: Add AcpiHelperLib to update ACPI DSDT table Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 08/34] JadePkg: Initial support for static ACPI tables Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 09/34] JadePkg: Install some ACPI tables at runtime Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 10/34] Silicon/Ampere: Support Non Volatile storage for Variable service Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 11/34] Silicon/Ampere: Support PlatformManagerUiLib Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 12/34] AmpereAltraPkg: Add PcieCore Library Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 13/34] AmpereAltraPkg: Add PciHostBridge driver Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 14/34] JadePkg: Add implementation for PcieBoardLib Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 15/34] JadePkg: Enable PCIe support Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 16/34] JadePkg: Add ASpeed GOP driver Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 17/34] Silicon/Ampere: Add Random Number Generator Support Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 18/34] Silicon/Ampere: Fixup runtime memory attribute Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 19/34] JadePkg: Add SMBIOS tables support Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 20/34] AmpereAltraPkg: Add DebugInfoPei module Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 21/34] Silicon/Ampere: Add platform info screen Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 22/34] Silicon/Ampere: Add Memory " Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 23/34] AmpereAltraPkg: Add CPU Configuration for SubNUMA Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 24/34] AmpereAltraPkg: Add ACPI configuration screen Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 25/34] AmpereAltraPkg: Implement PlatformFlashAccessLib instance Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 26/34] JadePkg: Add implementation for UEFI Capsule Update Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 27/34] JadePkg: Add Capsule Update support Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 28/34] Silicon/Ampere: Implement PlatformBootManagerLib for LinuxBoot Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 29/34] Platform/Ampere: Add LinuxBoot image Nhi Pham
2020-12-10 12:40   ` [edk2-devel] " Leif Lindholm
2020-12-11  2:38     ` Nhi Pham
2020-12-11 11:15       ` Leif Lindholm
2020-12-09  9:25 ` [edk2-platforms][PATCH 30/34] JadePkg: Support LinuxBoot DSC/FDF build for Jade platform Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 31/34] AmpereAltraPkg: Add BootProgress support Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 32/34] JadePkg: Add ACPI/APEI tables Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 33/34] Platform/Ampere: Add AcpiApeiLib Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 34/34] AmpereAltraPkg, JadePkg: Add RAS setting screen Nhi Pham

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20201209092531.30867-5-nhi@os.amperecomputing.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox