From: "Leif Lindholm" <leif@nuviainc.com>
To: Pankaj Bansal <pankaj.bansal@oss.nxp.com>
Cc: Meenakshi Aggarwal <meenakshi.aggarwal@nxp.com>,
Michael D Kinney <michael.d.kinney@intel.com>,
devel@edk2.groups.io, Varun Sethi <V.Sethi@nxp.com>,
Samer El-Haj-Mahmoud <Samer.El-Haj-Mahmoud@arm.com>,
Jon Nettleton <jon@solid-run.com>,
Ard Biesheuvel <ard.biesheuvel@linaro.org>
Subject: Re: [PATCH edk2-platforms v4 01/24] Silicon/NXP: Add I2c lib
Date: Tue, 5 May 2020 16:11:28 +0100 [thread overview]
Message-ID: <20200505151128.GH21486@vanye> (raw)
In-Reply-To: <20200501054955.13025-2-pankaj.bansal@oss.nxp.com>
On Fri, May 01, 2020 at 11:19:32 +0530, Pankaj Bansal wrote:
> From: Pankaj Bansal <pankaj.bansal@nxp.com>
>
> 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 <pankaj.bansal@nxp.com>
> ---
>
> 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 <leif@nuviainc.com>
> - 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 <Uefi.h>
> +#include <Pi/PiI2c.h>
> +
> +/**
> + 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 <Pi/PiI2c.h>
> +#include <Uefi.h>
> +
> +/** 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 <Uefi.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/I2cLib.h>
> +#include <Library/IoLib.h>
> +#include <Library/TimerLib.h>
> +
> +#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
>
next prev parent reply other threads:[~2020-05-05 15:11 UTC|newest]
Thread overview: 35+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-05-01 5:49 [PATCH edk2-platforms v4 00/24] Add PEI phase to LS1043ARDB Platform Pankaj Bansal
2020-05-01 5:49 ` [PATCH edk2-platforms v4 01/24] Silicon/NXP: Add I2c lib Pankaj Bansal
2020-05-05 15:11 ` Leif Lindholm [this message]
2020-05-01 5:49 ` [PATCH edk2-platforms v4 02/24] Silicon/NXP: changes to use I2clib in i2cdxe Pankaj Bansal
2020-05-01 5:49 ` [PATCH edk2-platforms v4 03/24] Silicon/NXP/I2cDxe: Fix I2c Timeout with RTC Pankaj Bansal
2020-05-01 5:49 ` [PATCH edk2-platforms v4 04/24] Silicon/Maxim: Fix bug in RtcWrite in Ds1307RtcLib Pankaj Bansal
2020-05-01 5:49 ` [PATCH edk2-platforms v4 05/24] Silicon/Maxim: Add comments " Pankaj Bansal
2020-05-01 5:49 ` [PATCH edk2-platforms v4 06/24] NXP/LS1043aRdb: Move Soc specific components to soc files Pankaj Bansal
2020-05-01 5:49 ` [PATCH edk2-platforms v4 07/24] Silicon/NXP: remove print information from Soc lib Pankaj Bansal
2020-05-01 5:49 ` [PATCH edk2-platforms v4 08/24] Silicon/NXP: remove not needed components Pankaj Bansal
2020-05-01 5:49 ` [PATCH edk2-platforms v4 09/24] Silicon/NXP: Remove unnecessary PCDs Pankaj Bansal
2020-05-01 5:49 ` [PATCH edk2-platforms v4 10/24] Silicon/NXP: Move dsc file Pankaj Bansal
2020-05-01 5:49 ` [PATCH edk2-platforms v4 11/24] Platform/NXP: rename the ArmPlatformLib as per ArmPlatformPkg Pankaj Bansal
2020-05-01 5:49 ` [PATCH edk2-platforms v4 12/24] Silicon/NXP: Move RAM retrieval from SocLib Pankaj Bansal
2020-05-06 10:44 ` Leif Lindholm
2020-05-07 7:28 ` Pankaj Bansal
2020-05-07 10:15 ` Leif Lindholm
2020-05-08 5:31 ` Pankaj Bansal
2020-05-11 10:27 ` Leif Lindholm
2020-05-01 5:49 ` [PATCH edk2-platforms v4 13/24] Platform/NXP/LS1043aRdbPkg: Add Clock retrieval APIs Pankaj Bansal
2020-05-01 5:49 ` [PATCH edk2-platforms v4 14/24] Silicon/NXP: Use Clock retrieval PPI in modules Pankaj Bansal
2020-05-01 5:49 ` [PATCH edk2-platforms v4 15/24] Silicon: NXP: Remove direct calls to SwapMmio* APIs Pankaj Bansal
2020-05-06 10:48 ` Leif Lindholm
2020-05-01 5:49 ` [PATCH edk2-platforms v4 16/24] Silicon/NXP: Add Chassis2 Package Pankaj Bansal
2020-05-06 10:52 ` Leif Lindholm
2020-05-01 5:49 ` [PATCH edk2-platforms v4 17/24] Silicon/NXP/LS1043A: Use ChassisLib from Chassis2 Pkg Pankaj Bansal
2020-05-06 10:54 ` Leif Lindholm
2020-05-01 5:49 ` [PATCH edk2-platforms v4 18/24] Silicon/NXP/LS1043A: Move SocLib to Soc Package Pankaj Bansal
2020-05-01 5:49 ` [PATCH edk2-platforms v4 19/24] NXP/LS1043aRdbPkg/ArmPlatformLib: Remove extern SocInit Pankaj Bansal
2020-05-01 5:49 ` [PATCH edk2-platforms v4 20/24] NXP: LS1043aRdbPkg: Use ArmPlatformHelper.S from ArmPlatformPkg Pankaj Bansal
2020-05-01 5:49 ` [PATCH edk2-platforms v4 21/24] Platform/NXP: Use FV rules from ArmVirtPkg Pankaj Bansal
2020-05-01 5:49 ` [PATCH edk2-platforms v4 22/24] Platform/NXP/LS1043aRdbPkg: Add VarStore Pankaj Bansal
2020-05-01 5:49 ` [PATCH edk2-platforms v4 23/24] Silicon/NXP: move MemoryInitPeiLib as per PEIM structures Pankaj Bansal
2020-05-01 5:49 ` [PATCH edk2-platforms v4 24/24] Platform/NXP/LS1043aRdbPkg: Add PEI Phase Pankaj Bansal
2020-05-06 10:57 ` Leif Lindholm
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20200505151128.GH21486@vanye \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox