/** @file
  This files contains PCH CNVi devices configuration

@copyright
 Copyright (c) 2015 - 2019 Intel Corporation. All rights reserved
 This software and associated documentation (if any) is furnished
 under a license and may only be used or copied in accordance
 with the terms of the license. Except as permitted by the
 license, no part of this software or documentation may be
 reproduced, stored in a retrieval system, or transmitted in any
 form or by any means without the express written consent of
 Intel Corporation.
 This file contains an 'Intel Peripheral Driver' and is uniquely
 identified as "Intel Reference Module" and is licensed for Intel
 CPUs and chipsets under the terms of your license agreement with
 Intel or your vendor. This file may be modified by the user, subject
 to additional terms of the license agreement.

@par Specification Reference:
**/

#if (CNVI_ENABLE == 1)
#include "ScInitPei.h"
#include <Private/Library/ScCnviInit.h>
#include <Private/Library/GpioPrivateLib.h>
#include <Library/PciLib.h>

/**
  Detect CNVi  WiFi and BT
**/
BOOLEAN
ScCnviDetect ( 
   VOID
   )
{
    UINTN           CnviWifiBaseAddress;   
    
    CnviWifiBaseAddress = MmPciBase (
             DEFAULT_PCI_BUS_NUMBER_SC,
             PCI_DEVICE_NUMBER_PCH_CNVI_WIFI,
             PCI_FUNCTION_NUMBER_PCH_CNVI_WIFI
             );    
    DEBUG ((DEBUG_INFO, "CNVi WiFi Device ID = 0x%08x.\n", MmioRead16 (CnviWifiBaseAddress + PCI_DEVICE_ID_OFFSET)));    
    
    if (MmioRead16 (CnviWifiBaseAddress) == 0xFFFF) { 
        return FALSE;
    }
    else {
        return TRUE;
    }  
}

/**
  This procedure will disable CNVi WiFi device at PSF level

  @param[in] None

  @retval None
**/
/*VOID
PsfDisableCnviWifiDevice (
  VOID
  )
{
  UINT16 PsfRegBase;
  
  PsfRegBase = R_PCH_LP_PCR_PSF3_T0_SHDW_CNVI_REG_BASE;
  //R_SC_PCH_PCR_PSF3_T0_SHDW_PMC_PMC_REG_BASE
  SideBandAndThenOr32 (
    SB_PORTID_PSF3,
    PsfRegBase + R_SC_PCH_PCR_PSFX_T0_SHDW_PCIEN,
    0xFFFFFFFF,
    B_SC_PCH_PCR_PSFX_T0_SHDW_PCIEN_FUNDIS
    );
} */

/**
  Disable CNVi
**/
VOID
ScDisableCnvi (
  UINT32    *FuncDisable1Reg  )
{
    UINT16 CmdReg;
    UINT16 DisableBits;    
    
    DisableBits = BIT2 | BIT1;
    DEBUG ((DEBUG_INFO, "ScDisableCnvi() - Start\n"));
    
    CmdReg = PciRead16 (PCI_LIB_ADDRESS (
                            DEFAULT_PCI_BUS_NUMBER_SC,
                            PCI_DEVICE_NUMBER_PCH_CNVI_WIFI,
                            PCI_FUNCTION_NUMBER_PCH_CNVI_WIFI, 
                            PCI_CMD)
                            );    
	if (CmdReg & DisableBits )	{					
	CmdReg = CmdReg &  ~DisableBits ;
	PciWrite16( PCI_LIB_ADDRESS (
	                DEFAULT_PCI_BUS_NUMBER_SC, 
	                PCI_DEVICE_NUMBER_PCH_CNVI_WIFI, 
	                PCI_FUNCTION_NUMBER_PCH_CNVI_WIFI, 
	                PCI_CMD),
	                CmdReg
	                ); 
	
	}
  //
  // Perform PSF function disable
  //
    DEBUG ((DEBUG_INFO, "PSF function disable\n"));
  //PsfDisableCnviWifiDevice ();
   
   DEBUG ((DEBUG_INFO, "CNVi WiFi found enabled and requested to be disabled - set PG bit and issue reset!\n"));
   *FuncDisable1Reg |= (UINT32)B_SC_PMC_MEM_FUNC_DIS_CNVI;
}

/**
  Configure CNVi BT
**/
VOID
ScCnviConfigureBt (
  IN SC_CNVI_CONFIG    *CnviConfig
  )
{
  if (CnviConfig->BtCore == 1) {
    switch (CnviConfig->BtInterface) {
      case (CnviBtIfUart): // BT over UART
        DEBUG ((DEBUG_INFO, "Set BT interface: UART\n"));
        GpioSetCnviBtInterface (GpioCnviBtIfUart);
        GpioConfigureCnviBtHostWakeInt ();
        break;
      case (CnviBtIfUsb): // BT over USB
        DEBUG ((DEBUG_INFO, "Set BT interface: USB\n"));
        GpioSetCnviBtInterface (GpioCnviBtIfUsb);
        break;
      default:
        ASSERT (FALSE);
        return;
    }
    
    DEBUG ((DEBUG_INFO, "Enable BT core\n"));
    GpioSetCnviBtEnState (1);
  } else {
    //
    // Disable BT core
    //
    DEBUG ((DEBUG_INFO, "Disable BT core\n"));
    GpioSetCnviBtEnState (0);
  }
  return;
}
/**
  Initialize CNVi devices according to PCH Policy PPI

  @param[in] PchPolicyPpi The PCH Policy PPI instance

  @retval EFI_SUCCESS             Succeeds.
**/
EFI_STATUS
ScCnviInit (
  IN  SC_POLICY_PPI     *ScPolicyPpi,
  IN UINT32              OriginalFuncDisable1Reg,           
  OUT UINT32            *FuncDisable1Reg               
  )
{
  UINT32                 Status;
  BOOLEAN                CnviDetect;
  SC_CNVI_CONFIG         *CnviConfig;
  DEBUG ((DEBUG_INFO, "ScCnviInit() - Start\n"));
  
  if (OriginalFuncDisable1Reg & B_SC_PMC_MEM_FUNC_DIS_CNVI) {
	  *FuncDisable1Reg = *FuncDisable1Reg | (UINT32)B_SC_PMC_MEM_FUNC_DIS_CNVI;
	} else {
	  *FuncDisable1Reg = *FuncDisable1Reg & (UINT32)~B_SC_PMC_MEM_FUNC_DIS_CNVI;
    }
  CnviDetect = FALSE;
  Status = GetConfigBlock ((VOID *) ScPolicyPpi, &gCnviConfigGuid, (VOID *) &CnviConfig);
  ASSERT_EFI_ERROR (Status);

  //
  // Care needs to be taken as CNVi connection depends on other devices like SerialIo or ISH
  //  
  
  if (CnviConfig->Mode == CnviModeAuto) {
      
    //
    // Check if CNVi WiFi PCI function is visible - that means CNVi is the selected connectivity solution
    //
     
    CnviDetect = ScCnviDetect();      
    if ( !CnviDetect ) {
        
		if (OriginalFuncDisable1Reg & B_SC_PMC_MEM_FUNC_DIS_CNVI) {
            
            DEBUG ((DEBUG_INFO, "CNVi WiFi found disabled and requested to be enabled - clear PG bit in PMC and issue reset!\n"));
            //
            // CNVi PMC function disable 1 register cnv bit(bit 1) made 0 to enable CNVi
            //
			*FuncDisable1Reg = *FuncDisable1Reg & (UINT32)~B_SC_PMC_MEM_FUNC_DIS_CNVI;
             return EFI_SUCCESS;
             } 
        else {
             DEBUG ((DEBUG_INFO, "CNVi WiFi function not found and Discreet component may be connected.\n")); 
             return EFI_SUCCESS;
             }
         }    
    //
    // Configuring RF related GPIO
    //
    DEBUG ((DEBUG_INFO, "Configure GRI and BRI\n"));
    if (CnviConfig->ModifyCrfGpios == 0) {
      //
      // Configure CNVi RF::: for wifi, BT and GNSS
      //
      GpioConfigureCnviCrfConnection(TRUE); 
    } else {
      //
      // Configure CNVi RF::: configuration of GNSS and BtOnUart gpios will not be done
      //
      GpioConfigureCnviCrfConnection(FALSE);
    }
      
    //
    // Configure WiFi Core mode to host accessible
    //
    GpioSetWifiHostAccessMode (GpioCnviWiFiHostaccessable);
    
    //
    // Configure BT
    //
    ScCnviConfigureBt(CnviConfig);


    //
    // Configure LTR Enable
    //
    
    ScCnviLTREnbale();

    //
    // Configure conditional wake states
    //
    ConfigureSxWake( ScPolicyPpi, CnviConfig->DisableSxWake);
    
    } else {

      CnviDetect = ScCnviDetect();     
      if(CnviDetect) {
          DEBUG ((DEBUG_INFO, "Configure GRI and BRI\n"));
          if (CnviConfig->ModifyCrfGpios == 0) {
      	    //
            // Configure CNVi RF::: for wifi, BT and GNSS
            //
            GpioConfigureCnviCrfConnection(TRUE); 
          } else {
            //
            // Configure CNVi RF::: configuration of GNSS and BtOnUart gpios will not be done
            //
            GpioConfigureCnviCrfConnection(FALSE);
          }
          DEBUG ((DEBUG_INFO, "Force CNVi disable \n"));
		  GpioSetCnviBtEnState (0);
          ScDisableCnvi (FuncDisable1Reg);
          }
      else
      {
		  if (OriginalFuncDisable1Reg & B_SC_PMC_MEM_FUNC_DIS_CNVI) {
                DEBUG ((DEBUG_INFO, "Configure GRI and BRI  \n"));
                if (CnviConfig->ModifyCrfGpios == 0) {
                  //
                  // Configure CNVi RF::: for wifi, BT and GNSS
                  //
                  GpioConfigureCnviCrfConnection(TRUE); 
                } else {
                  //
                  // Configure CNVi RF::: configuration of GNSS and BtOnUart gpios will not be done
                  //
                  GpioConfigureCnviCrfConnection(FALSE);
                }
               }                    
          }
      } 
  DEBUG ((DEBUG_INFO, "ScCnviInit() - End\n"));
  return EFI_SUCCESS;
}

BOOLEAN
ScCnviLTREnbale(
VOID
)
{
	
	UINT32    gioDevCap, gioDevCap2;
	gioDevCap = PciRead32(PCI_LIB_ADDRESS(
		DEFAULT_PCI_BUS_NUMBER_SC,
		PCI_DEVICE_NUMBER_PCH_CNVI_WIFI,
		PCI_FUNCTION_NUMBER_PCH_CNVI_WIFI,
		R_SC_PCI_CNVI_GIO_DEV)
		);
	DEBUG((DEBUG_INFO, "CNVi LTR offset 0x64 = 0x%x.\n", gioDevCap));

	if (gioDevCap & B_SC_PCI_CNVI_LTR_MEC_SUP)	{


		gioDevCap2 = PciRead32(PCI_LIB_ADDRESS(
			DEFAULT_PCI_BUS_NUMBER_SC,
			PCI_DEVICE_NUMBER_PCH_CNVI_WIFI,
			PCI_FUNCTION_NUMBER_PCH_CNVI_WIFI,
			R_SC_PCI_CNVI_GIO_DEV_2)
			);
		gioDevCap2 = gioDevCap2 | B_SC_PCI_CNVI_LTR_MEC_ENB;

		DEBUG((DEBUG_INFO, "CNVi LTR offset 0x68= 0x%x.\n", gioDevCap2));

		PciWrite32(PCI_LIB_ADDRESS(
			DEFAULT_PCI_BUS_NUMBER_SC,
			PCI_DEVICE_NUMBER_PCH_CNVI_WIFI,
			PCI_FUNCTION_NUMBER_PCH_CNVI_WIFI,
			R_SC_PCI_CNVI_GIO_DEV_2),
			gioDevCap2
			);

		gioDevCap2 = PciRead32(PCI_LIB_ADDRESS(
			DEFAULT_PCI_BUS_NUMBER_SC,
			PCI_DEVICE_NUMBER_PCH_CNVI_WIFI,
			PCI_FUNCTION_NUMBER_PCH_CNVI_WIFI,
			R_SC_PCI_CNVI_GIO_DEV_2)
			);

		DEBUG((DEBUG_INFO, "CNVi LTR Enabled offset 0x68= 0x%x.\n", gioDevCap2));
	}
	else
	{
		DEBUG((DEBUG_INFO, "CNVi LTR Mechanism not supported n"));
		return FALSE;
	}
	return TRUE;
}



/**
  Configure Sx wake states according to User setup option

  @param[in] UINT32  Sx wake state oprion set by user 

  @retval VOID.
**/
VOID
ConfigureSxWake(
  IN  SC_POLICY_PPI     *ScPolicyPpi,
  IN  UINT32            DisableSxWake
)
{
  UINT32                            PmcBase;
  UINT32                            BiosScratchPadReg;
  SC_GENERAL_CONFIG                 *GeneralConfig;
  EFI_STATUS                        Status;

  Status = GetConfigBlock ((VOID *) ScPolicyPpi, &gScGeneralConfigGuid, (VOID *) &GeneralConfig);
  ASSERT_EFI_ERROR (Status);

  PmcBase         = GeneralConfig->PmcBase;
  BiosScratchPadReg  = MmioRead32 (PmcBase+R_GCR_BASE+R_SC_PMC_BIOS_SCRATCHPAD );

  //Default Bit2 Scrathpad would be cleared
  BiosScratchPadReg = BiosScratchPadReg & (UINT32) ~BIT2;
  MmioWrite32((PmcBase+R_GCR_BASE+R_SC_PMC_BIOS_SCRATCHPAD), BiosScratchPadReg);


  //
  // configure the pmc.gcr.bios_scratchpad[2] based on setup option.
  // If CNVi - Wake from all Sx is enabled setting pmc.gcr.bios_scratchpad[2] to 0
  // If disabled setting pmc.gcr.bios_scratchpad[2] to 1
  // Note: pmc.gcr.bios_scratchpad[2], When set, PMC FW will send ForcePwrGatePOK and assert cnv_pgcb_rst on SX Entry
  //
  if(DisableSxWake == 1){
	  BiosScratchPadReg = BiosScratchPadReg | (UINT32) BIT2;
  } else {
	  BiosScratchPadReg = BiosScratchPadReg & (UINT32) ~BIT2;
  }
   MmioWrite32((PmcBase+R_GCR_BASE+R_SC_PMC_BIOS_SCRATCHPAD), BiosScratchPadReg);
}
#endif
