From: "Pankaj Bansal" <pankaj.bansal@nxp.com>
To: Leif Lindholm <leif@nuviainc.com>,
Meenakshi Aggarwal <meenakshi.aggarwal@nxp.com>,
Michael D Kinney <michael.d.kinney@intel.com>,
Varun Sethi <V.Sethi@nxp.com>
Cc: devel@edk2.groups.io, Pankaj Bansal <pankaj.bansal@nxp.com>
Subject: [PATCH 01/19] Silicon/NXP: Add I2c lib
Date: Fri, 7 Feb 2020 18:13:10 +0530 [thread overview]
Message-ID: <20200207124328.8723-2-pankaj.bansal@nxp.com> (raw)
In-Reply-To: <20200207124328.8723-1-pankaj.bansal@nxp.com>
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=y, Size: 28738 bytes --]
I2c lib is going to be used in PrePeiCore sec module to get the
System clock information from devices connected to i2c (like fpga
or clcok generator)
since we don't have support of DXE modules this early in boot stage,
move the i2c controller functionality in library.
Signed-off-by: Pankaj Bansal <pankaj.bansal@nxp.com>
---
Platform/NXP/NxpQoriqLs.dsc.inc | 4 +-
Silicon/NXP/Include/Library/I2cLib.h | 99 ++++
Silicon/NXP/Library/I2cLib/I2cLib.c | 532 ++++++++++++++++++++
Silicon/NXP/Library/I2cLib/I2cLib.inf | 30 ++
Silicon/NXP/Library/I2cLib/I2cLibInternal.h | 95 ++++
Silicon/NXP/NxpQoriqLs.dec | 10 +-
6 files changed, 768 insertions(+), 2 deletions(-)
create mode 100644 Silicon/NXP/Include/Library/I2cLib.h
create mode 100644 Silicon/NXP/Library/I2cLib/I2cLib.c
create mode 100644 Silicon/NXP/Library/I2cLib/I2cLib.inf
create mode 100644 Silicon/NXP/Library/I2cLib/I2cLibInternal.h
diff --git a/Platform/NXP/NxpQoriqLs.dsc.inc b/Platform/NXP/NxpQoriqLs.dsc.inc
index fa5f30dd39..b28e0615f7 100644
--- a/Platform/NXP/NxpQoriqLs.dsc.inc
+++ b/Platform/NXP/NxpQoriqLs.dsc.inc
@@ -1,6 +1,6 @@
# @file
#
-# Copyright 2017-2019 NXP.
+# Copyright 2017-2020 NXP.
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
@@ -94,6 +94,8 @@
NonDiscoverableDeviceRegistrationLib|MdeModulePkg/Library/NonDiscoverableDeviceRegistrationLib/NonDiscoverableDeviceRegistrationLib.inf
ReportStatusCodeLib|MdeModulePkg/Library/DxeReportStatusCodeLib/DxeReportStatusCodeLib.inf
+ I2cLib|Silicon/NXP/Library/I2cLib/I2cLib.inf
+
[LibraryClasses.common.SEC]
PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
UefiDecompressLib|MdePkg/Library/BaseUefiDecompressLib/BaseUefiDecompressLib.inf
diff --git a/Silicon/NXP/Include/Library/I2cLib.h b/Silicon/NXP/Include/Library/I2cLib.h
new file mode 100644
index 0000000000..3594e3fc2f
--- /dev/null
+++ b/Silicon/NXP/Include/Library/I2cLib.h
@@ -0,0 +1,99 @@
+/** @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 <Pi/PiI2c.h>
+
+/**
+ Early init I2C for reading the sysclk from I2c slave device.
+ I2c bus clock is determined from the clock input to I2c controller.
+ The clock input to I2c controller is derived from the sysclk.
+ sysclk is determined by clock generator, which is controller by i2c.
+
+ So, it's a chicken-egg problem to read the sysclk from clock generator.
+ To break this cycle (i.e. to read the sysclk), we setup the i2c bus clock to
+ lowest value, in the hope that it won't be out of clock generator's supported
+ i2c clock frequency. Once we have the correct sysclk, we can setup the correct
+ i2c bus clock.
+
+ @param[in] Base Base Address of I2c controller's registers
+
+ @return EFI_SUCCESS successfuly setup the i2c bus for reading sysclk
+ @return DEVICE_ERROR There was an error setting up the I2c bus
+**/
+EFI_STATUS
+I2cEarlyInitialize (
+ IN UINTN Base
+ );
+
+/**
+ Configure I2c bus to opearte 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
+**/
+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 setup the i2c bus for reading sysclk
+ @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 setup the i2c bus for reading sysclk
+ @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 UINT8 RegValueNumBytes
+ );
+
+#endif // __I2C_LIB_H__
diff --git a/Silicon/NXP/Library/I2cLib/I2cLib.c b/Silicon/NXP/Library/I2cLib/I2cLib.c
new file mode 100644
index 0000000000..f33c09d6d1
--- /dev/null
+++ b/Silicon/NXP/Library/I2cLib/I2cLib.c
@@ -0,0 +1,532 @@
+/** @file
+ I2c Lib to control I2c controller.
+
+ Copyright 2020 NXP
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#include <Uefi.h>
+#include <Library/I2cLib.h>
+#include <Library/TimerLib.h>
+#include <Library/IoLib.h>
+#include <Library/BaseMemoryLib.h>
+
+#include "I2cLibInternal.h"
+
+/*
+ * I2C divider and hold values when glitch filter is enabled
+ * taken from table 21-14, LX2160ARM_RevE 01/2020
+ *
+ * In case of duplicate SCL Divider value, the IBC 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.
+ */
+STATIC I2C_CLOCK_DIVIDER_PAIR I2cClockDividerGlitchEnabled[] = {
+ { 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 divider and hold values when glitch filter is disabled
+ * taken from table 21-13, LX2160ARM_RevE 01/2020
+ *
+ * In case of duplicate SCL Divider value, the IBC 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.
+ */
+STATIC I2C_CLOCK_DIVIDER_PAIR I2cClockDividerGlitchDisabled[] = {
+ { 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 }
+};
+
+/**
+ ERR009203 : 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.
+*/
+STATIC
+VOID
+ I2cErratumA009203 (
+ IN UINTN Base
+ )
+{
+ I2C_REGS *Regs;
+
+ Regs = (I2C_REGS *)Base;
+
+ MmioOr8 ( (UINTN)&Regs->Ibdbg, I2C_IBDBG_GLFLT_EN);
+}
+
+/**
+ Early init I2C for reading the sysclk from I2c slave device.
+ I2c bus clock is determined from the clock input to I2c controller.
+ The clock input to I2c controller is derived from the sysclk.
+ sysclk is determined by clock generator, which is controller by i2c.
+
+ So, it's a chicken-egg problem to read the sysclk from clock generator.
+ To break this cycle (i.e. to read the sysclk), we setup the i2c bus clock to
+ lowest value, in the hope that it won't be out of clock generator's supported
+ i2c clock frequency. Once we have the correct sysclk, we can setup the correct
+ i2c bus clock.
+
+ @param[in] Base Base Address of I2c controller's registers
+
+ @return EFI_SUCCESS successfuly setup the i2c bus for reading sysclk
+**/
+EFI_STATUS
+I2cEarlyInitialize (
+ IN UINTN Base
+ )
+{
+ I2C_REGS *Regs;
+ UINT8 Ibc;
+
+ Regs = (I2C_REGS *)Base;
+ if (FeaturePcdGet (PcdI2cErratumA009203)) {
+ I2cErratumA009203 (Base);
+ }
+
+ if (MmioRead8 ( (UINTN)&Regs->Ibdbg) & I2C_IBDBG_GLFLT_EN) {
+ Ibc = I2cClockDividerGlitchEnabled[ARRAY_SIZE (I2cClockDividerGlitchEnabled) - 1].Ibc;
+ } else {
+ Ibc = I2cClockDividerGlitchDisabled[ARRAY_SIZE (I2cClockDividerGlitchDisabled) - 1].Ibc;
+ }
+
+ MmioWrite8 ( (UINTN)&Regs->Ibfd, Ibc);
+ // Reset Module
+ MmioOr8 ( (UINTN)&Regs->Ibcr, I2C_IBCR_MDIS);
+ 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 opearte 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
+**/
+EFI_STATUS
+I2cInitialize (
+ IN UINTN Base,
+ IN UINT64 I2cBusClock,
+ IN UINT64 Speed
+ )
+{
+ I2C_REGS *Regs;
+ UINT16 ClockDivider;
+ UINT8 Ibc;
+ I2C_CLOCK_DIVIDER_PAIR *ClockDividerPair;
+ UINT32 ClockDividerPairSize;
+ UINT32 Index;
+
+ Regs = (I2C_REGS *)Base;
+ if (FeaturePcdGet (PcdI2cErratumA009203)) {
+ I2cErratumA009203 (Base);
+ }
+
+ Ibc = 0;
+ ClockDivider = (I2cBusClock + Speed - 1) / Speed;
+
+ if (MmioRead8 ( (UINTN)&Regs->Ibdbg) & I2C_IBDBG_GLFLT_EN) {
+ ClockDividerPair = I2cClockDividerGlitchEnabled;
+ ClockDividerPairSize = ARRAY_SIZE (I2cClockDividerGlitchEnabled);
+ } else {
+ ClockDividerPair = I2cClockDividerGlitchDisabled;
+ ClockDividerPairSize = ARRAY_SIZE (I2cClockDividerGlitchDisabled);
+ }
+
+ if (ClockDivider > ClockDividerPair[ClockDividerPairSize - 1].Divider) {
+ Ibc = ClockDividerPair[ClockDividerPairSize - 1].Ibc;
+ } else {
+ for (Index = 0; Index < ClockDividerPairSize; Index++) {
+ if (ClockDividerPair[Index].Divider >= ClockDivider) {
+ Ibc = ClockDividerPair[Index].Ibc;
+ break;
+ }
+ }
+ }
+
+ MmioWrite8 ( (UINTN)&Regs->Ibfd, Ibc);
+ // Reset Module
+ MmioOr8 ( (UINTN)&Regs->Ibcr, I2C_IBCR_MDIS);
+ MmioAnd8 ( (UINTN)&Regs->Ibcr, ~(I2C_IBCR_IBIE | I2C_IBCR_DMAEN));
+ MmioAnd8 ( (UINTN)&Regs->Ibic, (UINT8)(~I2C_IBIC_BIIE));
+
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+I2cBusTestBusBusy (
+ IN I2C_REGS *Regs,
+ IN BOOLEAN TestBusy
+ )
+{
+ UINT8 Index;
+ UINT8 Reg;
+
+ for (Index = 0; Index < 500; 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 == 500) {
+ return EFI_TIMEOUT;
+ } else {
+ return EFI_SUCCESS;
+ }
+}
+
+STATIC
+EFI_STATUS
+I2cTransferComplete (
+ IN I2C_REGS *Regs,
+ IN BOOLEAN TestRxAck
+)
+{
+ UINT8 Index;
+ UINT8 Reg;
+
+ for (Index = 0; Index < 500; 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 == 500) {
+ 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);
+ // 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;
+
+ 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));
+
+ // Generate Start Signal
+ MmioOr8 ( (UINTN)&Regs->Ibcr, I2C_IBCR_MSSL);
+ Status = I2cBusTestBusBusy (Regs, I2C_BUS_TEST_BUSY);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ 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 setup the i2c bus for reading sysclk
+ @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 setup the i2c bus for reading sysclk
+ @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 UINT8 RegValueNumBytes
+ )
+{
+ EFI_I2C_OPERATION *Operations;
+ I2C_REG_REQUEST RequestPacket;
+ UINTN OperationCount;
+ UINT8 Address[8];
+ UINT8 *Ptr;
+ EFI_STATUS Status;
+
+ ZeroMem (&RequestPacket, sizeof (RequestPacket));
+ OperationCount = 0;
+ Operations = RequestPacket.Operation;
+ Ptr = Address;
+
+ if (RegAddressWidthInBytes > ARRAY_SIZE (Address)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (RegAddressWidthInBytes != 0) {
+ Operations[OperationCount].LengthInBytes = RegAddressWidthInBytes;
+ Operations[OperationCount].Buffer = Ptr;
+ while (RegAddressWidthInBytes--) {
+ *Ptr++ = 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;
+}
+
diff --git a/Silicon/NXP/Library/I2cLib/I2cLib.inf b/Silicon/NXP/Library/I2cLib/I2cLib.inf
new file mode 100644
index 0000000000..9c8aae100b
--- /dev/null
+++ b/Silicon/NXP/Library/I2cLib/I2cLib.inf
@@ -0,0 +1,30 @@
+#/** @file
+#
+# Copyright 2020 NXP
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+#**/
+
+[Defines]
+ INF_VERSION = 1.27
+ BASE_NAME = I2cLib
+ FILE_GUID = f22393b1-98b6-4067-9ec2-6aa436321f03
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = I2cLib
+
+[Packages]
+ MdePkg/MdePkg.dec
+ Silicon/NXP/NxpQoriqLs.dec
+
+[LibraryClasses]
+ TimerLib
+ IoLib
+
+[Sources.common]
+ I2cLib.c
+
+[FeaturePcd]
+ gNxpQoriqLsTokenSpaceGuid.PcdI2cErratumA009203
+
diff --git a/Silicon/NXP/Library/I2cLib/I2cLibInternal.h b/Silicon/NXP/Library/I2cLib/I2cLibInternal.h
new file mode 100644
index 0000000000..14be9cb740
--- /dev/null
+++ b/Silicon/NXP/Library/I2cLib/I2cLibInternal.h
@@ -0,0 +1,95 @@
+/** @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
+ * timeif the bus is owned by another masterthe 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
+
+typedef struct _I2C_REGS {
+ UINT8 Ibad; // I2c Bus Address Register
+ UINT8 Ibfd; // I2c Bus Frequency Dividor 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 divider, register value pairs
+ */
+typedef struct _I2C_CLOCK_DIVIDER_PAIR {
+ UINT16 Divider;
+ UINT16 Ibc;
+} I2C_CLOCK_DIVIDER_PAIR;
+
+typedef struct {
+ UINTN OperationCount;
+ EFI_I2C_OPERATION Operation[2];
+} I2C_REG_REQUEST;
+
+#endif // __I2C_LIB_INTERNAL_H__
+
diff --git a/Silicon/NXP/NxpQoriqLs.dec b/Silicon/NXP/NxpQoriqLs.dec
index 764b9bb0e2..4a1cfb3e27 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,7 @@
gNxpQoriqLsTokenSpaceGuid.PcdPciLutBigEndian|FALSE|BOOLEAN|0x00000312
gNxpQoriqLsTokenSpaceGuid.PcdWatchdogBigEndian|FALSE|BOOLEAN|0x00000313
gNxpQoriqLsTokenSpaceGuid.PcdIfcBigEndian|FALSE|BOOLEAN|0x00000314
+
+[PcdsFeatureFlag]
+ gNxpQoriqLsTokenSpaceGuid.PcdI2cErratumA009203|FALSE|BOOLEAN|0x00000315
+
--
2.17.1
next prev parent reply other threads:[~2020-02-07 7:23 UTC|newest]
Thread overview: 49+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-02-07 12:43 [PATCH 00/19] ADD LX2160ARDB Platform Support Pankaj Bansal
2020-02-07 12:43 ` Pankaj Bansal [this message]
2020-02-08 17:13 ` [PATCH 01/19] Silicon/NXP: Add I2c lib Leif Lindholm
2020-02-09 11:49 ` [edk2-devel] " Ard Biesheuvel
2020-02-07 12:43 ` [PATCH 02/19] Silicon/NXP: changes to use I2clib in i2cdxe Pankaj Bansal
2020-02-08 17:23 ` Leif Lindholm
2020-02-07 12:43 ` [PATCH 03/19] NXP/LS1043aRdb: Move Soc specific components to soc files Pankaj Bansal
2020-02-08 17:27 ` Leif Lindholm
2020-02-07 12:43 ` [PATCH 04/19] Silicon/NXP: Remove DuartLib and use BaseSerialPortLib16550 Pankaj Bansal
2020-02-08 17:46 ` Leif Lindholm
2020-02-10 5:48 ` Pankaj Bansal
2020-02-12 23:27 ` Leif Lindholm
2020-02-07 12:43 ` [PATCH 05/19] NXP/BaseSerialPortLib16550: remove SerialPortInitalize functionality Pankaj Bansal
2020-02-07 12:43 ` [PATCH 06/19] Silicon/NXP: remove print information from Soc lib Pankaj Bansal
2020-02-10 17:09 ` [EXTERNAL] " Leif Lindholm
2020-02-07 12:43 ` [PATCH 07/19] Silicon/NXP: remove not needed components Pankaj Bansal
2020-02-10 17:11 ` Leif Lindholm
2020-02-11 7:24 ` Pankaj Bansal
2020-02-20 19:05 ` Leif Lindholm
2020-02-07 12:43 ` [PATCH 08/19] Silicon/NXP: Remove unnecessary PCDs Pankaj Bansal
2020-02-10 17:32 ` Leif Lindholm
2020-02-11 8:45 ` Pankaj Bansal
2020-02-20 18:56 ` Leif Lindholm
2020-02-07 12:43 ` [PATCH 09/19] Silicon/NXP: Move dsc file Pankaj Bansal
2020-02-11 11:35 ` Leif Lindholm
2020-02-07 12:43 ` [PATCH 10/19] Platform/NXP: rename the ArmPlatformLib as per ArmPlatformPkg Pankaj Bansal
2020-02-11 11:40 ` Leif Lindholm
2020-02-07 12:43 ` [PATCH 11/19] Silicon/NXP: Add Chassis Lib for Chassis2 Pankaj Bansal
2020-02-11 12:28 ` Leif Lindholm
2020-02-07 12:43 ` [PATCH 12/19] Silicon/NXP/LS1043A: Add SocLib Pankaj Bansal
2020-02-11 12:38 ` Leif Lindholm
2020-02-07 12:43 ` [PATCH 13/19] Silicon/NXP: Move RAM retrieval from SocLib Pankaj Bansal
2020-02-11 13:28 ` Leif Lindholm
2020-02-07 12:43 ` [PATCH 14/19] Silicon/NXP/LS1043A: Replce SocLib Pankaj Bansal
2020-02-11 13:35 ` Leif Lindholm
2020-02-12 9:37 ` Pankaj Bansal
2020-02-12 22:50 ` Leif Lindholm
2020-02-13 11:00 ` Pankaj Bansal
2020-02-20 18:45 ` Leif Lindholm
2020-02-07 12:43 ` [PATCH 15/19] Platform/NXP/LS1043ARDB: introduce PEI Phase Pankaj Bansal
2020-02-12 20:24 ` Leif Lindholm
2020-02-07 12:43 ` [PATCH 16/19] Silicon/NXP: Add Pl011 Serial port lib Pankaj Bansal
2020-02-12 20:26 ` Leif Lindholm
2020-02-07 12:43 ` [PATCH 17/19] Silicon/NXP: Add Chassis3V2 Pankaj Bansal
2020-02-12 20:33 ` Leif Lindholm
2020-02-07 12:43 ` [PATCH 18/19] Silicon/NXP: Add LX2160A SocLib Pankaj Bansal
2020-02-12 21:39 ` Leif Lindholm
2020-02-07 12:43 ` [PATCH 19/19] Platform/NXP: Add LX2160ARDBPKG Pankaj Bansal
2020-02-12 21:36 ` 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=20200207124328.8723-2-pankaj.bansal@nxp.com \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox