/** @file

  Copyright (c) 2014 - 2016, AMD Inc. All rights reserved.<BR>

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

**/

#include <PiPei.h>
#include <Library/DebugLib.h>
#include <Library/PcdLib.h>
#include <Library/PeimEntryPoint.h>
#include <Library/PeiServicesLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/HobLib.h>
#include <Library/ArmLib.h>
#include <Guid/ArmMpCoreInfo.h>

#include <Ppi/IscpPpi.h>
#include <Iscp.h>

/*----------------------------------------------------------------------------------------
 *                                  G L O B A L S
 *----------------------------------------------------------------------------------------
 */
//
// CoreInfo table
//
STATIC ARM_CORE_INFO mAmdMpCoreInfoTable[] = {
  {
    // Cluster 0, Core 0
    0x0, 0x0,
  },
  {
    // Cluster 0, Core 1
    0x0, 0x1,
  },
  {
    // Cluster 1, Core 0
    0x1, 0x0,
  },
  {
    // Cluster 1, Core 1
    0x1, 0x1,
  },
  {
    // Cluster 2, Core 0
    0x2, 0x0,
  },
  {
    // Cluster 2, Core 1
    0x2, 0x1,
  },
  {
    // Cluster 3, Core 0
    0x3, 0x0,
  },
  {
    // Cluster 3, Core 1
    0x3, 0x1,
  }
};

//
// Core count
//
STATIC UINTN mAmdCoreCount = sizeof (mAmdMpCoreInfoTable) / sizeof (ARM_CORE_INFO);


/*----------------------------------------------------------------------------------------
 *                                   P P I   L I S T
 *----------------------------------------------------------------------------------------
 */
STATIC EFI_PEI_ISCP_PPI            *PeiIscpPpi;


/*----------------------------------------------------------------------------------------
 *                            P P I   D E S C R I P T O R
 *----------------------------------------------------------------------------------------
 */
STATIC EFI_PEI_PPI_DESCRIPTOR mPlatInitPpiDescriptor =
{
  (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
  &gAmdStyxPlatInitPpiGuid,
  NULL
};


/**
 *---------------------------------------------------------------------------------------
 *  PlatInitPeiEntryPoint
 *
 *  Description:
 *    Entry point of the PlatInit PEI module.
 *
 *  Control flow:
 *    Query platform parameters via ISCP.
 *
 *  Parameters:
 *    @param[in]      FfsHeader            EFI_PEI_FILE_HANDLE
 *    @param[in]      **PeiServices        Pointer to the PEI Services Table.
 *
 *    @return         EFI_STATUS
 *
 *---------------------------------------------------------------------------------------
 */
EFI_STATUS
EFIAPI
PlatInitPeiEntryPoint (
  IN       EFI_PEI_FILE_HANDLE      FfsHeader,
  IN       CONST EFI_PEI_SERVICES   **PeiServices
  )
{
  EFI_STATUS                  Status;
  AMD_MEMORY_RANGE_DESCRIPTOR IscpMemDescriptor = {0};
  ISCP_FUSE_INFO              IscpFuseInfo = {0};
  ISCP_CPU_RESET_INFO         CpuResetInfo = {0};
  ISCP_MAC_INFO               MacAddrInfo = {0};
  UINTN                       MacSize;
  UINTN                       CpuCoreCount, CpuMap, CpuMapSize;
  UINTN                       Index, CoreNum;
  UINT32                      *CpuIdReg = (UINT32 *)FixedPcdGet32 (PcdCpuIdRegister);

  DEBUG ((EFI_D_ERROR, "PlatInit PEIM Loaded\n"));

  // CPUID
  Status = PcdSet32S (PcdSocCpuId, *CpuIdReg);
  ASSERT_EFI_ERROR (Status);
  DEBUG ((EFI_D_ERROR, "SocCpuId = 0x%X\n", PcdGet32 (PcdSocCpuId)));

  // Update core count based on PCD option
  if (mAmdCoreCount > PcdGet32 (PcdSocCoreCount)) {
    mAmdCoreCount = PcdGet32 (PcdSocCoreCount);
  }

  Status = PeiServicesLocatePpi (&gPeiIscpPpiGuid, 0, NULL, (VOID**)&PeiIscpPpi);
  ASSERT_EFI_ERROR (Status);

  // Get fuse information from ISCP
  Status = PeiIscpPpi->ExecuteFuseTransaction (PeiServices, &IscpFuseInfo);
  ASSERT_EFI_ERROR (Status);

  CpuMap = IscpFuseInfo.SocConfiguration.CpuMap;
  CpuCoreCount = IscpFuseInfo.SocConfiguration.CpuCoreCount;
  CpuMapSize = sizeof (IscpFuseInfo.SocConfiguration.CpuMap) * 8;

  ASSERT (CpuMap != 0);
  ASSERT (CpuCoreCount != 0);
  ASSERT (CpuCoreCount <= CpuMapSize);

  // Update core count based on fusing
  if (mAmdCoreCount > CpuCoreCount) {
    mAmdCoreCount = CpuCoreCount;
  }

  //
  // Update per-core information from ISCP
  // Walk CPU map to enumerate active cores
  //
  for (CoreNum = 0, Index = 0; CoreNum < CpuMapSize && Index < mAmdCoreCount; ++CoreNum) {
    if (CpuMap & 1) {
      CpuResetInfo.CoreNum = CoreNum;
      Status = PeiIscpPpi->ExecuteCpuRetrieveIdTransaction (
                 PeiServices, &CpuResetInfo );
      ASSERT_EFI_ERROR (Status);
      ASSERT (CpuResetInfo.CoreStatus.Status != CPU_CORE_DISABLED);
      ASSERT (CpuResetInfo.CoreStatus.Status != CPU_CORE_UNDEFINED);

      mAmdMpCoreInfoTable[Index].Mpidr = GET_MPID (CpuResetInfo.CoreStatus.ClusterId,
		                           CpuResetInfo.CoreStatus.CoreId);

      DEBUG ((EFI_D_ERROR, "Core[%d]: ClusterId = %d   CoreId = %d\n",
        Index, GET_MPIDR_AFF1 (mAmdMpCoreInfoTable[Index].Mpidr),
        GET_MPIDR_AFF0 (mAmdMpCoreInfoTable[Index].Mpidr)));

      // Next core in Table
      ++Index;
    }
    // Next core in Map
    CpuMap >>= 1;
  }

  // Update core count based on CPU map
  if (mAmdCoreCount > Index) {
    mAmdCoreCount = Index;
  }

  // Update SocCoreCount on Dynamic PCD
  if (PcdGet32 (PcdSocCoreCount) != mAmdCoreCount) {
    Status = PcdSet32S (PcdSocCoreCount, mAmdCoreCount);
    ASSERT_EFI_ERROR (Status);
  }

  DEBUG ((EFI_D_ERROR, "SocCoreCount = %d\n", PcdGet32 (PcdSocCoreCount)));

  // Build AmdMpCoreInfo HOB
  BuildGuidDataHob (&gAmdStyxMpCoreInfoGuid, mAmdMpCoreInfoTable, sizeof (ARM_CORE_INFO) * mAmdCoreCount);

  // Get SystemMemorySize from ISCP
  IscpMemDescriptor.Size0 = 0;
  Status = PeiIscpPpi->ExecuteMemoryTransaction (PeiServices, &IscpMemDescriptor);
  ASSERT_EFI_ERROR (Status);

  // Update SystemMemorySize on Dynamic PCD
  if (IscpMemDescriptor.Size0) {
    Status = PcdSet64S (PcdSystemMemorySize, IscpMemDescriptor.Size0);
    ASSERT_EFI_ERROR (Status);
  }
  if (IscpMemDescriptor.Size0 == 0) {
    DEBUG ((EFI_D_ERROR, "Warning: Could not get SystemMemorySize via ISCP, using default value.\n"));
  }

  DEBUG ((EFI_D_ERROR, "SystemMemorySize = %ld\n", PcdGet64 (PcdSystemMemorySize)));


  if (FixedPcdGetBool (PcdXgbeEnable)) {
    // Get MAC Address from ISCP
    Status = PeiIscpPpi->ExecuteGetMacAddressTransaction (
               PeiServices, &MacAddrInfo );
    ASSERT_EFI_ERROR (Status);

    // Check for bogus MAC addresses that have the multicast bit set. This
    // includes FF:FF:FF:FF:FF:FF, which is what you get from the SCP on
    // some versions of the B0 Overdrive
    MacSize = sizeof(MacAddrInfo.MacAddress0);
    if ((MacAddrInfo.MacAddress0[0] & 0x1) == 0x0) {
      Status = PcdSetPtrS (PcdEthMacA, &MacSize, MacAddrInfo.MacAddress0);
      ASSERT_EFI_ERROR (Status);
    }
    if ((MacAddrInfo.MacAddress1[0] & 0x1) == 0x0) {
      Status = PcdSetPtrS (PcdEthMacB, &MacSize, MacAddrInfo.MacAddress1);
      ASSERT_EFI_ERROR (Status);
    }

    DEBUG ((EFI_D_ERROR, "EthMacA = %02x:%02x:%02x:%02x:%02x:%02x\n",
      ((UINT8 *)PcdGetPtr (PcdEthMacA))[0], ((UINT8 *)PcdGetPtr (PcdEthMacA))[1],
      ((UINT8 *)PcdGetPtr (PcdEthMacA))[2], ((UINT8 *)PcdGetPtr (PcdEthMacA))[3],
      ((UINT8 *)PcdGetPtr (PcdEthMacA))[4], ((UINT8 *)PcdGetPtr (PcdEthMacA))[5]));
    DEBUG ((EFI_D_ERROR, "EthMacB = %02x:%02x:%02x:%02x:%02x:%02x\n",
      ((UINT8 *)PcdGetPtr (PcdEthMacB))[0], ((UINT8 *)PcdGetPtr (PcdEthMacB))[1],
      ((UINT8 *)PcdGetPtr (PcdEthMacB))[2], ((UINT8 *)PcdGetPtr (PcdEthMacB))[3],
      ((UINT8 *)PcdGetPtr (PcdEthMacB))[4], ((UINT8 *)PcdGetPtr (PcdEthMacB))[5]));
  }

  // Let other PEI modules know we're done!
  Status = (*PeiServices)->InstallPpi (PeiServices, &mPlatInitPpiDescriptor);
  ASSERT_EFI_ERROR (Status);

  return Status;
}

