/** @file I2C Bus implementation upon CirrusLogic. Copyright (c) 2008 - 2009, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "CirrusLogic5430.h" #include "CirrusLogic5430I2c.h" #define SEQ_ADDRESS_REGISTER 0x3c4 #define SEQ_DATA_REGISTER 0x3c5 #define I2C_CONTROL 0x08 #define I2CDAT_IN 7 #define I2CCLK_IN 2 #define I2CDAT_OUT 1 #define I2CCLK_OUT 0 #define I2C_BUS_SPEED 100 //100kbps /** PCI I/O byte write function. @param PciIo The pointer to PCI_IO_PROTOCOL. @param Address The bit map of I2C Data or I2C Clock pins. @param Data The date to write. **/ VOID I2cOutb ( EFI_PCI_IO_PROTOCOL *PciIo, UINTN Address, UINT8 Data ) { PciIo->Io.Write ( PciIo, EfiPciIoWidthUint8, EFI_PCI_IO_PASS_THROUGH_BAR, Address, 1, &Data ); } /** PCI I/O byte read function. @param PciIo The pointer to PCI_IO_PROTOCOL. @param Address The bit map of I2C Data or I2C Clock pins. return byte value read from PCI I/O space. **/ UINT8 I2cInb ( EFI_PCI_IO_PROTOCOL *PciIo, UINTN Address ) { UINT8 Data; PciIo->Io.Read ( PciIo, EfiPciIoWidthUint8, EFI_PCI_IO_PASS_THROUGH_BAR, Address, 1, &Data ); return Data; } /** Read status of I2C Data and I2C Clock Pins. @param PciIo The pointer to PCI_IO_PROTOCOL. @param Blt The bit map of I2C Data or I2C Clock pins. @retval 0 Low on I2C Data or I2C Clock Pin. @retval 1 High on I2C Data or I2C Clock Pin. **/ UINT8 I2cPinRead ( EFI_PCI_IO_PROTOCOL *PciIo, UINT8 Bit ) { I2cOutb (PciIo, SEQ_ADDRESS_REGISTER, I2C_CONTROL); return (UINT8) ((I2cInb (PciIo, SEQ_DATA_REGISTER) >> Bit ) & 0xfe); } /** Set/Clear I2C Data and I2C Clock Pins. @param PciIo The pointer to PCI_IO_PROTOCOL. @param Blt The bit map to controller I2C Data or I2C Clock pins. @param Value 1 or 0 stands for Set or Clear I2C Data and I2C Clock Pins. **/ VOID I2cPinWrite ( EFI_PCI_IO_PROTOCOL *PciIo, UINT8 Bit, UINT8 Value ) { UINT8 Byte; I2cOutb (PciIo, SEQ_ADDRESS_REGISTER, I2C_CONTROL); Byte = (UINT8) (I2cInb (PciIo, SEQ_DATA_REGISTER) & (UINT8) ~(1 << Bit)) ; Byte = (UINT8) (Byte | ((Value & 0x01) << Bit)); I2cOutb (PciIo, SEQ_DATA_REGISTER, (UINT8) (Byte | 0x40)); return; } /** Read/write delay acoording to I2C Bus Speed. **/ VOID I2cDelay ( VOID ) { MicroSecondDelay (1000 / I2C_BUS_SPEED); } /** Write a 8-bit data onto I2C Data Pin. @param PciIo The pointer to PCI_IO_PROTOCOL. @param Data The byte data to write. **/ VOID I2cSendByte ( EFI_PCI_IO_PROTOCOL *PciIo, UINT8 Data ) { UINTN Index; // // Send byte data onto I2C Bus // for (Index = 0; Index < 8; Index --) { I2cPinWrite (PciIo, I2CDAT_OUT, (UINT8) (Data >> (7 - Index))); I2cPinWrite (PciIo, I2CCLK_OUT, 1); I2cDelay (); I2cPinWrite (PciIo, I2CCLK_OUT, 0); } } /** Read a 8-bit data from I2C Data Pin. @param PciIo The pointer to PCI_IO_PROTOCOL. Return the byte data read from I2C Data Pin. **/ UINT8 I2cReceiveByte ( EFI_PCI_IO_PROTOCOL *PciIo ) { UINT8 Data; UINTN Index; Data = 0; // // Read byte data from I2C Bus // for (Index = 0; Index < 8; Index --) { I2cPinWrite (PciIo, I2CCLK_OUT, 1); I2cDelay (); Data = (UINT8) (Data << 1); Data = (UINT8) (Data | I2cPinRead (PciIo, I2CDAT_IN)); I2cPinWrite (PciIo, I2CCLK_OUT, 0); } return Data; } /** Receive an ACK signal from I2C Bus. @param PciIo The pointer to PCI_IO_PROTOCOL. **/ BOOLEAN I2cWaitAck ( EFI_PCI_IO_PROTOCOL *PciIo ) { // // Wait for ACK signal // I2cPinWrite (PciIo, I2CDAT_OUT, 1); I2cPinWrite (PciIo, I2CCLK_OUT, 1); I2cDelay (); if (I2cPinRead (PciIo, I2CDAT_IN) == 0) { I2cPinWrite (PciIo, I2CDAT_OUT, 1); return TRUE; } else { return FALSE; } } /** Send an ACK signal onto I2C Bus. @param PciIo The pointer to PCI_IO_PROTOCOL. **/ VOID I2cSendAck ( EFI_PCI_IO_PROTOCOL *PciIo ) { I2cPinWrite (PciIo, I2CCLK_OUT, 1); I2cPinWrite (PciIo, I2CDAT_OUT, 1); I2cPinWrite (PciIo, I2CDAT_OUT, 0); I2cPinWrite (PciIo, I2CCLK_OUT, 0); } /** Start a I2C transfer on I2C Bus. @param PciIo The pointer to PCI_IO_PROTOCOL. **/ VOID I2cStart ( EFI_PCI_IO_PROTOCOL *PciIo ) { // // Init CLK and DAT pins // I2cPinWrite (PciIo, I2CCLK_OUT, 1); I2cPinWrite (PciIo, I2CDAT_OUT, 1); // // Start a I2C transfer, set SDA low from high, when SCL is high // I2cPinWrite (PciIo, I2CDAT_OUT, 0); I2cPinWrite (PciIo, I2CCLK_OUT, 0); } /** Stop a I2C transfer on I2C Bus. @param PciIo The pointer to PCI_IO_PROTOCOL. **/ VOID I2cStop ( EFI_PCI_IO_PROTOCOL *PciIo ) { // // Stop a I2C transfer, set SDA high from low, when SCL is high // I2cPinWrite (PciIo, I2CDAT_OUT, 0); I2cPinWrite (PciIo, I2CCLK_OUT, 1); I2cPinWrite (PciIo, I2CDAT_OUT, 1); } /** Read one byte data on I2C Bus. Read one byte data from the slave device connectet to I2C Bus. If Data is NULL, then ASSERT(). @param PciIo The pointer to PCI_IO_PROTOCOL. @param DeviceAddress Slave device's address. @param RegisterAddress The register address on slave device. @param Data The pointer to returned data if EFI_SUCCESS returned. @retval EFI_DEVICE_ERROR @retval EFI_SUCCESS **/ EFI_STATUS EFIAPI I2cReadByte ( EFI_PCI_IO_PROTOCOL *PciIo, UINT8 DeviceAddress, UINT8 RegisterAddress, UINT8 *Data ) { ASSERT (Data != NULL); // // Start I2C transfer // I2cStart (PciIo); // // Send slave address with enabling write flag // I2cSendByte (PciIo, (UINT8) (DeviceAddress & 0xfe)); // // Wait for ACK signal // if (I2cWaitAck (PciIo) == FALSE) { return EFI_DEVICE_ERROR; } // // Send register address // I2cSendByte (PciIo, RegisterAddress); // // Wait for ACK signal // if (I2cWaitAck (PciIo) == FALSE) { return EFI_DEVICE_ERROR; } // // Send slave address with enabling read flag // I2cSendByte (PciIo, (UINT8) (DeviceAddress | 0x01)); // // Wait for ACK signal // if (I2cWaitAck (PciIo) == FALSE) { return EFI_DEVICE_ERROR; } // // Read byte data from I2C Bus // *Data = I2cReceiveByte (PciIo); // // Send ACK signal onto I2C Bus // I2cSendAck (PciIo); // // Stop a I2C transfer // I2cStop (PciIo); return EFI_SUCCESS; } /** Write one byte data onto I2C Bus. Write one byte data to the slave device connectet to I2C Bus. If Data is NULL, then ASSERT(). @param PciIo The pointer to PCI_IO_PROTOCOL. @param DeviceAddress Slave device's address. @param RegisterAddress The register address on slave device. @param Data The pointer to write data. @retval EFI_DEVICE_ERROR @retval EFI_SUCCESS **/ EFI_STATUS EFIAPI I2cWriteByte ( EFI_PCI_IO_PROTOCOL *PciIo, UINT8 DeviceAddress, UINT8 RegisterAddress, UINT8 *Data ) { ASSERT (Data != NULL); I2cStart (PciIo); // // Send slave address with enabling write flag // I2cSendByte (PciIo, (UINT8) (DeviceAddress & 0xfe)); // // Wait for ACK signal // if (I2cWaitAck (PciIo) == FALSE) { return EFI_DEVICE_ERROR; } // // Send register address // I2cSendByte (PciIo, RegisterAddress); // // Wait for ACK signal // if (I2cWaitAck (PciIo) == FALSE) { return EFI_DEVICE_ERROR; } // // Send byte data onto I2C Bus // I2cSendByte (PciIo, *Data); // // Wait for ACK signal // if (I2cWaitAck (PciIo) == FALSE) { return EFI_DEVICE_ERROR; } // // Stop a I2C transfer // I2cStop (PciIo); return EFI_SUCCESS; }