/*--------------------------------------------------------------------
 Copyright(c) 2015 Intel Corporation. All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:

 * Redistributions of source code must retain the above copyright
 notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 notice, this list of conditions and the following disclaimer in
 the documentation and/or other materials provided with the
 distribution.
 * Neither the name of Intel Corporation nor the names of its
 contributors may be used to endorse or promote products derived
 from this software without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 --------------------------------------------------------------------*/

/*-----------------------------------------------------------------------
 * Any required includes
 *------------------------------------------------------------------------
 */
#include "GPIO_I2C.h"

/*-----------------------------------------------------------------------
 * Any required local definitions
 *------------------------------------------------------------------------
 */
#ifndef NULL
	#define NULL (void *)0
#endif
/*-----------------------------------------------------------------------
 * Function prototypes
 *------------------------------------------------------------------------
 */
static void vGalileoRouteLEDPins(void);

/*-----------------------------------------------------------------------
 * Static variables
 *------------------------------------------------------------------------
 */
static struct BOARD_GPIO_CONTROLLER_CONFIG GpioConfig;
static struct BOARD_LEGACY_GPIO_CONFIG LegacyGpioConfig;

static uint32_t LegacyGpioBase = 0;
static uint32_t IohGpioBase = 0;
static uint32_t I2CGpioBase = 0;

static uint32_t bGalileoGPIOInitialized = FALSE;

/*-----------------------------------------------------------------------
 * GPIO support functions
 *------------------------------------------------------------------------
 */
 static uint32_t pciIOread32(uint32_t addr)
 {
	  outl(IO_PCI_ADDRESS_PORT, addr);
	  uint32_t data = inl(IO_PCI_DATA_PORT);
 	  return data;
 }
 /*-----------------------------------------------------------*/

 static void pciIOwrite32(uint32_t addr, uint32_t IO_data)
 {
	 outl(IO_PCI_ADDRESS_PORT, addr);
	 outl(IO_PCI_DATA_PORT, IO_data );
 }
 /*-----------------------------------------------------------*/

 static int32_t uiGalileoGPIORead(uint32_t Offset, uint8_t UseMask)
 {
	 // Keep reserved bits [31:8]
	 if (UseMask)
		 return *((volatile uint32_t *) (uintn_t)(IohGpioBase + Offset)) & 0xFFFFFF00;
	 else
		 return *((volatile uint32_t *) (uintn_t)(IohGpioBase + Offset));
 }
 /*-----------------------------------------------------------*/

 static void vGalileoGPIOWrite(uint32_t Offset, uint32_t WriteData32)
 {
	 uint32_t Data32 = uiGalileoGPIORead(Offset, true);
     if (Offset !=  GPIO_INTSTATUS)
    	 Data32 |= (WriteData32 & 0x000FFFFF);
     *((volatile uint32_t *) (uintn_t)(IohGpioBase + Offset)) = Data32;
 }
 /*-----------------------------------------------------------*/

 static int32_t uiGalileoLegacyGPIOPCIRead(uint32_t addr, uint32_t Mask)
 {
	 // Keep reserved bits (Mask Varies)
	 return pciIOread32(addr) & Mask;
 }
 /*-----------------------------------------------------------*/

 static void vGalileoLegacyGPIOPCIWrite(uint32_t addr, uint32_t WriteData32, uint32_t Mask)
 {
	 uint32_t Data32 = uiGalileoLegacyGPIOPCIRead(addr, Mask);
     Data32 |= (WriteData32 & ~Mask);
     pciIOwrite32(addr, Data32);
 }
 /*-----------------------------------------------------------*/

 static int32_t uiGalileoLegacyGPIOPortRead(uint32_t addr, uint32_t Mask)
 {
	 // Keep reserved bits (Mask Varies)
	 return inl(addr) & Mask;
 }
 /*-----------------------------------------------------------*/

 static void vGalileoLegacyGPIOPortRMW(uint32_t addr, uint32_t WriteData32, uint32_t Mask)
 {
	 uint32_t Data32 = uiGalileoLegacyGPIOPortRead(addr, Mask);
     Data32 |= (WriteData32 & ~Mask);
     outl(addr, Data32);
 }
 /*-----------------------------------------------------------*/

 /*-----------------------------------------------------------------------
  * Controller initialization functions
  *------------------------------------------------------------------------
  */
 void vGalileoInitializeLegacyGPIO(void)
 {
	 // Read Register Default Values into Structure
	 struct BOARD_LEGACY_GPIO_CONFIG LegacyGPIOConfigTable[] =
	 { PLATFORM_LEGACY_GPIO_CONFIG_DEFINITION };

	 // BDF for Legacy GPIO (from the Quark Datasheet)
	 uint8_t Bus = LEGACY_GPIO_BUS_NUMBER;
	 uint8_t Device = LEGACY_GPIO_DEVICE_NUMBER;
	 uint8_t Func = LEGACY_GPIO_FUNCTION_NUMBER;

	 // Get PCI Configuration IO Address
	 LegacyGpioBase =
	 uiGalileoLegacyGPIOPCIRead(IO_PCI_ADDRESS(Bus, Device, Func, R_QNC_LPC_GBA_BASE), B_QNC_LPC_GPA_BASE_MASK);

	 // Quiet compiler by doing a legacy GPIO write
	 uint32_t PciCmd = uiGalileoLegacyGPIOPCIRead((LegacyGpioBase + PCI_REG_PCICMD), 0xFFFFFFFF);
	 vGalileoLegacyGPIOPCIWrite((LegacyGpioBase + PCI_REG_PCICMD), (PciCmd | 0x7), 0xFFFFFFFF);

	 // Setup Structure
	 LegacyGpioConfig = LegacyGPIOConfigTable[0];

	 // Update values
	 vGalileoLegacyGPIOPortRMW(LegacyGpioBase + R_QNC_GPIO_CGEN_CORE_WELL, LegacyGpioConfig.CoreWellEnable, 0xFFFFFFFC);
	 vGalileoLegacyGPIOPortRMW(LegacyGpioBase + R_QNC_GPIO_CGIO_CORE_WELL, LegacyGpioConfig.CoreWellIoSelect, 0xFFFFFFFC);
	 vGalileoLegacyGPIOPortRMW(LegacyGpioBase + R_QNC_GPIO_CGLVL_CORE_WELL, LegacyGpioConfig.CoreWellLvlForInputOrOutput, 0xFFFFFFFC);
	 vGalileoLegacyGPIOPortRMW(LegacyGpioBase + R_QNC_GPIO_CGTPE_CORE_WELL, LegacyGpioConfig.CoreWellTriggerPositiveEdge, 0xFFFFFFFC);
	 vGalileoLegacyGPIOPortRMW(LegacyGpioBase + R_QNC_GPIO_CGTNE_CORE_WELL, LegacyGpioConfig.CoreWellTriggerNegativeEdge, 0xFFFFFFFC);
	 vGalileoLegacyGPIOPortRMW(LegacyGpioBase + R_QNC_GPIO_CGGPE_CORE_WELL, LegacyGpioConfig.ResumeWellGPEEnable, 0xFFFFFFFC);
	 vGalileoLegacyGPIOPortRMW(LegacyGpioBase + R_QNC_GPIO_CGSMI_CORE_WELL, LegacyGpioConfig.ResumeWellSMIEnable, 0xFFFFFFFC);
	 vGalileoLegacyGPIOPortRMW(LegacyGpioBase + R_QNC_GPIO_CGEN_CORE_WELL, LegacyGpioConfig.CoreWellTriggerStatus, 0xFFFFFFFC);
	 vGalileoLegacyGPIOPortRMW(LegacyGpioBase + R_QNC_GPIO_CNMIEN_CORE_WELL, LegacyGpioConfig.ResumeWellNMIEnable, 0xFFFFFFFC);
	 vGalileoLegacyGPIOPortRMW(LegacyGpioBase + R_QNC_GPIO_RGEN_RESUME_WELL, LegacyGpioConfig.ResumeWellEnable, 0xFFFFFFC0);
	 vGalileoLegacyGPIOPortRMW(LegacyGpioBase + R_QNC_GPIO_RGIO_RESUME_WELL, LegacyGpioConfig.ResumeWellIoSelect, 0xFFFFFFC0);
	 vGalileoLegacyGPIOPortRMW(LegacyGpioBase + R_QNC_GPIO_RGLVL_RESUME_WELL, LegacyGpioConfig.ResumeWellLvlForInputOrOutput, 0xFFFFFFC0);
	 vGalileoLegacyGPIOPortRMW(LegacyGpioBase + R_QNC_GPIO_RGTPE_RESUME_WELL, LegacyGpioConfig.ResumeWellTriggerPositiveEdge, 0xFFFFFFC0);
	 vGalileoLegacyGPIOPortRMW(LegacyGpioBase + R_QNC_GPIO_RGTNE_RESUME_WELL, LegacyGpioConfig.ResumeWellTriggerNegativeEdge, 0xFFFFFFC0);
	 vGalileoLegacyGPIOPortRMW(LegacyGpioBase + R_QNC_GPIO_RGGPE_RESUME_WELL, LegacyGpioConfig.CoreWellGPEEnable, 0xFFFFFFC0);
	 vGalileoLegacyGPIOPortRMW(LegacyGpioBase + R_QNC_GPIO_RGSMI_RESUME_WELL, LegacyGpioConfig.CoreWellSMIEnable, 0xFFFFFFC0);
	 vGalileoLegacyGPIOPortRMW(LegacyGpioBase + R_QNC_GPIO_RGTS_RESUME_WELL, LegacyGpioConfig.ResumeWellTriggerStatus, 0xFFFFFFC0);
	 vGalileoLegacyGPIOPortRMW(LegacyGpioBase + R_QNC_GPIO_RNMIEN_RESUME_WELL, LegacyGpioConfig.CoreWellNMIEnable, 0xFFFFFFC0);
 }
 /*-----------------------------------------------------------*/

 void vGalileoInitializeGpioController(void)
 {
	 // Read Register Default Values into Structure
	 struct BOARD_GPIO_CONTROLLER_CONFIG BoardGpioControllerConfigTable[] =
	 { PLATFORM_GPIO_CONTROLLER_CONFIG_DEFINITION };

	 // BDF for I2C Controller (from the Quark Datasheet)
	 uint8_t Bus = IOH_I2C_GPIO_BUS_NUMBER;
	 uint8_t Device = IOH_I2C_GPIO_DEVICE_NUMBER;
	 uint8_t Func = IOH_I2C_GPIO_FUNCTION_NUMBER;

	 // Get PCI Configuration MMIO Address
	 uint32_t gpio_controller_base = MMIO_PCI_ADDRESS(Bus, Device, Func, 0);

	 // Get Vendor and Device IDs
	 uint16_t PciVid = mem_read(gpio_controller_base, PCI_REG_VID, 2);
	 uint16_t PciDid = mem_read(gpio_controller_base, PCI_REG_DID, 2);

	 // Check for valid VID and DID
	 if((PciVid == V_IOH_I2C_GPIO_VENDOR_ID) && (PciDid == V_IOH_I2C_GPIO_DEVICE_ID))
	 {
		 // Read PCICMD
		 uint8_t PciCmd = mem_read(gpio_controller_base, PCI_REG_PCICMD, 1);
		 // Enable Bus Master(Bit2), MMIO Space(Bit1) & I/O Space(Bit0)
		 mem_write(gpio_controller_base, PCI_REG_PCICMD, 1, (PciCmd | 0x7));
		 // Read MEM_BASE
		 IohGpioBase = mem_read(gpio_controller_base, R_IOH_GPIO_MEMBAR, 4);
		 // Setup Structure
		 GpioConfig = BoardGpioControllerConfigTable[0];
		 // IEN- Interrupt Enable Register
		 vGalileoGPIOWrite(GPIO_INTEN, GpioConfig.IntEn);
		 // ISTATUS- Interrupt Status Register
		 vGalileoGPIOWrite(GPIO_INTSTATUS, 0);
		 // GPIO SWPORTA Data Register - GPIO_SWPORTA_DR
		 vGalileoGPIOWrite(GPIO_SWPORTA_DR, GpioConfig.PortADR);
		 // GPIO SWPORTA Data Direction Register - GPIO_SWPORTA_DDR
		 vGalileoGPIOWrite(GPIO_SWPORTA_DDR, GpioConfig.PortADir);
		 // Interrupt Mask Register - GPIO_INTMASK
		 vGalileoGPIOWrite(GPIO_INTMASK, GpioConfig.IntMask);
		 // Interrupt Level Type Register - GPIO_INTTYPE_LEVEL
		 vGalileoGPIOWrite(GPIO_INTTYPE_LEVEL, GpioConfig.IntType);
		 // Interrupt Polarity Type Register - GPIO_INT_POLARITY
		 vGalileoGPIOWrite(GPIO_INT_POLARITY, GpioConfig.IntPolarity);
		 // Interrupt Debounce Type Register - GPIO_DEBOUNCE
		 vGalileoGPIOWrite(GPIO_DEBOUNCE, GpioConfig.Debounce);
		 // Interrupt Clock Synchronization Register - GPIO_LS_SYNC
		 vGalileoGPIOWrite(GPIO_LS_SYNC, GpioConfig.LsSync);
		 bGalileoGPIOInitialized = true;
	 }
 }
 /*-----------------------------------------------------------*/

 /*-----------------------------------------------------------------------
  * I/O direction and level setting functions
  *------------------------------------------------------------------------
  */
  void vGalileoSetGPIOBitDirection(uint32_t GPIONumber, uint32_t Direction)
  {
	  /* Check Range. */
	  if(GPIONumber <= 9)
	  {
		  /* setup gpio direction. */
		  if (bGalileoGPIOInitialized)
		  {
			  if(Direction == GPIO_OUTPUT)
				  GpioConfig.PortADir |= (1 << GPIONumber);
			  else
				  GpioConfig.PortADir &= ~(1 << GPIONumber);
			  vGalileoGPIOWrite(GPIO_SWPORTA_DDR, GpioConfig.PortADir);
		  }
	  }
  }
  /*-----------------------------------------------------------*/

  void vGalileoSetGPIOBitLevel(uint32_t GPIONumber, uint32_t Level)
  {
	  /* Check Range. */
	  if(GPIONumber <= 9)
	  {
		  /* Set the bit high or low. */
		  if (bGalileoGPIOInitialized)
		  {
			  // 1 for on, 0 for off.
			  if (Level == HIGH)
				  GpioConfig.PortADR |= (1 << GPIONumber);
			  else
				  GpioConfig.PortADR &= ~(1 << GPIONumber);
			  vGalileoGPIOWrite(GPIO_SWPORTA_DR, GpioConfig.PortADR);
		  }
	  }
  }
  /*-----------------------------------------------------------*/

  static void LegacyGpioSetLevel(uint32_t RegOffset, uint32_t GpioNum, uint8_t HighLevel)
  {
	  uint32_t RegValue;
	  uint32_t legacy_gpio_base;
	  uint32_t GpioNumMask;
	  uint8_t Bus = LEGACY_GPIO_BUS_NUMBER;
	  uint8_t Device = LEGACY_GPIO_DEVICE_NUMBER;
	  uint8_t Func = LEGACY_GPIO_FUNCTION_NUMBER;

	  // Get PCI Configuration IO Address
	  legacy_gpio_base =
	  uiGalileoLegacyGPIOPCIRead(IO_PCI_ADDRESS(Bus, Device, Func, R_QNC_LPC_GBA_BASE), B_QNC_LPC_GPA_BASE_MASK);

	  // Read register (Port I/O )
	  RegValue = inl(legacy_gpio_base + RegOffset);

	  // Set Data and mask
	  GpioNumMask = (1 << GpioNum);
	  if (HighLevel)
	   	  RegValue |= (GpioNumMask);
	  else
		  RegValue &= ~(GpioNumMask);

	  // Write the data (Port I/O )
	  outl((legacy_gpio_base + RegOffset), RegValue);
  }
  /*-----------------------------------------------------------*/

  void vGalileoLegacyGPIOInitializationForLED(void)
  {
	  // Setup multiplexers to route GPIO_SUS<5> to LED
	  vGalileoRouteLEDPins();

	  // Set GPIO_SUS<5> as output
	  LegacyGpioSetLevel (R_QNC_GPIO_RGIO_RESUME_WELL,
	  GALILEO_GEN2_FLASH_UPDATE_LED_RESUMEWELL_GPIO, GPIO_OUTPUT);

	  // Set GPIO_SUS<5> level to low
	  LegacyGpioSetLevel (R_QNC_GPIO_RGLVL_RESUME_WELL,
      GALILEO_GEN2_FLASH_UPDATE_LED_RESUMEWELL_GPIO, LOW);
  }
  /*-----------------------------------------------------------*/

  void vGalileoBlinkLEDUsingLegacyGPIO(uint32_t Level)
  {
	  LegacyGpioSetLevel (R_QNC_GPIO_RGLVL_RESUME_WELL,
	  GALILEO_GEN2_FLASH_UPDATE_LED_RESUMEWELL_GPIO, Level);
  }
  /*-----------------------------------------------------------*/

  /*-----------------------------------------------------------------------
   * I2C support functions
   *------------------------------------------------------------------------
   */
  static inline uint64_t rdtsc(void)
  {
      uint32_t lo, hi;
      uint64_t tsc;
      __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
      tsc = hi;
      tsc <<= 32;
      tsc |= lo;
      return tsc;
  }
  /*-----------------------------------------------------------*/

  void vMicroSecondDelay(uint32_t DelayTime)
  {
	  uint64_t diff_in_us = 0;
	  uint64_t cpufreq_in_mhz = 400;
	  uint64_t tsc_start = rdtsc();
	  uint64_t tsc_current = tsc_start;

	  do
	  {
	  	  diff_in_us = ((tsc_current - tsc_start) / cpufreq_in_mhz);
		  tsc_current = rdtsc();
	  }
	  while (diff_in_us < (uint64_t) DelayTime);
  }
  /*-----------------------------------------------------------*/

  void vMilliSecondDelay(uint32_t DelayTime)
  {
	  vMicroSecondDelay (DelayTime * 1000);
  }
  /*-----------------------------------------------------------*/

  static uintn_t GetI2CIoPortBaseAddress(void)
  {
	  uint8_t Bus = IOH_I2C_GPIO_BUS_NUMBER;
	  uint8_t Device = IOH_I2C_GPIO_DEVICE_NUMBER;
	  int8_t Func = IOH_I2C_GPIO_FUNCTION_NUMBER;
	  uint32_t I2C_controller_base = MMIO_PCI_ADDRESS(Bus, Device, Func, 0);
	  uintn_t I2CIoPortBaseAddress = mem_read(I2C_controller_base, R_IOH_I2C_MEMBAR, 4);
	  return I2CIoPortBaseAddress;
  }
  /*-----------------------------------------------------------*/

  static void EnableI2CMmioSpace(void)
  {
	  uint8_t Bus = IOH_I2C_GPIO_BUS_NUMBER;
	  uint8_t Device = IOH_I2C_GPIO_DEVICE_NUMBER;
	  uint8_t Func = IOH_I2C_GPIO_FUNCTION_NUMBER;
	  uint32_t I2C_controller_base = MMIO_PCI_ADDRESS(Bus, Device, Func, 0);
	  uint8_t PciCmd = mem_read(I2C_controller_base, PCI_REG_PCICMD, 1);
	  mem_write(I2C_controller_base, PCI_REG_PCICMD, 1, (PciCmd | 0x7));
  }
  /*-----------------------------------------------------------*/

  static void DisableI2CController(void)
  {
	  uintn_t       I2CIoPortBaseAddress;
	  uint32_t      Addr;
	  uint32_t      Data;
	  uint8_t       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_t *) (uintn_t)(Addr));
	  Data &= ~B_I2C_REG_ENABLE;
	  *((volatile uint32_t *) (uintn_t)(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_t *) (uintn_t)(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).
		  if (++PollCount >= MAX_T_POLL_COUNT)
			  break;
		  vMicroSecondDelay(TI2C_POLL);
		  Data = *((volatile uint32_t *) (uintn_t)(Addr));
		  Data &= I2C_REG_ENABLE_STATUS;
	  }

	  // Read IC_CLR_INTR register to automatically clear the combined interrupt,
	  // all individual interrupts and the IC_TX_ABRT_SOURCE register.
	  if (PollCount < MAX_T_POLL_COUNT)
	  {
		  Addr = I2CIoPortBaseAddress + I2C_REG_CLR_INT;
		  Data = *((volatile uint32_t *) (uintn_t)(Addr));
	  }
  }
  /*-----------------------------------------------------------*/

  static void EnableI2CController(void)
  {
	  uintn_t   I2CIoPortBaseAddress;
	  uint32_t  Addr;
	  uint32_t  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_t *) (uintn_t)(Addr));
	  Data |= B_I2C_REG_ENABLE;
	  *((volatile uint32_t *) (uintn_t)(Addr)) = Data;

	  // Clear overflow and abort error status bits before transactions.
	  Addr = I2CIoPortBaseAddress + I2C_REG_CLR_RX_OVER;
	  Data = *((volatile uint32_t *) (uintn_t)(Addr));
	  Addr = I2CIoPortBaseAddress + I2C_REG_CLR_TX_OVER;
	  Data = *((volatile uint32_t *) (uintn_t)(Addr));
	  Addr = I2CIoPortBaseAddress + I2C_REG_CLR_TX_ABRT;
	  Data = *((volatile uint32_t *) (uintn_t)(Addr));
  }
  /*-----------------------------------------------------------*/

  static uint32_t InitializeInternal(I2C_ADDR_MODE AddrMode)
  {
		uintn_t   I2CIoPortBaseAddress;
		uintn_t   Addr;
		uint32_t  Data;
		uint32_t  Status = 0;

		// 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_t *) (uintn_t)(Addr));
		Data &= ~B_I2C_REG_CLR_START_DET;
		*((volatile uint32_t *) (uintn_t)(Addr)) = Data;

		// Clear STOP_DET
		Addr = I2CIoPortBaseAddress + I2C_REG_CLR_STOP_DET;
		Data = *((volatile uint32_t *) (uintn_t)(Addr));
		Data &= ~B_I2C_REG_CLR_STOP_DET;
		*((volatile uint32_t *) (uintn_t)(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_t *) (uintn_t)(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;

		// Default to slow mode
		Data |= BIT1;
		*((volatile uint32_t *) (uintn_t)(Addr)) = Data;
		Data = *((volatile uint32_t *) (uintn_t)(Addr));
		return Status;
  }
  /*-----------------------------------------------------------*/

  static void I2cEntry(uint16_t *SaveCmdPtr, uint32_t *SaveBar0Ptr)
  {
		uint8_t Bus = IOH_I2C_GPIO_BUS_NUMBER;
		uint8_t Device = IOH_I2C_GPIO_DEVICE_NUMBER;
		uint8_t Func = IOH_I2C_GPIO_FUNCTION_NUMBER;
		uint32_t I2C_controller_base = MMIO_PCI_ADDRESS(Bus, Device, Func, 0);

		I2CGpioBase = mem_read(I2C_controller_base, R_IOH_I2C_MEMBAR, 4);
		*SaveBar0Ptr = I2CGpioBase;
		if (((*SaveBar0Ptr) & B_IOH_I2C_GPIO_MEMBAR_ADDR_MASK) == 0)
		{
			mem_write(I2C_controller_base, R_IOH_I2C_MEMBAR, 4, IO_PCI_ADDRESS(Bus, Device, Func, 0));
			// also Save Cmd Register, Setup by InitializeInternal later during xfers.
			*SaveCmdPtr = mem_read(I2C_controller_base, PCI_REG_PCICMD, 1);
		}
  }
  /*-----------------------------------------------------------*/

  static void I2cExit(uint16_t SaveCmd, uint32_t SaveBar0)
  {
		if ((SaveBar0 & B_IOH_I2C_GPIO_MEMBAR_ADDR_MASK) == 0)
		{
			uint8_t Bus = IOH_I2C_GPIO_BUS_NUMBER;
			uint8_t Device = IOH_I2C_GPIO_DEVICE_NUMBER;
			uint8_t Func = IOH_I2C_GPIO_FUNCTION_NUMBER;
			uint32_t I2C_controller_base = MMIO_PCI_ADDRESS(Bus, Device, Func, 0);
			mem_write(I2C_controller_base, PCI_REG_PCICMD, 1, SaveCmd);
			mem_write(I2C_controller_base, R_IOH_I2C_MEMBAR, 4, SaveBar0);
		}
  }
  /*-----------------------------------------------------------*/

  static uint32_t WaitForStopDet(void)
  {
		uintn_t   I2CIoPortBaseAddress;
		uint32_t  Addr;
		uint32_t  Data;
		uint32_t  PollCount = 0;
		uint32_t  Status = 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_t *) (uintn_t)(Addr));
			if ((Data & I2C_REG_RAW_INTR_STAT_TX_ABRT) != 0)
			{
				Status = -1;
				break;
			}
			if ((Data & I2C_REG_RAW_INTR_STAT_TX_OVER) != 0)
			{
				Status = -1;
				break;
			}
			if ((Data & I2C_REG_RAW_INTR_STAT_RX_OVER) != 0)
			{
				Status = -1;
				break;
			}
			if ((Data & I2C_REG_RAW_INTR_STAT_STOP_DET) != 0)
			{
				  Status = 0;
				  break;
			}
			vMicroSecondDelay(TI2C_POLL);
			PollCount++;
			if (PollCount >= MAX_STOP_DET_POLL_COUNT)
			{
				  Status = -1;
				  break;
			}
		} while (TRUE);

		return Status;
  }
  /*-----------------------------------------------------------*/

  uint32_t WriteMultipleByte(uintn_t I2CAddress, uint8_t *WriteBuffer, uintn_t Length)
  {
		uintn_t   I2CIoPortBaseAddress;
		uintn_t   Index;
		uintn_t   Addr;
		uint32_t  Data;
		uint32_t  Status = 0;

		if (Length > I2C_FIFO_SIZE)
			return -1;  // 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_t *) (uintn_t)(Addr));
		Data &= ~B_I2C_REG_TAR;
		Data |= I2CAddress;
		*((volatile uint32_t *) (uintn_t)(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_t *) (uintn_t)(Addr));
			  Data &= 0xFFFFFF00;
			  Data |= (uint8_t)WriteBuffer[Index];
			  Data &= ~B_I2C_REG_DATA_CMD_RW;
			  if (Index == (Length-1))
				  Data |= B_I2C_REG_DATA_CMD_STOP;
			  *((volatile uint32_t *) (uintn_t)(Addr)) = Data;
		}

		// Wait for transfer completion
		Status = WaitForStopDet ();

		// Ensure I2C Controller disabled.
		DisableI2CController ();

		return Status;
  }
  /*-----------------------------------------------------------*/

 static void I2CWriteMultipleBytes(I2C_DEVICE_ADDRESS  SlaveAddress,
 I2C_ADDR_MODE AddrMode, uintn_t *Length, void *Buffer)
 {
	  uintn_t  I2CAddress;
	  uint16_t SaveCmd;
	  uint32_t SaveBar0;

	  if (Buffer != NULL && Length != NULL)
	  {
		  SaveCmd = 0;
		  SaveBar0 = 0;
		  I2cEntry (&SaveCmd, &SaveBar0);
		  if (InitializeInternal(AddrMode) == 0)
		  {
			  I2CAddress = SlaveAddress.I2CDeviceAddress;
			  WriteMultipleByte(I2CAddress, Buffer, (*Length));
		  }
		  I2cExit (SaveCmd, SaveBar0);
	  }
 }
 /*-----------------------------------------------------------*/

  uint32_t ReadMultipleByte(uintn_t I2CAddress, uint8_t *Buffer,
  uintn_t WriteLength, uintn_t ReadLength)
  {
	  uintn_t   I2CIoPortBaseAddress;
	  uintn_t   Index;
	  uintn_t   Addr;
	  uint32_t  Data;
	  uint8_t   PollCount;
	  uint32_t  Status;

	  if (WriteLength > I2C_FIFO_SIZE || ReadLength > I2C_FIFO_SIZE)
		  return -1;  // 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_t *) (uintn_t)(Addr));
	  Data &= ~B_I2C_REG_TAR;
	  Data |= I2CAddress;
	  *((volatile uint32_t *) (uintn_t)(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_t *) (uintn_t)(Addr));
      	  Data &= 0xFFFFFF00;
      	  Data |= (uint8_t)Buffer[Index];
      	  Data &= ~B_I2C_REG_DATA_CMD_RW;
      	  *((volatile uint32_t *) (uintn_t)(Addr)) = Data;
	  }

	  // Issue Read Transfers for each byte (Restart issued when write/read bit changed).
	  for (Index = 0; Index < ReadLength; Index++)
	  {
		  Data = *((volatile uint32_t *) (uintn_t)(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_t *) (uintn_t)(Addr)) = Data;
	  }

	  // Wait for STOP condition.
	  Status = WaitForStopDet();
	  if (Status != 0)
	  {
		  // 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_t *) (uintn_t)(Addr));
		  while ((Data != ReadLength) && (PollCount < MAX_T_POLL_COUNT))
		  {
			  vMicroSecondDelay(TI2C_POLL);
			  PollCount++;
			  Data = *((volatile uint32_t *) (uintn_t)(Addr));
		  }

		  Addr = I2CIoPortBaseAddress + I2C_REG_RAW_INTR_STAT;
		  Data = *((volatile uint32_t *) (uintn_t)(Addr));

		  // If no timeout or device error then read rx data.
		  if (PollCount == MAX_T_POLL_COUNT)
		  {
			  Status = -1;
		  }
		  else if ((Data & I2C_REG_RAW_INTR_STAT_RX_OVER) != 0)
		  {
			  Status = -1;
		  }
		  else
		  {
			  // Clear RX underflow before reading IC_DATA_CMD.
			  Addr = I2CIoPortBaseAddress + I2C_REG_CLR_RX_UNDER;
			  Data = *((volatile uint32_t *) (uintn_t)(Addr));

			  // Read data.
			  Addr = I2CIoPortBaseAddress + I2C_REG_DATA_CMD;
			  for (Index = 0; Index < ReadLength; Index++)
			  {
				  Data = *((volatile uint32_t *) (uintn_t)(Addr));
				  Data &= 0x000000FF;
				  *(Buffer+Index) = (uint8_t)Data;
			  }
			  Addr = I2CIoPortBaseAddress + I2C_REG_RAW_INTR_STAT;
			  Data = *((volatile uint32_t *) (uintn_t)(Addr));
			  Data &= I2C_REG_RAW_INTR_STAT_RX_UNDER;
			  if (Data != 0)
			  {
				  Status = -1;
			  }
			  else
			  {
				  Status = 0;
			  }
		  }
	  }

	  // Ensure I2C Controller disabled.
	  DisableI2CController ();

	  return Status;
  }
  /*-----------------------------------------------------------*/

 static void I2CReadMultipleBytes(I2C_DEVICE_ADDRESS SlaveAddress, I2C_ADDR_MODE AddrMode,
 uintn_t *WriteLength, uintn_t *ReadLength, void *Buffer )
 {
	  uintn_t  I2CAddress;
	  uint16_t SaveCmd;
	  uint32_t SaveBar0;

	  if (Buffer != NULL && WriteLength != NULL && ReadLength != NULL)
	  {
		  SaveCmd = 0;
		  SaveBar0 = 0;
		  I2cEntry (&SaveCmd, &SaveBar0);
		  if (InitializeInternal(AddrMode) == 0)
		  {
			  I2CAddress = SlaveAddress.I2CDeviceAddress;
			  ReadMultipleByte(I2CAddress, Buffer, (*WriteLength), (*ReadLength));
		  }
		  I2cExit (SaveCmd, SaveBar0);
	  }
 }
 /*-----------------------------------------------------------*/

 /*-----------------------------------------------------------------------
  * Pcal9555 chips used on Galileo Gen 2 boards (see FAB-H schematic)
  *------------------------------------------------------------------------
  */
 static void Pcal9555SetPortRegBit(uint32_t Pcal9555SlaveAddr, uint32_t GpioNum, uint8_t RegBase, uint8_t LogicOne)
 {
	 uintn_t            ReadLength;
	 uintn_t            WriteLength;
	 uint8_t            Data[2];
	 uint8_t            *RegValuePtr;
	 uint8_t            GpioNumMask;
	 uint8_t            SubAddr;
	 I2C_DEVICE_ADDRESS I2cDeviceAddr;
	 I2C_ADDR_MODE      I2cAddrMode;

	 // Set I2C address and mode.
	 I2cDeviceAddr.I2CDeviceAddress = (uintn_t) Pcal9555SlaveAddr;
	 I2cAddrMode = EfiI2CSevenBitAddrMode;

	 // Set I2C subaddress and GPIO mask.
	 if (GpioNum < 8)
	 {
		 SubAddr = RegBase;
		 GpioNumMask = (uintn_t) (1 << GpioNum);
	 }
	 else
	 {
		 SubAddr = RegBase + 1;
		 GpioNumMask = (uintn_t) (1 << (GpioNum - 8));
	 }

	 // Output port value always at 2nd byte in Data variable.
	 RegValuePtr = &Data[1];

	 // On read entry - sub address at 2nd byte, on read exit - output
	 // port value in 2nd byte.
	 Data[1] = SubAddr;
	 WriteLength = 1;
	 ReadLength = 1;
	 I2CReadMultipleBytes(I2cDeviceAddr, I2cAddrMode, &WriteLength, &ReadLength,  &Data[1]);

	 // Adjust output port bit using mask value.
	 if (LogicOne)
		 *RegValuePtr = *RegValuePtr | GpioNumMask;
	 else
		 *RegValuePtr = *RegValuePtr & ~(GpioNumMask);

	 // Update register. Sub address at 1st byte, value at 2nd byte.
	 WriteLength = 2;
	 Data[0] = SubAddr;
	 I2CWriteMultipleBytes(I2cDeviceAddr,I2cAddrMode, &WriteLength, Data);
 }
 /*-----------------------------------------------------------*/

 static void PlatformPcal9555GpioPullup(uint32_t Pcal9555SlaveAddr, uint32_t GpioNum, uint32_t Enable)
 {
	  Pcal9555SetPortRegBit(Pcal9555SlaveAddr, GpioNum, PCAL9555_REG_PULL_EN_PORT0, Enable );
 }
 /*-----------------------------------------------------------*/

 static void PlatformPcal9555GpioSetDir(uint32_t Pcal9555SlaveAddr, uint32_t GpioNum, uint32_t CfgAsInput)
 {
	  Pcal9555SetPortRegBit(Pcal9555SlaveAddr, GpioNum, PCAL9555_REG_CFG_PORT0, CfgAsInput);
 }
 /*-----------------------------------------------------------*/

 static void PlatformPcal9555GpioSetLevel(uint32_t Pcal9555SlaveAddr, uint32_t GpioNum, uint32_t HighLevel )
 {
	  Pcal9555SetPortRegBit(Pcal9555SlaveAddr, GpioNum, PCAL9555_REG_OUT_PORT0, HighLevel );
 }
 /*-----------------------------------------------------------*/

 /*-----------------------------------------------------------------------
  * GPIO pin routing function
  *------------------------------------------------------------------------
  */
 static void vGalileoRouteLEDPins(void)
 {
	 // For GpioNums below values 0 to 7 are for Port0 IE. P0-0 - P0-7 and
	 // values 8 to 15 are for Port1 IE. P1-0 - P1-7.
	 // Disable Pull-ups / pull downs on EXP0 pin for LVL_B_PU7 signal.
	 PlatformPcal9555GpioPullup (
	 GALILEO_GEN2_IOEXP0_7BIT_SLAVE_ADDR,  // IO Expander 0.
     15,                                   // P1-7.
     FALSE
     );

     // Make LVL_B_OE7_N an output pin.
     PlatformPcal9555GpioSetDir (
     GALILEO_GEN2_IOEXP0_7BIT_SLAVE_ADDR,  // IO Expander 0.
     14,                                   // P1-6.
     FALSE);

     // Set level of LVL_B_OE7_N to low.
     PlatformPcal9555GpioSetLevel (
     GALILEO_GEN2_IOEXP0_7BIT_SLAVE_ADDR,
     14,
     FALSE);

     // Make MUX8_SEL an output pin.
     PlatformPcal9555GpioSetDir (
     GALILEO_GEN2_IOEXP1_7BIT_SLAVE_ADDR,  // IO Expander 1.
     14,                                   // P1-6.
     FALSE);

     // Set level of MUX8_SEL to low to route GPIO_SUS<5> to LED.
     PlatformPcal9555GpioSetLevel (
     GALILEO_GEN2_IOEXP1_7BIT_SLAVE_ADDR,  // IO Expander 1.
     14,                                   // P1-6.
     FALSE);
 }
 /*-----------------------------------------------------------*/
