/** @file
Essential platform configuration.

Copyright (c) 2013 Intel Corporation.

SPDX-License-Identifier: BSD-2-Clause-Patent


**/

#include "PlatformInitDxe.h"

//
// The protocols, PPI and GUID defintions for this module
//

//
// The Library classes this module consumes
//

//
// RTC:28208 - System hang/crash when entering probe mode(ITP) when relocating SMBASE
//             Workaround to make default SMRAM UnCachable
//
#define SMM_DEFAULT_SMBASE                  0x30000     // Default SMBASE address
#define SMM_DEFAULT_SMBASE_SIZE_BYTES       0x10000     // Size in bytes of default SMRAM

BOOLEAN                       mMemCfgDone = FALSE;
UINT8                         ChipsetDefaultMac [6] = {0xff,0xff,0xff,0xff,0xff,0xff};

VOID
EFIAPI
PlatformInitializeUart0MuxGalileo (
  VOID
  )
/*++


Routine Description:

  This is the routine to initialize UART0 for DBG2 support. The hardware used in this process is a
  Legacy Bridge (Legacy GPIO), I2C controller, a bi-directional MUX and a Cypress CY8C9540A chip.

Arguments:

  None.

Returns:

  None.

--*/
{
  EFI_STATUS                        Status;
  EFI_I2C_DEVICE_ADDRESS            I2CSlaveAddress;
  UINTN                             Length;
  UINT8                             Buffer[2];

  if (PlatformLegacyGpioGetLevel (R_QNC_GPIO_RGLVL_RESUME_WELL, GALILEO_DETERMINE_IOEXP_SLA_RESUMEWELL_GPIO)) {
    I2CSlaveAddress.I2CDeviceAddress = GALILEO_IOEXP_J2HI_7BIT_SLAVE_ADDR;
  } else {
    I2CSlaveAddress.I2CDeviceAddress = GALILEO_IOEXP_J2LO_7BIT_SLAVE_ADDR;
  }

  //
  // Set GPIO_SUS<2> as an output, raise voltage to Vdd.
  //
  PlatformLegacyGpioSetLevel (R_QNC_GPIO_RGLVL_RESUME_WELL, 2, TRUE);

  //
  // Select Port 3
  //
  Length = 2;
  Buffer[0] = 0x18; //sub-address
  Buffer[1] = 0x03; //data

  Status = I2cWriteMultipleByte (
              I2CSlaveAddress,
              EfiI2CSevenBitAddrMode,
              &Length,
              &Buffer
              );
  ASSERT_EFI_ERROR (Status);

  //
  // Set "Pin Direction" bit4 and bit5 as outputs
  //
  Length = 2;
  Buffer[0] = 0x1C; //sub-address
  Buffer[1] = 0xCF; //data

  Status = I2cWriteMultipleByte (
              I2CSlaveAddress,
              EfiI2CSevenBitAddrMode,
              &Length,
              &Buffer
              );
  ASSERT_EFI_ERROR (Status);

  //
  // Lower GPORT3 bit4 and bit5 to Vss
  //
  Length = 2;
  Buffer[0] = 0x0B; //sub-address
  Buffer[1] = 0xCF; //data

  Status = I2cWriteMultipleByte (
              I2CSlaveAddress,
              EfiI2CSevenBitAddrMode,
              &Length,
              &Buffer
              );
  ASSERT_EFI_ERROR (Status);
}

VOID
EFIAPI
PlatformInitializeUart0MuxGalileoGen2 (
  VOID
  )
/*++


Routine Description:

  This is the routine to initialize UART0 on GalileoGen2. The hardware used in this process is
  I2C controller and the configuring the following IO Expander signal.

  EXP1.P1_5 should be configured as an output & driven high.
  EXP1.P0_0 should be configured as an output & driven high.
  EXP0.P1_4 should be configured as an output, driven low.
  EXP1.P0_1 pullup should be disabled.
  EXP0.P1_5 Pullup should be disabled.

Arguments:

  None.

Returns:

  None.

--*/

{
  //
  //  EXP1.P1_5 should be configured as an output & driven high.
  //
  PlatformPcal9555GpioSetDir (
    GALILEO_GEN2_IOEXP1_7BIT_SLAVE_ADDR,  // IO Expander 1.
    13,                                   // P1-5.
    TRUE
    );
  PlatformPcal9555GpioSetLevel (
    GALILEO_GEN2_IOEXP1_7BIT_SLAVE_ADDR,  // IO Expander 1.
    13,                                   // P1-5.
    TRUE
    );

  //
  // EXP1.P0_0 should be configured as an output & driven high.
  //
  PlatformPcal9555GpioSetDir (
    GALILEO_GEN2_IOEXP0_7BIT_SLAVE_ADDR,  // IO Expander 0.
    0,                                    // P0_0.
    TRUE
    );
  PlatformPcal9555GpioSetLevel (
    GALILEO_GEN2_IOEXP0_7BIT_SLAVE_ADDR,  // IO Expander 0.
    0,                                    // P0_0.
    TRUE
    );

  //
  //  EXP0.P1_4 should be configured as an output, driven low.
  //
  PlatformPcal9555GpioSetDir (
    GALILEO_GEN2_IOEXP0_7BIT_SLAVE_ADDR,  // IO Expander 0.
    12,                                   // P1-4.
    FALSE
    );
  PlatformPcal9555GpioSetLevel (          // IO Expander 0.
    GALILEO_GEN2_IOEXP0_7BIT_SLAVE_ADDR,  // P1-4
    12,
    FALSE
    );

  //
  // EXP1.P0_1 pullup should be disabled.
  //
  PlatformPcal9555GpioDisablePull (
    GALILEO_GEN2_IOEXP1_7BIT_SLAVE_ADDR,  // IO Expander 1.
    1                                     // P0-1.
    );

  //
  // EXP0.P1_5 Pullup should be disabled.
  //
  PlatformPcal9555GpioDisablePull (
    GALILEO_GEN2_IOEXP0_7BIT_SLAVE_ADDR,  // IO Expander 0.
    13                                    // P1-5.
    );
}

VOID
EFIAPI
PlatformConfigOnSmmConfigurationProtocol (
  IN  EFI_EVENT Event,
  IN  VOID      *Context
  )
/*++

Routine Description:

  Function runs in PI-DXE to perform platform specific config when
  SmmConfigurationProtocol is installed.

Arguments:
  Event       - The event that occured.
  Context     - For EFI compatiblity.  Not used.

Returns:
  None.
--*/

{
  EFI_STATUS            Status;
  UINT32                NewValue;
  UINT64                BaseAddress;
  UINT64                SmramLength;
  VOID                  *SmmCfgProt;

  Status = gBS->LocateProtocol (&gEfiSmmConfigurationProtocolGuid, NULL, &SmmCfgProt);
  if (Status != EFI_SUCCESS){
    DEBUG ((DEBUG_INFO, "gEfiSmmConfigurationProtocolGuid triggered but not valid.\n"));
    return;
  }
  if (mMemCfgDone) {
    DEBUG ((DEBUG_INFO, "Platform DXE Mem config already done.\n"));
    return;
  }

  //
  // Disable eSram block (this will also clear/zero eSRAM)
  // We only use eSRAM in the PEI phase. Disable now that we are in the DXE phase
  //
  NewValue = QNCPortRead (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, QUARK_NC_MEMORY_MANAGER_ESRAMPGCTRL_BLOCK);
  NewValue |= BLOCK_DISABLE_PG;
  QNCPortWrite (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, QUARK_NC_MEMORY_MANAGER_ESRAMPGCTRL_BLOCK, NewValue);

  //
  // Update HMBOUND to top of DDR3 memory and LOCK
  // We disabled eSRAM so now we move HMBOUND down to top of DDR3
  //
  QNCGetTSEGMemoryRange (&BaseAddress, &SmramLength);
  NewValue = (UINT32)(BaseAddress + SmramLength);
  DEBUG ((EFI_D_INFO,"Locking HMBOUND at: = 0x%8x\n",NewValue));
  QNCPortWrite (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QUARK_NC_HOST_BRIDGE_HMBOUND_REG, (NewValue | HMBOUND_LOCK));

  //
  // Lock IMR5 now that HMBOUND is locked (legacy S3 region)
  //
  NewValue = QNCPortRead (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, QUARK_NC_MEMORY_MANAGER_IMR5+QUARK_NC_MEMORY_MANAGER_IMRXL);
  NewValue |= IMR_LOCK;
  QNCPortWrite (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, QUARK_NC_MEMORY_MANAGER_IMR5+QUARK_NC_MEMORY_MANAGER_IMRXL, NewValue);

  //
  // Lock IMR6 now that HMBOUND is locked (ACPI Reclaim/ACPI/Runtime services/Reserved)
  //
  NewValue = QNCPortRead (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, QUARK_NC_MEMORY_MANAGER_IMR6+QUARK_NC_MEMORY_MANAGER_IMRXL);
  NewValue |= IMR_LOCK;
  QNCPortWrite (QUARK_NC_MEMORY_MANAGER_SB_PORT_ID, QUARK_NC_MEMORY_MANAGER_IMR6+QUARK_NC_MEMORY_MANAGER_IMRXL, NewValue);

  //
  // Disable IMR2 memory protection (RMU Main Binary)
  //
  QncImrWrite (
            QUARK_NC_MEMORY_MANAGER_IMR2,
            (UINT32)(IMRL_RESET & ~IMR_EN),
            (UINT32)IMRH_RESET,
            (UINT32)IMRX_ALL_ACCESS,
            (UINT32)IMRX_ALL_ACCESS
        );

  //
  // Disable IMR3 memory protection (Default SMRAM)
  //
  QncImrWrite (
            QUARK_NC_MEMORY_MANAGER_IMR3,
            (UINT32)(IMRL_RESET & ~IMR_EN),
            (UINT32)IMRH_RESET,
            (UINT32)IMRX_ALL_ACCESS,
            (UINT32)IMRX_ALL_ACCESS
        );

  //
  // Disable IMR4 memory protection (eSRAM).
  //
  QncImrWrite (
            QUARK_NC_MEMORY_MANAGER_IMR4,
            (UINT32)(IMRL_RESET & ~IMR_EN),
            (UINT32)IMRH_RESET,
            (UINT32)IMRX_ALL_ACCESS,
            (UINT32)IMRX_ALL_ACCESS
        );

  //
  // RTC:28208 - System hang/crash when entering probe mode(ITP) when relocating SMBASE
  //             Workaround to make default SMRAM UnCachable
  //
  Status = gDS->SetMemorySpaceAttributes (
                  (EFI_PHYSICAL_ADDRESS) SMM_DEFAULT_SMBASE,
                  SMM_DEFAULT_SMBASE_SIZE_BYTES,
                  EFI_MEMORY_WB
                  );
  ASSERT_EFI_ERROR (Status);

  mMemCfgDone = TRUE;
}

VOID
EFIAPI
PlatformConfigOnSpiReady (
  IN  EFI_EVENT Event,
  IN  VOID      *Context
  )
/*++

Routine Description:

  Function runs in PI-DXE to perform platform specific config when SPI
  interface is ready.

Arguments:
  Event       - The event that occured.
  Context     - For EFI compatiblity.  Not used.

Returns:
  None.

--*/
{
  EFI_STATUS                        Status;
  VOID                              *SpiReadyProt = NULL;
  EFI_PLATFORM_TYPE                 Type;
  EFI_BOOT_MODE                      BootMode;

  BootMode = GetBootModeHob ();

  Status = gBS->LocateProtocol (&gEfiSmmSpiReadyProtocolGuid, NULL, &SpiReadyProt);
  if (Status != EFI_SUCCESS){
    DEBUG ((DEBUG_INFO, "gEfiSmmSpiReadyProtocolGuid triggered but not valid.\n"));
    return;
  }

  //
  // Lock regions SPI flash.
  //
  PlatformFlashLockPolicy (FALSE);

  //
  // Configurations and checks to be done when DXE tracing available.
  //

  //
  // Platform specific Signal routing.
  //

  //
  // Skip any signal not needed for recovery and flash update.
  //
  if (BootMode != BOOT_ON_FLASH_UPDATE && BootMode != BOOT_IN_RECOVERY_MODE) {

    //
    // Galileo Platform UART0 support.
    //
    Type = (EFI_PLATFORM_TYPE)PcdGet16 (PcdPlatformType);
    if (Type == Galileo) {
      //
      // Use MUX to connect out UART0 pins.
      //
      PlatformInitializeUart0MuxGalileo ();
    }

    //
    // GalileoGen2 Platform UART0 support.
    //
    if (Type == GalileoGen2) {
      //
      // Use route out UART0 pins.
      //
      PlatformInitializeUart0MuxGalileoGen2 ();
    }
  }
}

EFI_STATUS
EFIAPI
CreateConfigEvents (
  VOID
  )
/*++

Routine Description:

Arguments:
  None

Returns:
  EFI_STATUS

--*/
{
  EFI_EVENT   EventSmmCfg;
  EFI_EVENT   EventSpiReady;
  VOID        *RegistrationSmmCfg;
  VOID        *RegistrationSpiReady;

  //
  // Schedule callback for when SmmConfigurationProtocol installed.
  //
  EventSmmCfg = EfiCreateProtocolNotifyEvent (
                  &gEfiSmmConfigurationProtocolGuid,
                  TPL_CALLBACK,
                  PlatformConfigOnSmmConfigurationProtocol,
                  NULL,
                  &RegistrationSmmCfg
                  );
  ASSERT (EventSmmCfg != NULL);

  //
  // Schedule callback to setup SPI Flash Policy when SPI interface ready.
  //
  EventSpiReady = EfiCreateProtocolNotifyEvent (
                    &gEfiSmmSpiReadyProtocolGuid,
                    TPL_CALLBACK,
                    PlatformConfigOnSpiReady,
                    NULL,
                    &RegistrationSpiReady
                    );
  ASSERT (EventSpiReady != NULL);
  return EFI_SUCCESS;
}
