mirror of https://github.com/acidanthera/audk.git
1005 lines
27 KiB
C
1005 lines
27 KiB
C
/** @file
|
|
I2C Library for Quark I2C Controller.
|
|
Follows I2C Controller setup instructions as detailed in
|
|
Quark DataSheet (doc id: 329676) Section 19.1/19.1.3.
|
|
|
|
|
|
Copyright (c) 2013-2015 Intel Corporation.
|
|
|
|
This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
**/
|
|
|
|
#include "CommonHeader.h"
|
|
|
|
/**
|
|
The Called to Common Service Entry.
|
|
|
|
@return None.
|
|
|
|
**/
|
|
|
|
VOID
|
|
I2cCommonServiceEntry (
|
|
OUT UINT16 *SaveCmdPtr,
|
|
OUT UINT32 *SaveBar0Ptr
|
|
)
|
|
{
|
|
*SaveBar0Ptr = IohMmPci32 (0, I2C_Bus, I2C_Device, I2C_Func, PCI_BAR0);
|
|
if (((*SaveBar0Ptr) & B_IOH_I2C_GPIO_MEMBAR_ADDR_MASK) == 0) {
|
|
|
|
IohMmPci32(0, I2C_Bus, I2C_Device, I2C_Func, PCI_BAR0) =
|
|
FixedPcdGet32 (PcdIohI2cMmioBase) & B_IOH_I2C_GPIO_MEMBAR_ADDR_MASK;
|
|
|
|
//
|
|
// also Save Cmd Register, Setup by InitializeInternal later during xfers.
|
|
//
|
|
*SaveCmdPtr = IohMmPci16 (0, I2C_Bus, I2C_Device, I2C_Func, PCI_CMD);
|
|
}
|
|
}
|
|
|
|
/**
|
|
The Called on Common Service Exit.
|
|
|
|
@return None.
|
|
|
|
**/
|
|
VOID
|
|
I2cCommonServiceExit (
|
|
IN CONST UINT16 SaveCmd,
|
|
IN CONST UINT32 SaveBar0
|
|
|
|
)
|
|
{
|
|
if ((SaveBar0 & B_IOH_I2C_GPIO_MEMBAR_ADDR_MASK) == 0) {
|
|
IohMmPci16 (0, I2C_Bus, I2C_Device, I2C_Func, PCI_CMD) = SaveCmd;
|
|
IohMmPci32 (0, I2C_Bus, I2C_Device, I2C_Func, PCI_BAR0) = SaveBar0;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
The GetI2CIoPortBaseAddress() function gets IO port base address of I2C Controller.
|
|
|
|
Always reads PCI configuration space to get MMIO base address of I2C Controller.
|
|
|
|
@return The IO port base address of I2C controller.
|
|
|
|
**/
|
|
UINTN
|
|
GetI2CIoPortBaseAddress (
|
|
VOID
|
|
)
|
|
{
|
|
UINTN I2CIoPortBaseAddress;
|
|
|
|
//
|
|
// Get I2C Memory Mapped registers base address.
|
|
//
|
|
I2CIoPortBaseAddress = IohMmPci32(0, I2C_Bus, I2C_Device, I2C_Func, PCI_BAR0);
|
|
|
|
//
|
|
// Make sure that the IO port base address has been properly set.
|
|
//
|
|
ASSERT (I2CIoPortBaseAddress != 0);
|
|
ASSERT (I2CIoPortBaseAddress != 0xFF);
|
|
|
|
return I2CIoPortBaseAddress;
|
|
}
|
|
|
|
|
|
/**
|
|
The EnableI2CMmioSpace() function enables access to I2C MMIO space.
|
|
|
|
**/
|
|
VOID
|
|
EnableI2CMmioSpace (
|
|
VOID
|
|
)
|
|
{
|
|
UINT8 PciCmd;
|
|
|
|
//
|
|
// Read PCICMD. Bus=0, Dev=0, Func=0, Reg=0x4
|
|
//
|
|
PciCmd = IohMmPci8(0, I2C_Bus, I2C_Device, I2C_Func, PCI_REG_PCICMD);
|
|
|
|
//
|
|
// Enable Bus Master(Bit2), MMIO Space(Bit1) & I/O Space(Bit0)
|
|
//
|
|
PciCmd |= 0x7;
|
|
IohMmPci8(0, I2C_Bus, I2C_Device, I2C_Func, PCI_REG_PCICMD) = PciCmd;
|
|
|
|
}
|
|
|
|
/**
|
|
The DisableI2CController() functions disables I2C Controller.
|
|
|
|
**/
|
|
VOID
|
|
DisableI2CController (
|
|
VOID
|
|
)
|
|
{
|
|
UINTN I2CIoPortBaseAddress;
|
|
UINT32 Addr;
|
|
UINT32 Data;
|
|
UINT8 PollCount;
|
|
|
|
PollCount = 0;
|
|
|
|
//
|
|
// Get I2C Memory Mapped registers base address.
|
|
//
|
|
I2CIoPortBaseAddress = GetI2CIoPortBaseAddress ();
|
|
|
|
//
|
|
// Disable the I2C Controller by setting IC_ENABLE.ENABLE to zero
|
|
//
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_ENABLE;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
Data &= ~B_I2C_REG_ENABLE;
|
|
*((volatile UINT32 *) (UINTN)(Addr)) = Data;
|
|
|
|
//
|
|
// Read the IC_ENABLE_STATUS.IC_EN Bit to check if Controller is disabled
|
|
//
|
|
Data = 0xFF;
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_ENABLE_STATUS;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr)) & I2C_REG_ENABLE_STATUS;
|
|
while (Data != 0) {
|
|
//
|
|
// Poll the IC_ENABLE_STATUS.IC_EN Bit to check if Controller is disabled, until timeout (TI2C_POLL*MAX_T_POLL_COUNT).
|
|
//
|
|
PollCount++;
|
|
if (PollCount >= MAX_T_POLL_COUNT) {
|
|
break;
|
|
}
|
|
MicroSecondDelay(TI2C_POLL);
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
Data &= I2C_REG_ENABLE_STATUS;
|
|
}
|
|
|
|
//
|
|
// Asset if controller does not enter Disabled state.
|
|
//
|
|
ASSERT (PollCount < MAX_T_POLL_COUNT);
|
|
|
|
//
|
|
// Read IC_CLR_INTR register to automatically clear the combined interrupt,
|
|
// all individual interrupts and the IC_TX_ABRT_SOURCE register.
|
|
//
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_CLR_INT;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
|
|
}
|
|
|
|
/**
|
|
The EnableI2CController() function enables the I2C Controller.
|
|
|
|
**/
|
|
VOID
|
|
EnableI2CController (
|
|
VOID
|
|
)
|
|
{
|
|
UINTN I2CIoPortBaseAddress;
|
|
UINT32 Addr;
|
|
UINT32 Data;
|
|
|
|
//
|
|
// Get I2C Memory Mapped registers base address.
|
|
//
|
|
I2CIoPortBaseAddress = GetI2CIoPortBaseAddress ();
|
|
|
|
//
|
|
// Enable the I2C Controller by setting IC_ENABLE.ENABLE to 1
|
|
//
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_ENABLE;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
Data |= B_I2C_REG_ENABLE;
|
|
*((volatile UINT32 *) (UINTN)(Addr)) = Data;
|
|
|
|
//
|
|
// Clear overflow and abort error status bits before transactions.
|
|
//
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_CLR_RX_OVER;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_CLR_TX_OVER;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_CLR_TX_ABRT;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
|
|
}
|
|
|
|
/**
|
|
The WaitForStopDet() function waits until I2C STOP Condition occurs,
|
|
indicating transfer completion.
|
|
|
|
@retval EFI_SUCCESS Stop detected.
|
|
@retval EFI_TIMEOUT Timeout while waiting for stop condition.
|
|
@retval EFI_ABORTED Tx abort signaled in HW status register.
|
|
@retval EFI_DEVICE_ERROR Tx or Rx overflow detected.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
WaitForStopDet (
|
|
VOID
|
|
)
|
|
{
|
|
UINTN I2CIoPortBaseAddress;
|
|
UINT32 Addr;
|
|
UINT32 Data;
|
|
UINT32 PollCount;
|
|
EFI_STATUS Status;
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
PollCount = 0;
|
|
|
|
//
|
|
// Get I2C Memory Mapped registers base address.
|
|
//
|
|
I2CIoPortBaseAddress = GetI2CIoPortBaseAddress ();
|
|
|
|
//
|
|
// Wait for STOP Detect.
|
|
//
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_RAW_INTR_STAT;
|
|
|
|
do {
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
if ((Data & I2C_REG_RAW_INTR_STAT_TX_ABRT) != 0) {
|
|
Status = EFI_ABORTED;
|
|
break;
|
|
}
|
|
if ((Data & I2C_REG_RAW_INTR_STAT_TX_OVER) != 0) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
break;
|
|
}
|
|
if ((Data & I2C_REG_RAW_INTR_STAT_RX_OVER) != 0) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
break;
|
|
}
|
|
if ((Data & I2C_REG_RAW_INTR_STAT_STOP_DET) != 0) {
|
|
Status = EFI_SUCCESS;
|
|
break;
|
|
}
|
|
MicroSecondDelay(TI2C_POLL);
|
|
PollCount++;
|
|
if (PollCount >= MAX_STOP_DET_POLL_COUNT) {
|
|
Status = EFI_TIMEOUT;
|
|
break;
|
|
}
|
|
|
|
} while (TRUE);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
|
|
The InitializeInternal() function initialises internal I2C Controller
|
|
register values that are commonly required for I2C Write and Read transfers.
|
|
|
|
@param AddrMode I2C Addressing Mode: 7-bit or 10-bit address.
|
|
|
|
@retval EFI_SUCCESS I2C Operation completed successfully.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
InitializeInternal (
|
|
IN EFI_I2C_ADDR_MODE AddrMode
|
|
)
|
|
{
|
|
UINTN I2CIoPortBaseAddress;
|
|
UINTN Addr;
|
|
UINT32 Data;
|
|
EFI_STATUS Status;
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
//
|
|
// Enable access to I2C Controller MMIO space.
|
|
//
|
|
EnableI2CMmioSpace ();
|
|
|
|
//
|
|
// Disable I2C Controller initially
|
|
//
|
|
DisableI2CController ();
|
|
|
|
//
|
|
// Get I2C Memory Mapped registers base address.
|
|
//
|
|
I2CIoPortBaseAddress = GetI2CIoPortBaseAddress ();
|
|
|
|
//
|
|
// Clear START_DET
|
|
//
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_CLR_START_DET;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
Data &= ~B_I2C_REG_CLR_START_DET;
|
|
*((volatile UINT32 *) (UINTN)(Addr)) = Data;
|
|
|
|
//
|
|
// Clear STOP_DET
|
|
//
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_CLR_STOP_DET;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
Data &= ~B_I2C_REG_CLR_STOP_DET;
|
|
*((volatile UINT32 *) (UINTN)(Addr)) = Data;
|
|
|
|
//
|
|
// Set addressing mode to user defined (7 or 10 bit) and
|
|
// speed mode to that defined by PCD (standard mode default).
|
|
//
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_CON;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
// Set Addressing Mode
|
|
if (AddrMode == EfiI2CSevenBitAddrMode) {
|
|
Data &= ~B_I2C_REG_CON_10BITADD_MASTER;
|
|
} else {
|
|
Data |= B_I2C_REG_CON_10BITADD_MASTER;
|
|
}
|
|
// Set Speed Mode
|
|
Data &= ~B_I2C_REG_CON_SPEED;
|
|
if (FeaturePcdGet (PcdI2CFastModeEnabled)) {
|
|
Data |= BIT2;
|
|
} else {
|
|
Data |= BIT1;
|
|
}
|
|
*((volatile UINT32 *) (UINTN)(Addr)) = Data;
|
|
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
The WriteByte() function provides a standard way to execute a
|
|
standard single byte write to an IC2 device (without accessing
|
|
sub-addresses), as defined in the I2C Specification.
|
|
|
|
@param I2CAddress I2C Slave device address
|
|
@param Value The 8-bit value to write.
|
|
|
|
@retval EFI_SUCCESS Transfer success.
|
|
@retval EFI_UNSUPPORTED Unsupported input param.
|
|
@retval EFI_TIMEOUT Timeout while waiting xfer.
|
|
@retval EFI_ABORTED Controller aborted xfer.
|
|
@retval EFI_DEVICE_ERROR Device error detected by controller.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
WriteByte (
|
|
IN UINTN I2CAddress,
|
|
IN UINT8 Value
|
|
)
|
|
{
|
|
UINTN I2CIoPortBaseAddress;
|
|
UINTN Addr;
|
|
UINT32 Data;
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// Get I2C Memory Mapped registers base address
|
|
//
|
|
I2CIoPortBaseAddress = GetI2CIoPortBaseAddress ();
|
|
|
|
//
|
|
// Write to the IC_TAR register the address of the slave device to be addressed
|
|
//
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_TAR;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
Data &= ~B_I2C_REG_TAR;
|
|
Data |= I2CAddress;
|
|
*((volatile UINT32 *) (UINTN)(Addr)) = Data;
|
|
|
|
//
|
|
// Enable the I2C Controller
|
|
//
|
|
EnableI2CController ();
|
|
|
|
//
|
|
// Write the data and transfer direction to the IC_DATA_CMD register.
|
|
// Also specify that transfer should be terminated by STOP condition.
|
|
//
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_DATA_CMD;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
Data &= 0xFFFFFF00;
|
|
Data |= (UINT8)Value;
|
|
Data &= ~B_I2C_REG_DATA_CMD_RW;
|
|
Data |= B_I2C_REG_DATA_CMD_STOP;
|
|
*((volatile UINT32 *) (UINTN)(Addr)) = Data;
|
|
|
|
//
|
|
// Wait for transfer completion.
|
|
//
|
|
Status = WaitForStopDet ();
|
|
|
|
//
|
|
// Ensure I2C Controller disabled.
|
|
//
|
|
DisableI2CController();
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
|
|
The ReadByte() function provides a standard way to execute a
|
|
standard single byte read to an IC2 device (without accessing
|
|
sub-addresses), as defined in the I2C Specification.
|
|
|
|
@param I2CAddress I2C Slave device address
|
|
@param ReturnDataPtr Pointer to location to receive read byte.
|
|
|
|
@retval EFI_SUCCESS Transfer success.
|
|
@retval EFI_UNSUPPORTED Unsupported input param.
|
|
@retval EFI_TIMEOUT Timeout while waiting xfer.
|
|
@retval EFI_ABORTED Controller aborted xfer.
|
|
@retval EFI_DEVICE_ERROR Device error detected by controller.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ReadByte (
|
|
IN UINTN I2CAddress,
|
|
OUT UINT8 *ReturnDataPtr
|
|
)
|
|
{
|
|
UINTN I2CIoPortBaseAddress;
|
|
UINTN Addr;
|
|
UINT32 Data;
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// Get I2C Memory Mapped registers base address.
|
|
//
|
|
I2CIoPortBaseAddress = GetI2CIoPortBaseAddress ();
|
|
|
|
//
|
|
// Write to the IC_TAR register the address of the slave device to be addressed
|
|
//
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_TAR;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
Data &= ~B_I2C_REG_TAR;
|
|
Data |= I2CAddress;
|
|
*((volatile UINT32 *) (UINTN)(Addr)) = Data;
|
|
|
|
//
|
|
// Enable the I2C Controller
|
|
//
|
|
EnableI2CController ();
|
|
|
|
//
|
|
// Write transfer direction to the IC_DATA_CMD register and
|
|
// specify that transfer should be terminated by STOP condition.
|
|
//
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_DATA_CMD;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
Data &= 0xFFFFFF00;
|
|
Data |= B_I2C_REG_DATA_CMD_RW;
|
|
Data |= B_I2C_REG_DATA_CMD_STOP;
|
|
*((volatile UINT32 *) (UINTN)(Addr)) = Data;
|
|
|
|
//
|
|
// Wait for transfer completion
|
|
//
|
|
Status = WaitForStopDet ();
|
|
if (!EFI_ERROR(Status)) {
|
|
|
|
//
|
|
// Clear RX underflow before reading IC_DATA_CMD.
|
|
//
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_CLR_RX_UNDER;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
|
|
//
|
|
// Obtain and return read data byte from RX buffer (IC_DATA_CMD[7:0]).
|
|
//
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_DATA_CMD;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
Data &= 0x000000FF;
|
|
*ReturnDataPtr = (UINT8) Data;
|
|
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_RAW_INTR_STAT;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
Data &= I2C_REG_RAW_INTR_STAT_RX_UNDER;
|
|
if (Data != 0) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Ensure I2C Controller disabled.
|
|
//
|
|
DisableI2CController ();
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
|
|
The WriteMultipleByte() function provides a standard way to execute
|
|
multiple byte writes to an IC2 device (e.g. when accessing sub-addresses or
|
|
when writing block of data), as defined in the I2C Specification.
|
|
|
|
@param I2CAddress The I2C slave address of the device
|
|
with which to communicate.
|
|
|
|
@param WriteBuffer Contains the value of byte to be written to the
|
|
I2C slave device.
|
|
|
|
@param Length No. of bytes to be written.
|
|
|
|
@retval EFI_SUCCESS Transfer success.
|
|
@retval EFI_UNSUPPORTED Unsupported input param.
|
|
@retval EFI_TIMEOUT Timeout while waiting xfer.
|
|
@retval EFI_ABORTED Tx abort signaled in HW status register.
|
|
@retval EFI_DEVICE_ERROR Tx overflow detected.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
WriteMultipleByte (
|
|
IN UINTN I2CAddress,
|
|
IN UINT8 *WriteBuffer,
|
|
IN UINTN Length
|
|
)
|
|
{
|
|
UINTN I2CIoPortBaseAddress;
|
|
UINTN Index;
|
|
UINTN Addr;
|
|
UINT32 Data;
|
|
EFI_STATUS Status;
|
|
|
|
if (Length > I2C_FIFO_SIZE) {
|
|
return EFI_UNSUPPORTED; // Routine does not handle xfers > fifo size.
|
|
}
|
|
|
|
I2CIoPortBaseAddress = GetI2CIoPortBaseAddress ();
|
|
|
|
//
|
|
// Write to the IC_TAR register the address of the slave device to be addressed
|
|
//
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_TAR;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
Data &= ~B_I2C_REG_TAR;
|
|
Data |= I2CAddress;
|
|
*((volatile UINT32 *) (UINTN)(Addr)) = Data;
|
|
|
|
//
|
|
// Enable the I2C Controller
|
|
//
|
|
EnableI2CController ();
|
|
|
|
//
|
|
// Write the data and transfer direction to the IC_DATA_CMD register.
|
|
// Also specify that transfer should be terminated by STOP condition.
|
|
//
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_DATA_CMD;
|
|
for (Index = 0; Index < Length; Index++) {
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
Data &= 0xFFFFFF00;
|
|
Data |= (UINT8)WriteBuffer[Index];
|
|
Data &= ~B_I2C_REG_DATA_CMD_RW;
|
|
if (Index == (Length-1)) {
|
|
Data |= B_I2C_REG_DATA_CMD_STOP;
|
|
}
|
|
*((volatile UINT32 *) (UINTN)(Addr)) = Data;
|
|
}
|
|
|
|
//
|
|
// Wait for transfer completion
|
|
//
|
|
Status = WaitForStopDet ();
|
|
|
|
//
|
|
// Ensure I2C Controller disabled.
|
|
//
|
|
DisableI2CController ();
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
|
|
The ReadMultipleByte() function provides a standard way to execute
|
|
multiple byte writes to an IC2 device (e.g. when accessing sub-addresses or
|
|
when reading block of data), as defined in the I2C Specification (I2C combined
|
|
write/read protocol).
|
|
|
|
@param I2CAddress The I2C slave address of the device
|
|
with which to communicate.
|
|
|
|
@param Buffer Contains the value of byte data written or read from the
|
|
I2C slave device.
|
|
|
|
@param WriteLength No. of bytes to be written. In this case data
|
|
written typically contains sub-address or sub-addresses
|
|
in Hi-Lo format, that need to be read (I2C combined
|
|
write/read protocol).
|
|
|
|
@param ReadLength No. of bytes to be read from I2C slave device.
|
|
|
|
@retval EFI_SUCCESS Transfer success.
|
|
@retval EFI_UNSUPPORTED Unsupported input param.
|
|
@retval EFI_TIMEOUT Timeout while waiting xfer.
|
|
@retval EFI_ABORTED Tx abort signaled in HW status register.
|
|
@retval EFI_DEVICE_ERROR Rx underflow or Rx/Tx overflow detected.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ReadMultipleByte (
|
|
IN UINTN I2CAddress,
|
|
IN OUT UINT8 *Buffer,
|
|
IN UINTN WriteLength,
|
|
IN UINTN ReadLength
|
|
)
|
|
{
|
|
UINTN I2CIoPortBaseAddress;
|
|
UINTN Index;
|
|
UINTN Addr;
|
|
UINT32 Data;
|
|
UINT8 PollCount;
|
|
EFI_STATUS Status;
|
|
|
|
if (WriteLength > I2C_FIFO_SIZE || ReadLength > I2C_FIFO_SIZE) {
|
|
return EFI_UNSUPPORTED; // Routine does not handle xfers > fifo size.
|
|
}
|
|
|
|
I2CIoPortBaseAddress = GetI2CIoPortBaseAddress ();
|
|
|
|
//
|
|
// Write to the IC_TAR register the address of the slave device to be addressed
|
|
//
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_TAR;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
Data &= ~B_I2C_REG_TAR;
|
|
Data |= I2CAddress;
|
|
*((volatile UINT32 *) (UINTN)(Addr)) = Data;
|
|
|
|
//
|
|
// Enable the I2C Controller
|
|
//
|
|
EnableI2CController ();
|
|
|
|
//
|
|
// Write the data (sub-addresses) to the IC_DATA_CMD register.
|
|
//
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_DATA_CMD;
|
|
for (Index = 0; Index < WriteLength; Index++) {
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
Data &= 0xFFFFFF00;
|
|
Data |= (UINT8)Buffer[Index];
|
|
Data &= ~B_I2C_REG_DATA_CMD_RW;
|
|
*((volatile UINT32 *) (UINTN)(Addr)) = Data;
|
|
}
|
|
|
|
//
|
|
// Issue Read Transfers for each byte (Restart issued when write/read bit changed).
|
|
//
|
|
for (Index = 0; Index < ReadLength; Index++) {
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
Data |= B_I2C_REG_DATA_CMD_RW;
|
|
// Issue a STOP for last read transfer.
|
|
if (Index == (ReadLength-1)) {
|
|
Data |= B_I2C_REG_DATA_CMD_STOP;
|
|
}
|
|
*((volatile UINT32 *) (UINTN)(Addr)) = Data;
|
|
}
|
|
|
|
//
|
|
// Wait for STOP condition.
|
|
//
|
|
Status = WaitForStopDet ();
|
|
if (!EFI_ERROR(Status)) {
|
|
|
|
//
|
|
// Poll Receive FIFO Buffer Level register until valid (upto MAX_T_POLL_COUNT times).
|
|
//
|
|
Data = 0;
|
|
PollCount = 0;
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_RXFLR;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
while ((Data != ReadLength) && (PollCount < MAX_T_POLL_COUNT)) {
|
|
MicroSecondDelay(TI2C_POLL);
|
|
PollCount++;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
}
|
|
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_RAW_INTR_STAT;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
|
|
//
|
|
// If no timeout or device error then read rx data.
|
|
//
|
|
if (PollCount == MAX_T_POLL_COUNT) {
|
|
Status = EFI_TIMEOUT;
|
|
} else if ((Data & I2C_REG_RAW_INTR_STAT_RX_OVER) != 0) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
} else {
|
|
|
|
//
|
|
// Clear RX underflow before reading IC_DATA_CMD.
|
|
//
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_CLR_RX_UNDER;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
|
|
//
|
|
// Read data.
|
|
//
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_DATA_CMD;
|
|
for (Index = 0; Index < ReadLength; Index++) {
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
Data &= 0x000000FF;
|
|
*(Buffer+Index) = (UINT8)Data;
|
|
}
|
|
Addr = I2CIoPortBaseAddress + I2C_REG_RAW_INTR_STAT;
|
|
Data = *((volatile UINT32 *) (UINTN)(Addr));
|
|
Data &= I2C_REG_RAW_INTR_STAT_RX_UNDER;
|
|
if (Data != 0) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
} else {
|
|
Status = EFI_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Ensure I2C Controller disabled.
|
|
//
|
|
DisableI2CController ();
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
|
|
The I2cWriteByte() function is a wrapper function for the WriteByte function.
|
|
Provides a standard way to execute a standard single byte write to an IC2 device
|
|
(without accessing sub-addresses), as defined in the I2C Specification.
|
|
|
|
@param SlaveAddress The I2C slave address of the device
|
|
with which to communicate.
|
|
|
|
@param AddrMode I2C Addressing Mode: 7-bit or 10-bit address.
|
|
|
|
@param Buffer Contains the value of byte data to execute to the
|
|
I2C slave device.
|
|
|
|
|
|
@retval EFI_SUCCESS Transfer success.
|
|
@retval EFI_INVALID_PARAMETER This or Buffer pointers are invalid.
|
|
@retval EFI_UNSUPPORTED Unsupported input param.
|
|
@retval EFI_TIMEOUT Timeout while waiting xfer.
|
|
@retval EFI_ABORTED Controller aborted xfer.
|
|
@retval EFI_DEVICE_ERROR Device error detected by controller.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
I2cWriteByte (
|
|
IN EFI_I2C_DEVICE_ADDRESS SlaveAddress,
|
|
IN EFI_I2C_ADDR_MODE AddrMode,
|
|
IN OUT VOID *Buffer
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN I2CAddress;
|
|
UINT16 SaveCmd;
|
|
UINT32 SaveBar0;
|
|
|
|
if (Buffer == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
SaveCmd = 0;
|
|
SaveBar0 = 0;
|
|
|
|
I2cCommonServiceEntry (&SaveCmd, &SaveBar0);
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
I2CAddress = SlaveAddress.I2CDeviceAddress;
|
|
Status = InitializeInternal (AddrMode);
|
|
if (!EFI_ERROR(Status)) {
|
|
Status = WriteByte (I2CAddress, *(UINT8 *) Buffer);
|
|
}
|
|
|
|
I2cCommonServiceExit (SaveCmd, SaveBar0);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
|
|
The I2cReadByte() function is a wrapper function for the ReadByte function.
|
|
Provides a standard way to execute a standard single byte read to an I2C device
|
|
(without accessing sub-addresses), as defined in the I2C Specification.
|
|
|
|
@param SlaveAddress The I2C slave address of the device
|
|
with which to communicate.
|
|
|
|
@param AddrMode I2C Addressing Mode: 7-bit or 10-bit address.
|
|
|
|
@param Buffer Contains the value of byte data read from the
|
|
I2C slave device.
|
|
|
|
|
|
@retval EFI_SUCCESS Transfer success.
|
|
@retval EFI_INVALID_PARAMETER This or Buffer pointers are invalid.
|
|
@retval EFI_TIMEOUT Timeout while waiting xfer.
|
|
@retval EFI_ABORTED Controller aborted xfer.
|
|
@retval EFI_DEVICE_ERROR Device error detected by controller.
|
|
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
I2cReadByte (
|
|
IN EFI_I2C_DEVICE_ADDRESS SlaveAddress,
|
|
IN EFI_I2C_ADDR_MODE AddrMode,
|
|
IN OUT VOID *Buffer
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN I2CAddress;
|
|
UINT16 SaveCmd;
|
|
UINT32 SaveBar0;
|
|
|
|
if (Buffer == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
SaveCmd = 0;
|
|
SaveBar0 =0;
|
|
|
|
I2cCommonServiceEntry (&SaveCmd, &SaveBar0);
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
I2CAddress = SlaveAddress.I2CDeviceAddress;
|
|
|
|
Status = InitializeInternal (AddrMode);
|
|
if (!EFI_ERROR(Status)) {
|
|
Status = ReadByte (I2CAddress, (UINT8 *) Buffer);
|
|
}
|
|
I2cCommonServiceExit (SaveCmd, SaveBar0);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
|
|
The I2cWriteMultipleByte() function is a wrapper function for the
|
|
WriteMultipleByte() function. Provides a standard way to execute multiple
|
|
byte writes to an I2C device (e.g. when accessing sub-addresses or writing
|
|
block of data), as defined in the I2C Specification.
|
|
|
|
@param SlaveAddress The I2C slave address of the device
|
|
with which to communicate.
|
|
|
|
@param AddrMode I2C Addressing Mode: 7-bit or 10-bit address.
|
|
|
|
@param Length No. of bytes to be written.
|
|
|
|
@param Buffer Contains the value of byte to be written to the
|
|
I2C slave device.
|
|
|
|
@retval EFI_SUCCESS Transfer success.
|
|
@retval EFI_INVALID_PARAMETER This, Length or Buffer pointers are invalid.
|
|
@retval EFI_UNSUPPORTED Unsupported input param.
|
|
@retval EFI_TIMEOUT Timeout while waiting xfer.
|
|
@retval EFI_ABORTED Controller aborted xfer.
|
|
@retval EFI_DEVICE_ERROR Device error detected by controller.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
I2cWriteMultipleByte (
|
|
IN EFI_I2C_DEVICE_ADDRESS SlaveAddress,
|
|
IN EFI_I2C_ADDR_MODE AddrMode,
|
|
IN UINTN *Length,
|
|
IN OUT VOID *Buffer
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN I2CAddress;
|
|
UINT16 SaveCmd;
|
|
UINT32 SaveBar0;
|
|
|
|
if (Buffer == NULL || Length == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
SaveCmd = 0;
|
|
SaveBar0 =0;
|
|
|
|
I2cCommonServiceEntry (&SaveCmd, &SaveBar0);
|
|
Status = EFI_SUCCESS;
|
|
|
|
I2CAddress = SlaveAddress.I2CDeviceAddress;
|
|
|
|
Status = InitializeInternal (AddrMode);
|
|
if (!EFI_ERROR(Status)) {
|
|
Status = WriteMultipleByte (I2CAddress, Buffer, (*Length));
|
|
}
|
|
|
|
I2cCommonServiceExit (SaveCmd, SaveBar0);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
|
|
The I2cReadMultipleByte() function is a wrapper function for the ReadMultipleByte() function.
|
|
Provides a standard way to execute multiple byte writes to an I2C device
|
|
(e.g. when accessing sub-addresses or when reading block of data), as defined
|
|
in the I2C Specification (I2C combined write/read protocol).
|
|
|
|
@param SlaveAddress The I2C slave address of the device
|
|
with which to communicate.
|
|
|
|
@param AddrMode I2C Addressing Mode: 7-bit or 10-bit address.
|
|
|
|
@param WriteLength No. of bytes to be written. In this case data
|
|
written typically contains sub-address or sub-addresses
|
|
in Hi-Lo format, that need to be read (I2C combined
|
|
write/read protocol).
|
|
|
|
@param ReadLength No. of bytes to be read from I2C slave device.
|
|
|
|
@param Buffer Contains the value of byte data read from the
|
|
I2C slave device.
|
|
|
|
@retval EFI_SUCCESS Transfer success.
|
|
@retval EFI_INVALID_PARAMETER This, WriteLength, ReadLength or Buffer
|
|
pointers are invalid.
|
|
@retval EFI_UNSUPPORTED Unsupported input param.
|
|
@retval EFI_TIMEOUT Timeout while waiting xfer.
|
|
@retval EFI_ABORTED Controller aborted xfer.
|
|
@retval EFI_DEVICE_ERROR Device error detected by controller.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
I2cReadMultipleByte (
|
|
IN EFI_I2C_DEVICE_ADDRESS SlaveAddress,
|
|
IN EFI_I2C_ADDR_MODE AddrMode,
|
|
IN UINTN *WriteLength,
|
|
IN UINTN *ReadLength,
|
|
IN OUT VOID *Buffer
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN I2CAddress;
|
|
UINT16 SaveCmd;
|
|
UINT32 SaveBar0;
|
|
|
|
if (Buffer == NULL || WriteLength == NULL || ReadLength == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
SaveCmd = 0;
|
|
SaveBar0 =0;
|
|
|
|
I2cCommonServiceEntry (&SaveCmd, &SaveBar0);
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
I2CAddress = SlaveAddress.I2CDeviceAddress;
|
|
Status = InitializeInternal (AddrMode);
|
|
if (!EFI_ERROR(Status)) {
|
|
Status = ReadMultipleByte (I2CAddress, Buffer, (*WriteLength), (*ReadLength));
|
|
}
|
|
I2cCommonServiceExit (SaveCmd, SaveBar0);
|
|
return Status;
|
|
}
|
|
|
|
|