From: Vu Nguyen <vunguyen@os.amperecomputing.com>
The DwI2cLib library provides basic functions to control the I2C
controller on Ampere Altra processor.
Cc: Thang Nguyen <thang@os.amperecomputing.com>
Cc: Chuong Tran <chuong@os.amperecomputing.com>
Cc: Phong Vo <phong@os.amperecomputing.com>
Cc: Leif Lindholm <leif@nuviainc.com>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Nate DeSimone <nathaniel.l.desimone@intel.com>
Signed-off-by: Vu Nguyen <vunguyen@os.amperecomputing.com>
---
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 (#75613): https://edk2.groups.io/g/devel/message/75613
Mute This Topic: https://groups.io/mt/83097099/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-
On Wed, May 26, 2021 at 17:06:57 +0700, Nhi Pham wrote:
> From: Vu Nguyen <vunguyen@os.amperecomputing.com>
>
> The DwI2cLib library provides basic functions to control the I2C
> controller on Ampere Altra processor.
>
> Cc: Thang Nguyen <thang@os.amperecomputing.com>
> Cc: Chuong Tran <chuong@os.amperecomputing.com>
> Cc: Phong Vo <phong@os.amperecomputing.com>
> Cc: Leif Lindholm <leif@nuviainc.com>
> Cc: Michael D Kinney <michael.d.kinney@intel.com>
> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
> Cc: Nate DeSimone <nathaniel.l.desimone@intel.com>
>
> Signed-off-by: Vu Nguyen <vunguyen@os.amperecomputing.com>
Reviewed-by: Leif Lindholm <leif@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/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-
On 6/5/21 06:21, Leif Lindholm wrote:
> On Wed, May 26, 2021 at 17:06:57 +0700, Nhi Pham wrote:
>> From: Vu Nguyen <vunguyen@os.amperecomputing.com>
>>
>> The DwI2cLib library provides basic functions to control the I2C
>> controller on Ampere Altra processor.
>>
>> Cc: Thang Nguyen <thang@os.amperecomputing.com>
>> Cc: Chuong Tran <chuong@os.amperecomputing.com>
>> Cc: Phong Vo <phong@os.amperecomputing.com>
>> Cc: Leif Lindholm <leif@nuviainc.com>
>> Cc: Michael D Kinney <michael.d.kinney@intel.com>
>> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
>> Cc: Nate DeSimone <nathaniel.l.desimone@intel.com>
>>
>> Signed-off-by: Vu Nguyen <vunguyen@os.amperecomputing.com>
> Reviewed-by: Leif Lindholm <leif@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)
Thanks, Leif. I will fix the clang compilation issues in the v3.
Best regards,
Nhi
>
> /
> 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 (#76543): https://edk2.groups.io/g/devel/message/76543
Mute This Topic: https://groups.io/mt/83097099/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-
© 2016 - 2026 Red Hat, Inc.