/** @file
  This files contains SoC South Cluster CNVi devices configuration

@copyright
 Copyright (c) 2017 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 <Uefi/UefiBaseType.h>
#include <Library/DebugLib.h>
#include <Library/MmPciLib.h>
#include <Library/TimerLib.h>
#include <Library/S3BootScriptLib.h>
#include <Library/SteppingLib.h>

#include <Library/GpioLib.h>
#include <Library/GlkGpioLib.h>
#include <Private/Library/GpioPrivateLib.h>


#include "ScAccess.h"
#include "ScRegs/RegsCnvi.h"

/**
  This function returns CNViBAR through CnviBaseAddress

  @param[in] None

  @retval CnviBar0
**/
UINTN
getCnviWiFiBaseAddr (
    VOID
)
{
  UINTN    CnviWiFiBaseAddr  =
                      MmPciBase (
                        DEFAULT_PCI_BUS_NUMBER_SC,
                        PCI_DEVICE_NUMBER_PCH_CNVI_WIFI,
                        PCI_FUNCTION_NUMBER_PCH_CNVI_WIFI
                      );

  if(MmioRead32(CnviWiFiBaseAddr) == 0xFFFFFFFF){
    DEBUG((DEBUG_ERROR, "GLK SC CNVi does not exist...\n"));
    return EFI_NO_MEDIA;
  }
  return CnviWiFiBaseAddr;
}

/**
  This function returns CNViBAR through CnviBaseAddress

  @param[in] None

  @retval CnviBar0
**/
UINTN
getCnviBar (

  )
{
  volatile  UINT32    Bar0L, Bar0U;
  UINTN                CnviWiFiBaseAddr = getCnviWiFiBaseAddr();

  if(CnviWiFiBaseAddr == EFI_NO_MEDIA){
    return 0xFFFFFFFF;
  }
  //obtain the CNVi WiFi MMIO region address
  Bar0L = MmioRead32(CnviWiFiBaseAddr + 0x10);
  if(Bar0L & 4){  //64b BAR
    Bar0U = MmioRead32(CnviWiFiBaseAddr + 0x14);
    if(Bar0U){  //BAR0 is greater than 32b, need to scale down to 32b for S3 flow
      //current implementation is that PCI resource allocation is all 32-bit MMIO space
      ASSERT_EFI_ERROR (Bar0U);  //assert to indicate change required to support 64-bit PCI support
    }
  }
  Bar0L &= 0xFFFFFFF0;
  if( !Bar0L) {    //exit if CNVi BAR not initialized
    DEBUG((DEBUG_ERROR, "CNVi WiFi BAR0 not init....exiting!\n"));
    return 0xFFFFFFFF;
  } else {
    DEBUG((DEBUG_INFO, "CNVi WiFi BAR0: 0x%x\n", Bar0L));
  }
  return   Bar0L;

}

/**
  This function implements a fix for PID 1207523735/1604193853

   @param[in]  None

   @retval     None
**/
VOID
ScCNViPMEDisableWA (
    UINT8 S3flag
)
{
  //
  // GLK bugeco 1207523735: After sending PME message vnn_req remains asserted
  //  [cloned to 1604193853] -  need to set the specific PME idle disable in HOST_CHICKEN_CSR_AD.PM_IDLE_SRC_DIS
  //                field, HPET CSR 0x204 bit 19. When set, the PME status will not prevent
  //                the VNN request to deassert
  //
  UINTN                CnviBar0,CnviWiFiBaseAddr;

  volatile  UINT32    CmdSts=0, ddata;

  if ((GetBxtSeries() == Glk) && (BxtStepping() < GlkB0) ) {

    CnviWiFiBaseAddr = getCnviWiFiBaseAddr ();
    if( CnviWiFiBaseAddr == EFI_NO_MEDIA) {
      return;
    }
    CnviBar0=getCnviBar();

    if( CnviBar0 == 0xFFFFFFFF) {
      return;
    }

    CmdSts = MmioRead32(CnviWiFiBaseAddr + 4);
    ddata = CmdSts;          //save the orignal state of the CNVi WiFi PCI device
    if( !(CmdSts & BIT1)){
      ddata |= BIT1;
    }
    if( !(CmdSts & BIT2)){
      ddata |= BIT2;
    }
    MmioWrite32(CnviWiFiBaseAddr + 4, ddata);
    if (S3flag == 1) {
      S3BootScriptSaveMemWrite (
          S3BootScriptWidthUint32,
          CnviWiFiBaseAddr + 4,
          1,
          (VOID*)&ddata
      );
    }

    //
    // GLK bugeco 1207523735: After sending PME message vnn_req remains asserted
    //    [cloned to 1604193853] -  need to set the specific PME idle disable in HOST_CHICKEN_CSR_AD.PM_IDLE_SRC_DIS
    //                field, HPET CSR 0x204 bit 19. When set, the PME status will not prevent
    //                the VNN request to deassert
    ddata = MmioOr32(CnviBar0 + R_SC_CNVI_WIFI_HOST_CHICKEN_CSR_AD, B_SC_CNVI_WIFI_PM_IDLE_SRC_DIS_SB_PME);
    if (S3flag == 1) {

    S3BootScriptSaveMemWrite (
        S3BootScriptWidthUint32,
              CnviBar0 + R_SC_CNVI_WIFI_HOST_CHICKEN_CSR_AD,
              1,
              (VOID*)&ddata
              );
      }
    DEBUG((DEBUG_INFO, "CNVi WiFi BAR0+R_SC_CNVI_WIFI_HOST_CHICKEN_CSR_AD: 0x%x\n",
        MmioRead32(CnviBar0 + R_SC_CNVI_WIFI_HOST_CHICKEN_CSR_AD) ));

    //restore the state of the CNVi WiFi PCI device
    MmioWrite32(CnviWiFiBaseAddr + 4, CmdSts);
    if (S3flag == 1) {
      S3BootScriptSaveMemWrite (
          S3BootScriptWidthUint32,
          CnviWiFiBaseAddr + 4,
          1,
          (VOID*)&CmdSts
      );
    }
  }
  return;
}

/**
  This function implements a fix for Shutdown global reset with JfP inserted

  @param[in]  S3flag

  @retval     None
**/
VOID
ScCNViJFPGlobalResetWA (
    UINT8 S3flag
)
{

  UINTN              CnviBar0,CnviWiFiBaseAddr;
  volatile UINT32    ddata,CmdSts=0;
  UINT32             cmd;
  UINT32             BitMask, Bitvalue;
  UINT32             Rfid;

  CnviWiFiBaseAddr = getCnviWiFiBaseAddr ();
  if( CnviWiFiBaseAddr == EFI_NO_MEDIA) {
    return;
  }
  CnviBar0=getCnviBar();
  if( CnviBar0 == 0xFFFFFFFF) {
    return;
  }

  //
  // GLK sighting 1208981478 : CNVi: Shutdown global reset with JfP inserted
  //  [cloned to 1604230335]
  //
  //   step 1) Force MAC to wake via MMIO read-modify-write to CNVI_WIFI_GP_CTRL_CSR_AD. Set bits 2 and 3
  // Get the BAR from PCI

    // W/A should be applied only for ES1/ES2, ignored for ES3
    // ES3 stepping
    // Step (A - 0x0, B - 0x1, …) ES1/2==A step, ES3=B step

  CmdSts = MmioRead32(CnviWiFiBaseAddr + 4);
  ddata = CmdSts;          //save the orignal state of the CNVi WiFi PCI device
  if( !(CmdSts & BIT1)){
    ddata |= BIT1;
  }
  if( !(CmdSts & BIT2)){
    ddata |= BIT2;
  }
  MmioWrite32(CnviWiFiBaseAddr + 4, ddata);

    Rfid=MmioRead32(CnviBar0 + R_SC_CNVI_MEM_WIFI_RF_ID_VAL);



  if((((Rfid & 0x00000F00)>>8)< B_SC_CNVI_MEM_WIFI_RF_ID_VAL_B_STEP) && (((Rfid & 0x00FFF000)>>12) == B_SC_CNVI_MEM_WIFI_RF_ID_VAL_PRODUCT_TYPE))
  {


  if (S3flag == 1) {
    S3BootScriptSaveMemWrite (
        S3BootScriptWidthUint32,
        CnviWiFiBaseAddr + 4,
        1,
        (VOID*)&ddata
    );
  }

  ddata = MmioOr32(CnviBar0 + R_SC_CNVI_MEM_WIFI_GP_CTRL_CSR_AD, B_SC_CNVI_MEM_WIFI_HS_MAC_ACC_REQ + B_SC_CNVI_MEM_WIFI_INIT_DONE);
  if (S3flag == 1) {
    S3BootScriptSaveMemWrite (
        S3BootScriptWidthUint32,
        CnviBar0 + R_SC_CNVI_MEM_WIFI_GP_CTRL_CSR_AD,
        1,
        (VOID*)&ddata
    );
  }
  DEBUG((DEBUG_INFO, "CNVi WiFi BAR0+R_SC_CNVI_MEM_WIFI_GP_CTRL_CSR_AD: 0x%x\n",
      MmioRead32(CnviBar0 + R_SC_CNVI_MEM_WIFI_GP_CTRL_CSR_AD) ));

  // step 2) Wait for MAC to be up - poll on CNV_WIFI_GP_CTRL_CSR[0] (MAC_CLK_RDY_0) to be set
  //        a) Poll for 1 ms; if MAC not ready in 1 ms then timeout with global reset
  {
    UINTN    TimeOut = 100;

    while( !(MmioRead32(CnviBar0 + R_SC_CNVI_MEM_WIFI_GP_CTRL_CSR_AD) & B_SC_CNVI_MEM_WIFI_MAC_CLK_RDY_0)){
      MicroSecondDelay(100);
      TimeOut--;
      if( !TimeOut){  //time out if it past 1ms
        break;
      }
    }
    if( !TimeOut){
      DEBUG((DEBUG_ERROR, "Not able to rectify shutdown global reset issue with CNVi...\n"));
      return;
    }

    if (S3flag == 1) {
      BitMask = MmioRead32(CnviBar0 + R_SC_CNVI_MEM_WIFI_GP_CTRL_CSR_AD);
      Bitvalue = BitMask;
#if 0
      S3BootScriptSaveMemPoll (
          S3BootScriptWidthUint32,
          CnviBar0 + R_SC_CNVI_MEM_WIFI_GP_CTRL_CSR_AD,
          &BitMask,
          &Bitvalue,
          100,
          100
      );
#else
   S3BootScriptSaveStall (10 * 1000);
#endif
    }
  }

  //
  // step 3) Read-modify-write sequence to clear HPM_HIPM_GEN_CFG[10] - this is force active bit
  //      a) Setup read command via write to CNVI_WIFI_HBUS_TARG_PRPH_RADDR
  //      b) Read CNVI_WIFI_HBUS_TARG_PRPH_RDAT to get read return contents
  //      c) Modify read return contents and clear bit 10 for force active
  //      d) Setup write data command via write to CNVI_WIFI_HBUS_TARG_PRPH_WADDR
  //
  //      e) Send write data and execute write command via write to CNVI_WIFI_HBUS_TARG_PRPH_WDAT
  //    //
  {
    UINT32  cmd = 0;
    cmd = (3 << N_SC_CNVI_MEM_WIFI_HBUS_HPBR_SIZE) | (A_SC_CNVI_MEM_WIFI_HPM_HIPM_GEN_CFG << N_SC_CNVI_MEM_WIFI_HBUS_HPBR_ADDR);
    MmioWrite32(CnviBar0 + R_SC_CNVI_MEM_WIFI_HBUS_TARG_PRPH_RADDR, cmd);
    ddata = MmioRead32(CnviBar0 + R_SC_CNVI_MEM_WIFI_HBUS_TARG_PRPH_RDAT);
    DEBUG((DEBUG_INFO, "(pre) HPM_HIPM_GEN_CFG: 0x%x\n", ddata));
    ddata &= ~BIT10;  //clear bit 10 for force active

    MmioWrite32(CnviBar0 + R_SC_CNVI_MEM_WIFI_HBUS_TARG_PRPH_WADDR, cmd);
    MmioWrite32(CnviBar0 + R_SC_CNVI_MEM_WIFI_HBUS_TARG_PRPH_WDAT, ddata);
    if (S3flag == 1) {
      S3BootScriptSaveMemWrite (
          S3BootScriptWidthUint32,
          CnviBar0 + R_SC_CNVI_MEM_WIFI_HBUS_TARG_PRPH_WADDR,
          1,
          &cmd
      );
      S3BootScriptSaveMemWrite (
          S3BootScriptWidthUint32,
          CnviBar0 + R_SC_CNVI_MEM_WIFI_HBUS_TARG_PRPH_WDAT,
          1,
          (VOID*)&ddata
      );
    }
    MmioWrite32(CnviBar0 + R_SC_CNVI_MEM_WIFI_HBUS_TARG_PRPH_RADDR, cmd);
    ddata = MmioRead32(CnviBar0 + R_SC_CNVI_MEM_WIFI_HBUS_TARG_PRPH_RDAT);
    DEBUG((DEBUG_INFO, "{post} HPM_HIPM_GEN_CFG: 0x%x\n", ddata));
  }

  //
  // step 4) Reset MAC by setting CNVI_WIFI_RESET_CSR_AD.CNT_RST_REQ
  //
  ddata = MmioOr32(CnviBar0 + R_SC_CNVI_MEM_WIFI_RESET_CSR_AD, B_SC_CNVI_MEM_WIFI_CNT_RST_REQ);
  if (S3flag == 1) {
    S3BootScriptSaveMemWrite (
        S3BootScriptWidthUint32,
        CnviBar0 + R_SC_CNVI_MEM_WIFI_RESET_CSR_AD,
        1,
        (VOID*)&ddata
    );
  }

  while( (MmioRead32(CnviBar0 + R_SC_CNVI_MEM_WIFI_RESET_CSR_AD) & B_SC_CNVI_MEM_WIFI_CNT_RST_REQ) ){
    MicroSecondDelay(100);
  }

  if (S3flag == 1) {
    BitMask = MmioRead32(CnviBar0 + R_SC_CNVI_MEM_WIFI_RESET_CSR_AD) | B_SC_CNVI_MEM_WIFI_CNT_RST_REQ;
    Bitvalue = BitMask & ~B_SC_CNVI_MEM_WIFI_CNT_RST_REQ;
#if 0
    S3BootScriptSaveMemPoll (
        S3BootScriptWidthUint32,
        CnviBar0 + R_SC_CNVI_MEM_WIFI_RESET_CSR_AD,
        &BitMask,
        &Bitvalue,
        100,
        100
    );
#else
  S3BootScriptSaveStall (10 * 1000);
#endif

  }

  //
  //re-check the Si WA bit
  //
  cmd = (3 << N_SC_CNVI_MEM_WIFI_HBUS_HPBR_SIZE) | (A_SC_CNVI_MEM_WIFI_HPM_HIPM_GEN_CFG << N_SC_CNVI_MEM_WIFI_HBUS_HPBR_ADDR);
  MmioWrite32(CnviBar0 + R_SC_CNVI_MEM_WIFI_HBUS_TARG_PRPH_RADDR, cmd);
  ddata = MmioRead32(CnviBar0 + R_SC_CNVI_MEM_WIFI_HBUS_TARG_PRPH_RDAT);
  DEBUG((DEBUG_INFO, "{post MAC reset} HPM_HIPM_GEN_CFG: 0x%x\n", ddata));

  if (S3flag == 1) {
    S3BootScriptSaveMemWrite (
        S3BootScriptWidthUint32,
        CnviWiFiBaseAddr + 4,
        1,
        (VOID*)&CmdSts
    );
  }
    }
  //restore the state of the CNVi WiFi PCI device
  MmioWrite32(CnviWiFiBaseAddr + 4, CmdSts);

}

VOID
ScBTLow(UINT8 S3flag)
{
  UINTN			   CnviWiFiBaseAddr;
  BXT_GPIO_CONFIG Gpio_Conf_Data;
  UINT32      CommAndOffset;

  CnviWiFiBaseAddr = getCnviWiFiBaseAddr ();
	if( CnviWiFiBaseAddr == EFI_NO_MEDIA) {
	  return;
		}
  CommAndOffset          = NW_VGPIO_0;
  Gpio_Conf_Data.CommAndOffset   = CommAndOffset;
  Gpio_Conf_Data.padConfg0.padCnf0 = GpioPadRead(CommAndOffset);
  Gpio_Conf_Data.padConfg1.padCnf1 = GpioPadRead(CommAndOffset + BXT_GPIO_PAD_CONF1_OFFSET);
  Gpio_Conf_Data.HostSw       = GPIO_D;
  Gpio_Conf_Data.WakeEnabled     = Wake_Disabled;
  Gpio_Conf_Data.padConfg0.r.PMode     = GPIO;
  Gpio_Conf_Data.padConfg0.r.GPIORxTxDis = GPO;
  Gpio_Conf_Data.padConfg0.r.GPIOTxState   =  LO;
  GpioPadConfigItem(Gpio_Conf_Data);

}



#endif
