From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wm1-f53.google.com (mail-wm1-f53.google.com [209.85.128.53]) by mx.groups.io with SMTP id smtpd.web10.6495.1622848904965806085 for ; Fri, 04 Jun 2021 16:21:45 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@nuviainc-com.20150623.gappssmtp.com header.s=20150623 header.b=ucN3e6IS; spf=pass (domain: nuviainc.com, ip: 209.85.128.53, mailfrom: leif@nuviainc.com) Received: by mail-wm1-f53.google.com with SMTP id p13-20020a05600c358db029019f44afc845so6406673wmq.3 for ; Fri, 04 Jun 2021 16:21:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nuviainc-com.20150623.gappssmtp.com; s=20150623; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to; bh=YtzJi6ov8YUag0OYIYkA2uGTmEspYPdI9v2AzF/iYX0=; b=ucN3e6ISbd9HpAnHcu+RViQl8YPQ3UdGS3irnuZ2vuzPerrwrR3njsJfgXDT05qVvY b9ojTGulnpwf7QwlHBV7tLNxDIIWrNKN8HdsbStQnltUFhtKPienlWBt/V/frF0XHmUU BGrfXJDYstU3vjGzJYrawcISaZpgf5Qfw9e/WrIFkx9h8c5aQRhSyw4aAWt8O0uEW2EV J5KrvV6bQe4h7xAVRRmPqkdiYMSndjZGWJlrVMaVC+7C6xjglESf0pq+pWdQLZt4lTHC tCF2stpOqdqcWXERnt991AhgGiv3SbANxVqsJVi1A0CIVw0uZNo3WOy3xYqh3x5Klm/S IPvg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to; bh=YtzJi6ov8YUag0OYIYkA2uGTmEspYPdI9v2AzF/iYX0=; b=AVJsSmF4IDJmi5oy2lu8cMGOyGdQ0hmzp7weWMh5F9f+967Ci88USWmuuG7mPHEMg4 m4C9/8f5wwi3P7fuYcNMCG5O9a193k6z7ppY+Uh34tU8uRIg9rE8VZmFwg7IXjvXjRXa gq5d5AZYAcyRpRSKZJjFHCkdXp7bx64SVuvA0P8YxA06M1hj0cjtqQPTVw+Jf3+bgg9Z TkUEtfIjNPpmPXWO+5/gbs8VtpekMOYHHkmraVZdaQF6OICdSeMeukCSQILtSY3YTcHo E71u1fGPsaQ0Dci6CMosjC1N3oeIgV10oZ6wB4P0GK2JVLq+mFYZmtN4JJSQ4CtaugDY izFw== X-Gm-Message-State: AOAM531EBbth5vIX5Ve6iFi0drRh7oz9VWguOolI++vrZ+DAvettse1y zc7rr9lei2VlA4mbyKwKO0+k/A== X-Google-Smtp-Source: ABdhPJyGCu6jXBYitzj18iKn6SiDxXf0l0dFyVBsdBpXCCGX0pGNY3iLlRgDzrSJejmJ86wSGjqVxg== X-Received: by 2002:a1c:a54b:: with SMTP id o72mr375406wme.124.1622848903399; Fri, 04 Jun 2021 16:21:43 -0700 (PDT) Return-Path: Received: from leviathan (cpc1-cmbg19-2-0-cust915.5-4.cable.virginm.net. [82.27.183.148]) by smtp.gmail.com with ESMTPSA id m132sm6983458wmf.10.2021.06.04.16.21.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 04 Jun 2021 16:21:43 -0700 (PDT) Date: Sat, 5 Jun 2021 00:21:41 +0100 From: "Leif Lindholm" To: Nhi Pham Cc: devel@edk2.groups.io, Vu Nguyen , Thang Nguyen , Chuong Tran , Phong Vo , Michael D Kinney , Ard Biesheuvel , Nate DeSimone Subject: Re: [edk2-platforms][PATCH v2 05/32] AmpereAltraPkg: Add DwI2cLib library Message-ID: <20210604232141.asjuirz52gxvis7a@leviathan> References: <20210526100724.5359-1-nhi@os.amperecomputing.com> <20210526100724.5359-7-nhi@os.amperecomputing.com> MIME-Version: 1.0 In-Reply-To: <20210526100724.5359-7-nhi@os.amperecomputing.com> Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Wed, May 26, 2021 at 17:06:57 +0700, Nhi Pham wrote: > From: Vu Nguyen > > The DwI2cLib library provides basic functions to control the I2C > controller on Ampere Altra processor. > > Cc: Thang Nguyen > Cc: Chuong Tran > Cc: Phong Vo > Cc: Leif Lindholm > Cc: Michael D Kinney > Cc: Ard Biesheuvel > Cc: Nate DeSimone > > Signed-off-by: Vu Nguyen Reviewed-by: Leif Lindholm (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.
> +# > +# 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.
> + > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#ifndef I2C_LIB_H_ > +#define I2C_LIB_H_ > + > +#include > + > +/** > + 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.
> + > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#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 >