From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wr1-f65.google.com (mail-wr1-f65.google.com [209.85.221.65]) by mx.groups.io with SMTP id smtpd.web12.9971.1588691493643495681 for ; Tue, 05 May 2020 08:11:34 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@nuviainc-com.20150623.gappssmtp.com header.s=20150623 header.b=z60BAY/w; spf=pass (domain: nuviainc.com, ip: 209.85.221.65, mailfrom: leif@nuviainc.com) Received: by mail-wr1-f65.google.com with SMTP id z8so2261402wrw.3 for ; Tue, 05 May 2020 08:11:33 -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:user-agent; bh=F2JCN0vvEhNelqLVOn704LGyzERmnap/EI8jH2bZIaE=; b=z60BAY/wYKJIuAAGMu/9/JQLdZr561FmAEm8Y+g3NBgZWevdINGQY4hSYxS0nck3tj Vt4KMVW+hibdUpzrm5DQIvDI/kLivbVejUpgqU95BJv5BbZZrkSkilbLGqIOXaIwhKl0 7wHWNj4v1bNc4eYMFMlh+X6GycWGixlzvi4K1PA8i/uyvqDl0SL+4yQwCMNfJVXCs4za 2xDCDVlY8m87jRDhv/v3qCgSL74rmc/hki8WqeEML4F+idjLV1bIXR/0TgtLicV9Zv7J cBrMkrDlk5RbQTEYTVOQ1Iyc6wfCB240ADLGf7ppzXnMpdn09SwU38LAeA2/+0+fhnWO SK5A== 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:user-agent; bh=F2JCN0vvEhNelqLVOn704LGyzERmnap/EI8jH2bZIaE=; b=evHCsk08eKkzIW4p5cRy5wdtK9Opp33ekkheuGm4QkGiR8y04GgFHYZQgu9HYF9mP4 KjTYhfNttIzHKev4Y7jzMrTQN5uTIKzijbjGDcwQbCZbhisW4o+myxlJ+zxQ+2UNJ0bH F0h09e7A/YG9kWOsdgTflp2p2Rsm5VOmZ5O+OKhiqBmc0mD7TGQo2Hac8vC3XAfvziAu k936EFgjAHvwbbcxjE2xeGoqt5LL76gIWBEvUY6NULTrPiFMTMvtM7y8Pu7K9LsuHFmN 325IQKtz6Rqcv28PIayNqn6PEnZ6Xa+uvgQANpetPFpg/jLsVPeLtP3MPAeOpTImShRy ShWQ== X-Gm-Message-State: AGi0PuZbcFJMXk4pxebXzzK2eK1TIUYiLTBzvu2nCsk/2Z8gHGoipIQw 3kE5gsVdHrvU0a1iIytABfIAgw== X-Google-Smtp-Source: APiQypLibBlTmxldtCgzncLxlKtf6UiF4t/P/Bi6FMaA0NfA2EWjEmrEfutBTf6lGreNu+4Ew75XjQ== X-Received: by 2002:a5d:42c8:: with SMTP id t8mr4203829wrr.70.1588691492011; Tue, 05 May 2020 08:11:32 -0700 (PDT) Return-Path: Received: from vanye ([2001:470:1f09:12f0:b26e:bfff:fea9:f1b8]) by smtp.gmail.com with ESMTPSA id h2sm4410994wmf.34.2020.05.05.08.11.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 05 May 2020 08:11:31 -0700 (PDT) Date: Tue, 5 May 2020 16:11:28 +0100 From: "Leif Lindholm" To: Pankaj Bansal Cc: Meenakshi Aggarwal , Michael D Kinney , devel@edk2.groups.io, Varun Sethi , Samer El-Haj-Mahmoud , Jon Nettleton , Ard Biesheuvel Subject: Re: [PATCH edk2-platforms v4 01/24] Silicon/NXP: Add I2c lib Message-ID: <20200505151128.GH21486@vanye> References: <20200501054955.13025-1-pankaj.bansal@oss.nxp.com> <20200501054955.13025-2-pankaj.bansal@oss.nxp.com> MIME-Version: 1.0 In-Reply-To: <20200501054955.13025-2-pankaj.bansal@oss.nxp.com> User-Agent: Mutt/1.10.1 (2018-07-13) Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Fri, May 01, 2020 at 11:19:32 +0530, Pankaj Bansal wrote: > From: Pankaj Bansal > > Add an I2c library to control the i2c controllers to communicate > with I2c device. > > We need this functionality in a lib because this is going to be used > in PrePeiCore sec module to get the System clock information from > devices connected to i2c (like fpga or clock generator) > > In subsequent patches I2c functionality in I2cDxe driver would be removed > and I2cLib APIs would be used in I2cDxe. > > The I2cLib differs from I2c Controller functionality in I2c Dxe in > several aspects: > - I2cLib doesn't assume that each I2c Transaction would be of minimum > two operations > - I2cLib generates repeat start between operations which complies with > PI I2c specs. > > Signed-off-by: Pankaj Bansal > --- > > Notes: > V4: > - Dividor -> divider > - Added line between license and copyright in files > - Fixed newline at the end of file errors > - removed I2cEarlyInitialize > - changed Ibfd from UINT16 to UINT8 in I2C_CLOCK_DIVISOR_PAIR in > I2cLibInternal.h I don't recall this being mentioned as part of the review, and I did not see it called out in the cover letter. I am not going to ask for another revision now, and this may be tiny enough that it doesn't make sense to break out as a separate patch - but that may be something to consider in the future for bugs you find yourself during code rework. Reviewed-by: Leif Lindholm > - Modify commit description > > V3: > - moved I2cLib addition in NxpQoriqLs.dsc.inc to PATCH 2 > - Added Qoriq Layerscape in description of CLOCK_DIVISOR_PAIR > - ERRXXXXXX -> A-XXXXXX > - Added u-boot patch reference to errata workaround description > - Added Glossary and Revision Reference in file header > - Dividor -> divider > - Added explaination before calling I2cErratumA009203 > - PtrAddress -> AddressPtr > - Update commit description > > Silicon/NXP/NxpQoriqLs.dec | 9 +- > Silicon/NXP/Library/I2cLib/I2cLib.inf | 30 ++ > Silicon/NXP/Include/Library/I2cLib.h | 100 ++++ > Silicon/NXP/Library/I2cLib/I2cLibInternal.h | 105 ++++ > Silicon/NXP/Library/I2cLib/I2cLib.c | 545 ++++++++++++++++++++ > 5 files changed, 788 insertions(+), 1 deletion(-) > > diff --git a/Silicon/NXP/NxpQoriqLs.dec b/Silicon/NXP/NxpQoriqLs.dec > index 764b9bb0e2d3..1aff519bfaf4 100644 > --- a/Silicon/NXP/NxpQoriqLs.dec > +++ b/Silicon/NXP/NxpQoriqLs.dec > @@ -1,6 +1,6 @@ > # @file. > # > -# Copyright 2017-2019 NXP > +# Copyright 2017-2020 NXP > # > # SPDX-License-Identifier: BSD-2-Clause-Patent > # > @@ -13,6 +13,10 @@ > [Includes] > Include > > +[LibraryClasses] > + ## @libraryclass Provides services to read/write to I2c devices > + I2cLib|Include/Library/I2cLib.h > + > [Guids.common] > gNxpQoriqLsTokenSpaceGuid = {0x98657342, 0x4aee, 0x4fc6, {0xbc, 0xb5, 0xff, 0x45, 0xb7, 0xa8, 0x71, 0xf2}} > gNxpNonDiscoverableI2cMasterGuid = { 0x5f2c099c, 0x54a3, 0x4dd4, {0x9e, 0xc5, 0xe9, 0x12, 0x8c, 0x36, 0x81, 0x6a}} > @@ -101,3 +105,6 @@ > gNxpQoriqLsTokenSpaceGuid.PcdPciLutBigEndian|FALSE|BOOLEAN|0x00000312 > gNxpQoriqLsTokenSpaceGuid.PcdWatchdogBigEndian|FALSE|BOOLEAN|0x00000313 > gNxpQoriqLsTokenSpaceGuid.PcdIfcBigEndian|FALSE|BOOLEAN|0x00000314 > + > +[PcdsFeatureFlag] > + gNxpQoriqLsTokenSpaceGuid.PcdI2cErratumA009203|FALSE|BOOLEAN|0x00000315 > diff --git a/Silicon/NXP/Library/I2cLib/I2cLib.inf b/Silicon/NXP/Library/I2cLib/I2cLib.inf > new file mode 100644 > index 000000000000..3da2331db37d > --- /dev/null > +++ b/Silicon/NXP/Library/I2cLib/I2cLib.inf > @@ -0,0 +1,30 @@ > +# @file > +# Component description file for I2cLib module > +# > +# Copyright 2017, 2020 NXP > +# > +# SPDX-License-Identifier: BSD-2-Clause-Patent > +# > +# > + > +[Defines] > + INF_VERSION = 0x0001001A > + BASE_NAME = I2cLib > + FILE_GUID = 8ecefc8f-a2c4-4091-b81f-20f7aeb0567f > + MODULE_TYPE = BASE > + VERSION_STRING = 1.0 > + LIBRARY_CLASS = I2cLib > + > +[Sources.common] > + I2cLib.c > + > +[LibraryClasses] > + IoLib > + TimerLib > + > +[Packages] > + MdePkg/MdePkg.dec > + Silicon/NXP/NxpQoriqLs.dec > + > +[FeaturePcd] > + gNxpQoriqLsTokenSpaceGuid.PcdI2cErratumA009203 > diff --git a/Silicon/NXP/Include/Library/I2cLib.h b/Silicon/NXP/Include/Library/I2cLib.h > new file mode 100644 > index 000000000000..f09324c00e47 > --- /dev/null > +++ b/Silicon/NXP/Include/Library/I2cLib.h > @@ -0,0 +1,100 @@ > +/** @file > + I2c Lib to control I2c controller. > + > + Copyright 2020 NXP > + > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#ifndef I2C_LIB_H__ > +#define I2C_LIB_H__ > + > +#include > +#include > + > +/** > + software reset of the entire I2C module. > + The module is reset and disabled. > + Status register fields (IBSR) are cleared. > + > + @param[in] Base Base Address of I2c controller's registers > + > + @return EFI_SUCCESS successfuly reset the I2c module > +**/ > +EFI_STATUS > +I2cReset ( > + IN UINTN Base > + ); > + > +/** > + Configure I2c bus to operate at a given speed > + > + @param[in] Base Base Address of I2c controller's registers > + @param[in] I2cBusClock Input clock to I2c controller > + @param[in] Speed speed to be configured for I2c bus > + > + @return EFI_SUCCESS successfuly setup the i2c bus > +**/ > +EFI_STATUS > +I2cInitialize ( > + IN UINTN Base, > + IN UINT64 I2cBusClock, > + IN UINT64 Speed > + ); > + > +/** > + Transfer data to/from I2c slave device > + > + @param[in] Base Base Address of I2c controller's registers > + @param[in] SlaveAddress Slave Address from which data is to be read > + @param[in] RequestPacket Pointer to an EFI_I2C_REQUEST_PACKET structure > + describing the I2C transaction > + > + @return EFI_SUCCESS successfuly transfer the data > + @return EFI_DEVICE_ERROR There was an error while transferring data through > + I2c bus > + @return EFI_NO_RESPONSE There was no Ack from i2c device > + @return EFI_TIMEOUT I2c Bus is busy > + @return EFI_NOT_READY I2c Bus Arbitration lost > +**/ > +EFI_STATUS > +I2cBusXfer ( > + IN UINTN Base, > + IN UINT32 SlaveAddress, > + IN EFI_I2C_REQUEST_PACKET *RequestPacket > + ); > + > +/** > + Read a register from I2c slave device. This API is wrapper around I2cBusXfer > + > + @param[in] Base Base Address of I2c controller's registers > + @param[in] SlaveAddress Slave Address from which register value is > + to be read > + @param[in] RegAddress Register Address in Slave's memory map > + @param[in] RegAddressWidthInBytes Number of bytes in RegAddress to send to > + I2c Slave for simple reads without any > + register, make this value = 0 > + (RegAddress is don't care in that case) > + @param[out] RegValue Value to be read from I2c slave's regiser > + @param[in] RegValueNumBytes Number of bytes to read from I2c slave > + register > + > + @return EFI_SUCCESS successfuly read the registers > + @return EFI_DEVICE_ERROR There was an error while transferring data through > + I2c bus > + @return EFI_NO_RESPONSE There was no Ack from i2c device > + @return EFI_TIMEOUT I2c Bus is busy > + @return EFI_NOT_READY I2c Bus Arbitration lost > +**/ > +EFI_STATUS > +I2cBusReadReg ( > + IN UINTN Base, > + IN UINT32 SlaveAddress, > + IN UINT64 RegAddress, > + IN UINT8 RegAddressWidthInBytes, > + OUT UINT8 *RegValue, > + IN UINT32 RegValueNumBytes > + ); > + > +#endif // I2C_LIB_H__ > diff --git a/Silicon/NXP/Library/I2cLib/I2cLibInternal.h b/Silicon/NXP/Library/I2cLib/I2cLibInternal.h > new file mode 100644 > index 000000000000..cd82c423947a > --- /dev/null > +++ b/Silicon/NXP/Library/I2cLib/I2cLibInternal.h > @@ -0,0 +1,105 @@ > +/** @file > + I2c Lib to control I2c controller. > + > + Copyright 2020 NXP > + > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#ifndef I2C_LIB_INTERNAL_H__ > +#define I2C_LIB_INTERNAL_H__ > + > +#include > +#include > + > +/** Module Disable > + 0b - The module is enabled. You must clear this field before any other IBCR > + fields have any effect. > + 1b - The module is reset and disabled. This is the power-on reset situation. > + When high, the interface is held in reset, but registers can still be > + accessed. Status register fields (IBSR) are not valid when the module > + is disabled. > +**/ > +#define I2C_IBCR_MDIS BIT7 > +// I2c Bus Interrupt Enable > +#define I2C_IBCR_IBIE BIT6 > +/** Master / Slave Mode 0b - Slave mode 1b - Master mode > + When you change this field from 0 to 1, the module generates a START signal > + on the bus and selects the master mode. When you change this field from 1 to > + 0, the module generates a STOP signal and changes the operation mode from > + master to slave. You should generate a STOP signal only if IBSR[IBIF]=1. > + The module clears this field without generating a STOP signal when the > + master loses arbitration. > +*/ > +#define I2C_IBCR_MSSL BIT5 > +// 0b - Receive 1b - Transmit > +#define I2C_IBCR_TXRX BIT4 > +/** Data acknowledge disable > + Values written to this field are only used when the I2C module is a receiver, > + not a transmitter. > + 0b - The module sends an acknowledge signal to the bus at the 9th clock bit > + after receiving one byte of data. > + 1b - The module does not send an acknowledge-signal response (that is, > + acknowledge bit = 1). > +**/ > +#define I2C_IBCR_NOACK BIT3 > +/**Repeat START > + If the I2C module is the current bus master, and you program RSTA=1, the I2C > + module generates a repeated START condition. This field always reads as a 0. > + If you attempt a repeated START at the wrong time, if the bus is owned by > + another master the result is loss of arbitration. > +**/ > +#define I2C_IBCR_RSTA BIT2 > +// DMA enable > +#define I2C_IBCR_DMAEN BIT1 > + > +// Transfer Complete > +#define I2C_IBSR_TCF BIT7 > +// I2C bus Busy. 0b - Bus is idle, 1b - Bus is busy > +#define I2C_IBSR_IBB BIT5 > +// Arbitration Lost. software must clear this field by writing a one to it. > +#define I2C_IBSR_IBAL BIT4 > +// I2C bus interrupt flag > +#define I2C_IBSR_IBIF BIT1 > +// Received acknowledge 0b - Acknowledge received 1b - No acknowledge received > +#define I2C_IBSR_RXAK BIT0 > + > +//Bus idle interrupt enable > +#define I2C_IBIC_BIIE BIT7 > + > +// Glitch filter enable > +#define I2C_IBDBG_GLFLT_EN BIT3 > + > +#define I2C_BUS_TEST_BUSY TRUE > +#define I2C_BUS_TEST_IDLE !I2C_BUS_TEST_BUSY > +#define I2C_BUS_TEST_RX_ACK TRUE > +#define I2C_BUS_NO_TEST_RX_ACK !I2C_BUS_TEST_RX_ACK > + > +#define ARRAY_LAST_ELEM(x) (x)[ARRAY_SIZE (x) - 1] > +#define I2C_NUM_RETRIES 500 > + > +typedef struct _I2C_REGS { > + UINT8 Ibad; // I2c Bus Address Register > + UINT8 Ibfd; // I2c Bus Frequency Divider Register > + UINT8 Ibcr; // I2c Bus Control Register > + UINT8 Ibsr; // I2c Bus Status Register > + UINT8 Ibdr; // I2C Bus Data I/O Register > + UINT8 Ibic; // I2C Bus Interrupt Config Register > + UINT8 Ibdbg; // I2C Bus Debug Register > +} I2C_REGS; > + > +/* > + * sorted list of clock divisor, Ibfd register value pairs > + */ > +typedef struct _I2C_CLOCK_DIVISOR_PAIR { > + UINT16 Divisor; > + UINT8 Ibfd; // I2c Bus Frequency Divider Register value > +} I2C_CLOCK_DIVISOR_PAIR; > + > +typedef struct { > + UINTN OperationCount; > + EFI_I2C_OPERATION Operation[2]; > +} I2C_REG_REQUEST; > + > +#endif // I2C_LIB_INTERNAL_H__ > diff --git a/Silicon/NXP/Library/I2cLib/I2cLib.c b/Silicon/NXP/Library/I2cLib/I2cLib.c > new file mode 100644 > index 000000000000..4b9055969112 > --- /dev/null > +++ b/Silicon/NXP/Library/I2cLib/I2cLib.c > @@ -0,0 +1,545 @@ > +/** @file > + I2c Lib to control I2c controller. > + > + Copyright 2017, 2020 NXP > + > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > + @par Revision Reference: > + - PI Version 1.7 > + > + @par Glossary: > + - Ibfd - I2c Bus Frequency Divider > +**/ > +#include > +#include > +#include > +#include > +#include > + > +#include "I2cLibInternal.h" > + > +/** > + I2C divisor and Ibfd register values when glitch filter is enabled > + > + In case of duplicate SCL Divisor value, the Ibfd value with high MUL value > + has been selected. A higher MUL value results in a lower sampling rate of > + the I2C signals. This gives the I2C module greater immunity against glitches > + in the I2C signals. > + > + These values can be referred from NXP QorIQ Layerscape series SOC Reference > + Manual in which this I2C ip has been used. e.g. LX2160ARM, LS1043ARM > +**/ > +STATIC CONST I2C_CLOCK_DIVISOR_PAIR mI2cClockDivisorGlitchEnabled[] = { > + { 34, 0x0 }, { 36, 0x1 }, { 38, 0x2 }, { 40, 0x3 }, > + { 42, 0x4 }, { 44, 0x8 }, { 48, 0x9 }, { 52, 0xA }, > + { 54, 0x7 }, { 56, 0xB }, { 60, 0xC }, { 64, 0x10 }, > + { 68, 0x40 }, { 72, 0x41 }, { 76, 0x42 }, { 80, 0x43 }, > + { 84, 0x44 }, { 88, 0x48 }, { 96, 0x49 }, { 104, 0x4A }, > + { 108, 0x47 }, { 112, 0x4B }, { 120, 0x4C }, { 128, 0x50 }, > + { 136, 0x80 }, { 144, 0x81 }, { 152, 0x82 }, { 160, 0x83 }, > + { 168, 0x84 }, { 176, 0x88 }, { 192, 0x89 }, { 208, 0x8A }, > + { 216, 0x87 }, { 224, 0x8B }, { 240, 0x8C }, { 256, 0x90 }, > + { 288, 0x91 }, { 320, 0x92 }, { 336, 0x8F }, { 352, 0x93 }, > + { 384, 0x98 }, { 416, 0x95 }, { 448, 0x99 }, { 480, 0x96 }, > + { 512, 0x9A }, { 576, 0x9B }, { 640, 0xA0 }, { 704, 0x9D }, > + { 768, 0xA1 }, { 832, 0x9E }, { 896, 0xA2 }, { 960, 0x67 }, > + { 1024, 0xA3 }, { 1152, 0xA4 }, { 1280, 0xA8 }, { 1536, 0xA9 }, > + { 1792, 0xAA }, { 1920, 0xA7 }, { 2048, 0xAB }, { 2304, 0xAC }, > + { 2560, 0xB0 }, { 3072, 0xB1 }, { 3584, 0xB2 }, { 3840, 0xAF }, > + { 4096, 0xB3 }, { 4608, 0xB4 }, { 5120, 0xB8 }, { 6144, 0xB9 }, > + { 7168, 0xBA }, { 7680, 0xB7 }, { 8192, 0xBB }, { 9216, 0xBC }, > + { 10240, 0xBD }, { 12288, 0xBE }, { 15360, 0xBF } > +}; > + > +/** > + I2C divisor and Ibfd register values when glitch filter is disabled > + > + In case of duplicate SCL Divisor value, the Ibfd value with high MUL value > + has been selected. A higher MUL value results in a lower sampling rate of > + the I2C signals. This gives the I2C module greater immunity against glitches > + in the I2C signals. > + > + These values can be referred from NXP QorIQ Layerscape series SOC Reference > + Manual in which this I2C ip has been used. e.g. LX2160ARM, LS1043ARM > +**/ > +STATIC CONST I2C_CLOCK_DIVISOR_PAIR mI2cClockDivisorGlitchDisabled[] = { > + { 20, 0x0 },{ 22, 0x1 },{ 24, 0x2 },{ 26, 0x3 }, > + { 28, 0x8 },{ 30, 0x5 },{ 32, 0x9 },{ 34, 0x6 }, > + { 36, 0x0A },{ 40, 0x40 },{ 44, 0x41 },{ 48, 0x42 }, > + { 52, 0x43 },{ 56, 0x48 },{ 60, 0x45 },{ 64, 0x49 }, > + { 68, 0x46 },{ 72, 0x4A },{ 80, 0x80 },{ 88, 0x81 }, > + { 96, 0x82 },{ 104, 0x83 },{ 112, 0x88 },{ 120, 0x85 }, > + { 128, 0x89 },{ 136, 0x86 },{ 144, 0x8A },{ 160, 0x8B }, > + { 176, 0x8C },{ 192, 0x90 },{ 208, 0x56 },{ 224, 0x91 }, > + { 240, 0x1F },{ 256, 0x92 },{ 272, 0x8F },{ 288, 0x93 }, > + { 320, 0x98 },{ 352, 0x95 },{ 384, 0x99 },{ 416, 0x96 }, > + { 448, 0x9A },{ 480, 0x5F },{ 512, 0x9B },{ 576, 0x9C }, > + { 640, 0xA0 },{ 768, 0xA1 },{ 896, 0xA2 },{ 960, 0x9F }, > + { 1024, 0xA3 },{ 1152, 0xA4 },{ 1280, 0xA8 },{ 1536, 0xA9 }, > + { 1792, 0xAA },{ 1920, 0xA7 },{ 2048, 0xAB },{ 2304, 0xAC }, > + { 2560, 0xAD },{ 3072, 0xB1 },{ 3584, 0xB2 },{ 3840, 0xAF }, > + { 4096, 0xB3 },{ 4608, 0xB4 },{ 5120, 0xB8 },{ 6144, 0xB9 }, > + { 7168, 0xBA },{ 7680, 0xB7 },{ 8192, 0xBB },{ 9216, 0xBC }, > + { 10240, 0xBD },{ 12288, 0xBE },{ 15360, 0xBF } > +}; > + > +/** > + A-009203 : I2C may not work reliably with the default setting > + > + Description : The clocking circuitry of I2C module may not work reliably due > + to the slow rise time of SCL signal. > + Workaround : Enable the receiver digital filter by setting IBDBG[GLFLT_EN] > + to 1. refer https://patchwork.ozlabs.org/patch/453575/ > +**/ > +STATIC > +VOID > +I2cErratumA009203 ( > + IN UINTN Base > + ) > +{ > + I2C_REGS *Regs; > + > + Regs = (I2C_REGS *)Base; > + > + MmioOr8 ((UINTN)&Regs->Ibdbg, I2C_IBDBG_GLFLT_EN); > +} > + > +/** > + software reset of the entire I2C module. > + The module is reset and disabled. > + Status register fields (IBSR) are cleared. > + > + @param[in] Base Base Address of I2c controller's registers > + > + @return EFI_SUCCESS successfuly reset the I2c module > +**/ > +EFI_STATUS > +I2cReset ( > + IN UINTN Base > + ) > +{ > + I2C_REGS *Regs; > + > + Regs = (I2C_REGS *)Base; > + > + MmioOr8 ((UINTN)&Regs->Ibcr, I2C_IBCR_MDIS); > + MmioOr8 ((UINTN)&Regs->Ibsr, (I2C_IBSR_IBAL | I2C_IBSR_IBIF)); > + MmioAnd8 ((UINTN)&Regs->Ibcr, ~(I2C_IBCR_IBIE | I2C_IBCR_DMAEN)); > + MmioAnd8 ((UINTN)&Regs->Ibic, (UINT8)(~I2C_IBIC_BIIE)); > + > + return EFI_SUCCESS; > +} > + > +/** > + Configure I2c bus to operate at a given speed > + > + @param[in] Base Base Address of I2c controller's registers > + @param[in] I2cBusClock Input clock to I2c controller > + @param[in] Speed speed to be configured for I2c bus > + > + @return EFI_SUCCESS successfuly setup the i2c bus > +**/ > +EFI_STATUS > +I2cInitialize ( > + IN UINTN Base, > + IN UINT64 I2cBusClock, > + IN UINT64 Speed > + ) > +{ > + I2C_REGS *Regs; > + UINT16 ClockDivisor; > + UINT8 Ibfd; // I2c Bus Frequency Divider Register > + CONST I2C_CLOCK_DIVISOR_PAIR *ClockDivisorPair; > + UINT32 ClockDivisorPairSize; > + UINT32 Index; > + > + Regs = (I2C_REGS *)Base; > + if (FeaturePcdGet (PcdI2cErratumA009203)) { > + // Apply Erratum A-009203 before writing Ibfd register > + I2cErratumA009203 (Base); > + } > + > + Ibfd = 0; > + ClockDivisor = (I2cBusClock + Speed - 1) / Speed; > + > + if (MmioRead8 ((UINTN)&Regs->Ibdbg) & I2C_IBDBG_GLFLT_EN) { > + ClockDivisorPair = mI2cClockDivisorGlitchEnabled; > + ClockDivisorPairSize = ARRAY_SIZE (mI2cClockDivisorGlitchEnabled); > + } else { > + ClockDivisorPair = mI2cClockDivisorGlitchDisabled; > + ClockDivisorPairSize = ARRAY_SIZE (mI2cClockDivisorGlitchDisabled); > + } > + > + if (ClockDivisor > ClockDivisorPair[ClockDivisorPairSize - 1].Divisor) { > + Ibfd = ClockDivisorPair[ClockDivisorPairSize - 1].Ibfd; > + } else { > + for (Index = 0; Index < ClockDivisorPairSize; Index++) { > + if (ClockDivisorPair[Index].Divisor >= ClockDivisor) { > + Ibfd = ClockDivisorPair[Index].Ibfd; > + break; > + } > + } > + } > + > + MmioWrite8 ((UINTN)&Regs->Ibfd, Ibfd); > + > + I2cReset (Base); > + > + return EFI_SUCCESS; > +} > + > +STATIC > +EFI_STATUS > +I2cBusTestBusBusy ( > + IN I2C_REGS *Regs, > + IN BOOLEAN TestBusy > + ) > +{ > + UINT32 Index; > + UINT8 Reg; > + > + for (Index = 0; Index < I2C_NUM_RETRIES; Index++) { > + Reg = MmioRead8 ((UINTN)&Regs->Ibsr); > + > + if (Reg & I2C_IBSR_IBAL) { > + MmioWrite8 ((UINTN)&Regs->Ibsr, Reg); > + return EFI_NOT_READY; > + } > + > + if (TestBusy && (Reg & I2C_IBSR_IBB)) { > + break; > + } > + > + if (!TestBusy && !(Reg & I2C_IBSR_IBB)) { > + break; > + } > + > + MicroSecondDelay (1); > + } > + > + if (Index == I2C_NUM_RETRIES) { > + return EFI_TIMEOUT; > + } > + > + return EFI_SUCCESS; > +} > + > +STATIC > +EFI_STATUS > +I2cTransferComplete ( > + IN I2C_REGS *Regs, > + IN BOOLEAN TestRxAck > +) > +{ > + UINT32 Index; > + UINT8 Reg; > + > + for (Index = 0; Index < I2C_NUM_RETRIES; Index++) { > + Reg = MmioRead8 ((UINTN)&Regs->Ibsr); > + > + if (Reg & I2C_IBSR_IBIF) { > + // Write 1 to clear the IBIF field > + MmioWrite8 ((UINTN)&Regs->Ibsr, Reg); > + break; > + } > + > + MicroSecondDelay (1); > + } > + > + if (Index == I2C_NUM_RETRIES) { > + return EFI_TIMEOUT; > + } > + > + if (TestRxAck && (Reg & I2C_IBSR_RXAK)) { > + return EFI_NO_RESPONSE; > + } > + > + if (Reg & I2C_IBSR_TCF) { > + return EFI_SUCCESS; > + } > + > + return EFI_DEVICE_ERROR; > +} > + > +STATIC > +EFI_STATUS > +I2cRead ( > + IN I2C_REGS *Regs, > + IN UINT32 SlaveAddress, > + IN EFI_I2C_OPERATION *Operation, > + IN BOOLEAN IsLastOperation > +) > +{ > + EFI_STATUS Status; > + UINTN Index; > + > + // Write Slave Address > + MmioWrite8 ((UINTN)&Regs->Ibdr, (SlaveAddress << BIT0) | BIT0); > + Status = I2cTransferComplete (Regs, I2C_BUS_TEST_RX_ACK); > + if (EFI_ERROR (Status)) { > + return Status; > + } > + // select Receive mode. > + MmioAnd8 ((UINTN)&Regs->Ibcr, ~I2C_IBCR_TXRX); > + if (Operation->LengthInBytes > 1) { > + // Set No ACK = 0 > + MmioAnd8 ((UINTN)&Regs->Ibcr, ~I2C_IBCR_NOACK); > + } > + > + // Perform a dummy read to initiate the receive operation. > + MmioRead8 ((UINTN)&Regs->Ibdr); > + > + for (Index = 0; Index < Operation->LengthInBytes; Index++) { > + Status = I2cTransferComplete (Regs, I2C_BUS_NO_TEST_RX_ACK); > + if (EFI_ERROR (Status)) { > + return Status; > + } > + if (Index == (Operation->LengthInBytes - 2)) { > + // Set No ACK = 1 > + MmioOr8 ((UINTN)&Regs->Ibcr, I2C_IBCR_NOACK); > + } else if (Index == (Operation->LengthInBytes - 1)) { > + if (!IsLastOperation) { > + // select Transmit mode (for repeat start) > + MmioOr8 ((UINTN)&Regs->Ibcr, I2C_IBCR_TXRX); > + } else { > + // Generate Stop Signal > + MmioAnd8 ((UINTN)&Regs->Ibcr, ~(I2C_IBCR_MSSL | I2C_IBCR_TXRX)); > + Status = I2cBusTestBusBusy (Regs, I2C_BUS_TEST_IDLE); > + if (EFI_ERROR (Status)) { > + return Status; > + } > + } > + } > + Operation->Buffer[Index] = MmioRead8 ((UINTN)&Regs->Ibdr); > + } > + > + return EFI_SUCCESS; > +} > + > +STATIC > +EFI_STATUS > +I2cWrite ( > + IN I2C_REGS *Regs, > + IN UINT32 SlaveAddress, > + IN EFI_I2C_OPERATION *Operation > +) > +{ > + EFI_STATUS Status; > + UINTN Index; > + > + // Write Slave Address > + MmioWrite8 ((UINTN)&Regs->Ibdr, (SlaveAddress << BIT0) & (UINT8)(~BIT0)); > + Status = I2cTransferComplete (Regs, I2C_BUS_TEST_RX_ACK); > + if (EFI_ERROR (Status)) { > + return Status; > + } > + > + // Write Data > + for (Index = 0; Index < Operation->LengthInBytes; Index++) { > + MmioWrite8 ((UINTN)&Regs->Ibdr, Operation->Buffer[Index]); > + Status = I2cTransferComplete (Regs, I2C_BUS_TEST_RX_ACK); > + if (EFI_ERROR (Status)) { > + return Status; > + } > + } > + return EFI_SUCCESS; > +} > + > +STATIC > +EFI_STATUS > +I2cStop ( > + IN I2C_REGS *Regs > + ) > +{ > + EFI_STATUS Status; > + UINT8 Reg; > + > + Status = EFI_SUCCESS; > + Reg = MmioRead8 ((UINTN)&Regs->Ibsr); > + if (Reg & I2C_IBSR_IBB) { > + // Generate Stop Signal > + MmioAnd8 ((UINTN)&Regs->Ibcr, ~(I2C_IBCR_MSSL | I2C_IBCR_TXRX)); > + Status = I2cBusTestBusBusy (Regs, I2C_BUS_TEST_IDLE); > + if (EFI_ERROR (Status)) { > + return Status; > + } > + } > + > + // Disable I2c Controller > + MmioOr8 ((UINTN)&Regs->Ibcr, I2C_IBCR_MDIS); > + > + return Status; > +} > + > +STATIC > +EFI_STATUS > +I2cStart ( > + IN I2C_REGS *Regs > + ) > +{ > + EFI_STATUS Status; > + > + MmioOr8 ((UINTN)&Regs->Ibsr, (I2C_IBSR_IBAL | I2C_IBSR_IBIF)); > + MmioAnd8 ((UINTN)&Regs->Ibcr, (UINT8)(~I2C_IBCR_MDIS)); > + > + //Wait controller to be stable > + MicroSecondDelay (1); > + > + // Generate Start Signal > + MmioOr8 ((UINTN)&Regs->Ibcr, I2C_IBCR_MSSL); > + Status = I2cBusTestBusBusy (Regs, I2C_BUS_TEST_BUSY); > + if (EFI_ERROR (Status)) { > + return Status; > + } > + > + // Select Transmit Mode. set No ACK = 1 > + MmioOr8 ((UINTN)&Regs->Ibcr, (I2C_IBCR_TXRX | I2C_IBCR_NOACK)); > + > + return Status; > +} > + > +/** > + Transfer data to/from I2c slave device > + > + @param[in] Base Base Address of I2c controller's registers > + @param[in] SlaveAddress Slave Address from which data is to be read > + @param[in] RequestPacket Pointer to an EFI_I2C_REQUEST_PACKET structure > + describing the I2C transaction > + > + @return EFI_SUCCESS successfuly transfer the data > + @return EFI_DEVICE_ERROR There was an error while transferring data through > + I2c bus > + @return EFI_NO_RESPONSE There was no Ack from i2c device > + @return EFI_TIMEOUT I2c Bus is busy > + @return EFI_NOT_READY I2c Bus Arbitration lost > +**/ > +EFI_STATUS > +I2cBusXfer ( > + IN UINTN Base, > + IN UINT32 SlaveAddress, > + IN EFI_I2C_REQUEST_PACKET *RequestPacket > + ) > +{ > + UINTN Index; > + I2C_REGS *Regs; > + EFI_I2C_OPERATION *Operation; > + EFI_STATUS Status; > + BOOLEAN IsLastOperation; > + > + Regs = (I2C_REGS *)Base; > + IsLastOperation = FALSE; > + > + Status = I2cBusTestBusBusy (Regs, I2C_BUS_TEST_IDLE); > + if (EFI_ERROR (Status)) { > + goto ErrorExit; > + } > + > + Status = I2cStart (Regs); > + if (EFI_ERROR (Status)) { > + goto ErrorExit; > + } > + > + for (Index = 0, Operation = RequestPacket->Operation; > + Index < RequestPacket->OperationCount; > + Index++, Operation++) { > + if (Index == (RequestPacket->OperationCount - 1)) { > + IsLastOperation = TRUE; > + } > + // Send repeat start after first transmit/recieve > + if (Index) { > + MmioOr8 ((UINTN)&Regs->Ibcr, I2C_IBCR_RSTA); > + Status = I2cBusTestBusBusy (Regs, I2C_BUS_TEST_BUSY); > + if (EFI_ERROR (Status)) { > + goto ErrorExit; > + } > + } > + // Read/write data > + if (Operation->Flags & I2C_FLAG_READ) { > + Status = I2cRead (Regs, SlaveAddress, Operation, IsLastOperation); > + } else { > + Status = I2cWrite (Regs, SlaveAddress, Operation); > + } > + if (EFI_ERROR (Status)) { > + goto ErrorExit; > + } > + } > + > +ErrorExit: > + > + I2cStop (Regs); > + > + return Status; > +} > + > +/** > + Read a register from I2c slave device. This API is wrapper around I2cBusXfer > + > + @param[in] Base Base Address of I2c controller's registers > + @param[in] SlaveAddress Slave Address from which register value is > + to be read > + @param[in] RegAddress Register Address in Slave's memory map > + @param[in] RegAddressWidthInBytes Number of bytes in RegAddress to send to > + I2c Slave for simple reads without any > + register, make this value = 0 > + (RegAddress is don't care in that case) > + @param[out] RegValue Value to be read from I2c slave's regiser > + @param[in] RegValueNumBytes Number of bytes to read from I2c slave > + register > + > + @return EFI_SUCCESS successfuly read the registers > + @return EFI_DEVICE_ERROR There was an error while transferring data through > + I2c bus > + @return EFI_NO_RESPONSE There was no Ack from i2c device > + @return EFI_TIMEOUT I2c Bus is busy > + @return EFI_NOT_READY I2c Bus Arbitration lost > +**/ > +EFI_STATUS > +I2cBusReadReg ( > + IN UINTN Base, > + IN UINT32 SlaveAddress, > + IN UINT64 RegAddress, > + IN UINT8 RegAddressWidthInBytes, > + OUT UINT8 *RegValue, > + IN UINT32 RegValueNumBytes > + ) > +{ > + EFI_I2C_OPERATION *Operations; > + I2C_REG_REQUEST RequestPacket; > + UINTN OperationCount; > + UINT8 Address[sizeof (RegAddress)]; > + UINT8 *AddressPtr; > + EFI_STATUS Status; > + > + ZeroMem (&RequestPacket, sizeof (RequestPacket)); > + OperationCount = 0; > + Operations = RequestPacket.Operation; > + AddressPtr = Address; > + > + if (RegAddressWidthInBytes > ARRAY_SIZE (Address)) { > + return EFI_INVALID_PARAMETER; > + } > + > + if (RegAddressWidthInBytes != 0) { > + Operations[OperationCount].LengthInBytes = RegAddressWidthInBytes; > + Operations[OperationCount].Buffer = AddressPtr; > + while (RegAddressWidthInBytes--) { > + *AddressPtr++ = RegAddress >> (8 * RegAddressWidthInBytes); > + } > + OperationCount++; > + } > + > + Operations[OperationCount].LengthInBytes = RegValueNumBytes; > + Operations[OperationCount].Buffer = RegValue; > + Operations[OperationCount].Flags = I2C_FLAG_READ; > + OperationCount++; > + > + RequestPacket.OperationCount = OperationCount; > + > + Status = I2cBusXfer ( > + Base, SlaveAddress, > + (EFI_I2C_REQUEST_PACKET *)&RequestPacket > + ); > + > + return Status; > +} > -- > 2.17.1 >