/**
 * This function initializes the processor and updates the cluster id
 * which indicates CPU on which fsbl is running
 *
 * @param	FsblInstancePtr is pointer to the XFsbl Instance
 *
 * @return	returns the error codes described in xfsbl_error.h on any error
 * 			returns XFSBL_SUCCESS on success
 *
 ******************************************************************************/
static u32 XFsbl_ProcessorInit(XFsblPs * FsblInstancePtr)
{
	u32 Status = XFSBL_SUCCESS;
	//u64 ClusterId=0U;
	PTRSIZE ClusterId=0U;
	u32 RegValue;
	u32 Index=0U;

	/**
	 * Read the cluster ID and Update the Processor ID
	 * Initialize the processor settings that are not done in
	 * BSP startup code
	 */
#ifdef ARMA53_64
	ClusterId = mfcp(MPIDR_EL1);
#else
	ClusterId = mfcp(XREG_CP15_MULTI_PROC_AFFINITY);
#endif

	XFsbl_Printf(DEBUG_INFO,"Cluster ID 0x%0lx\n\r", ClusterId);

	if (XGet_Zynq_UltraMp_Platform_info() == XPLAT_ZYNQ_ULTRA_MPQEMU) {
		/**
		 * Remmaping for R5 in QEMU
		 */
		if (ClusterId == 0x80000004U) {
			ClusterId = 0xC0000100U;
		} else if (ClusterId == 0x80000005U) {
			/* this corresponds to R5-1 */
			Status = XFSBL_ERROR_UNSUPPORTED_CLUSTER_ID;
			XFsbl_Printf(DEBUG_GENERAL,
					"XFSBL_ERROR_UNSUPPORTED_CLUSTER_ID\n\r");
			goto END;
		} else {
			/* For MISRA C compliance */
		}
	}

	/* store the processor ID based on the cluster ID */
	if ((ClusterId & XFSBL_CLUSTER_ID_MASK) == XFSBL_A53_PROCESSOR) {
		XFsbl_Printf(DEBUG_GENERAL,"Running on A53-0 ");
		FsblInstancePtr->ProcessorID =
				XIH_PH_ATTRB_DEST_CPU_A53_0;
#ifdef __aarch64__
		/* Running on A53 64-bit */
		XFsbl_Printf(DEBUG_GENERAL,"(64-bit) Processor \n\r");
		FsblInstancePtr->A53ExecState = XIH_PH_ATTRB_A53_EXEC_ST_AA64;
#else
		/* Running on A53 32-bit */
		XFsbl_Printf(DEBUG_GENERAL,"(32-bit) Processor \n\r");
		FsblInstancePtr->A53ExecState = XIH_PH_ATTRB_A53_EXEC_ST_AA32;
#endif

	} else if ((ClusterId & XFSBL_CLUSTER_ID_MASK) == XFSBL_R5_PROCESSOR) {
		/* A53ExecState is not valid for R5 */
		FsblInstancePtr->A53ExecState = XIH_INVALID_EXEC_ST;

		RegValue = XFsbl_In32(RPU_RPU_GLBL_CNTL);
		if ((RegValue & RPU_RPU_GLBL_CNTL_SLSPLIT_MASK) == 0U) {
			XFsbl_Printf(DEBUG_GENERAL,
				"Running on R5 Processor in Lockstep \n\r");
			FsblInstancePtr->ProcessorID =
				XIH_PH_ATTRB_DEST_CPU_R5_L;
		} else {
			XFsbl_Printf(DEBUG_GENERAL,
				"Running on R5-0 Processor \n\r");
			FsblInstancePtr->ProcessorID =
				XIH_PH_ATTRB_DEST_CPU_R5_0;
		}

		/**
		 * Update the Vector locations in R5 TCM
		 */
		while (Index<32U) {
			XFsbl_Out32(Index, XFSBL_R5_VECTOR_VALUE);
			Index += 4;
		}

	} else {
		Status = XFSBL_ERROR_UNSUPPORTED_CLUSTER_ID;
		XFsbl_Printf(DEBUG_GENERAL,
				"XFSBL_ERROR_UNSUPPORTED_CLUSTER_ID\n\r");
		goto END;
	}

	/**
	 * Register the exception handlers
	 */
	XFsbl_RegisterHandlers();

	/* Prints for the perf measurement */
#ifdef XFSBL_PERF

#if !defined(ARMR5)
	if (FsblInstancePtr->ProcessorID == XIH_PH_ATTRB_DEST_CPU_A53_0) {
		XFsbl_Printf(DEBUG_PRINT_ALWAYS, "Proc: A53-0 Freq: %d Hz",
				XPAR_CPU_CORTEXA53_0_CPU_CLK_FREQ_HZ);

		if (FsblInstancePtr->A53ExecState == XIH_PH_ATTRB_A53_EXEC_ST_AA32) {
			XFsbl_Printf(DEBUG_PRINT_ALWAYS, " Arch: 32 \r\n");
		}
		else if (FsblInstancePtr->A53ExecState ==
				XIH_PH_ATTRB_A53_EXEC_ST_AA64) {
			XFsbl_Printf(DEBUG_PRINT_ALWAYS, " Arch: 64 \r\n");
		}
	}
#else
	if (FsblInstancePtr->ProcessorID == XIH_PH_ATTRB_DEST_CPU_R5_0) {
		XFsbl_Printf(DEBUG_PRINT_ALWAYS, "Proc: R5-0 Freq: %d Hz \r\n",
				XPAR_PSU_CORTEXR5_0_CPU_CLK_FREQ_HZ)
	}
	else if (FsblInstancePtr->ProcessorID == XIH_PH_ATTRB_DEST_CPU_R5_L) {
		XFsbl_Printf(DEBUG_PRINT_ALWAYS, "Proc: R5-Lockstep "
			"Freq: %d Hz \r\n", XPAR_PSU_CORTEXR5_0_CPU_CLK_FREQ_HZ);
	}
#endif

#endif

END:
	return Status;
}
/**
 * This function is used to initialize the system
 *
 * @param	None
 *
 * @return	None
 *
 *****************************************************************************/
u32 XFsbl_InitWdt(void)
{
	s32 Status;
	u32 UStatus;
	XWdtPs_Config *ConfigPtr; 	/* Config structure of the WatchDog Timer */
	u32 CounterValue;
	u32 RegValue;


	/**
	 * Initialize the WDT timer
	 */
	ConfigPtr = XWdtPs_LookupConfig(XFSBL_WDT_DEVICE_ID);

	if(ConfigPtr==NULL) {
		UStatus = XFSBL_WDT_INIT_FAILED;
		goto END;
	}

	Status = XWdtPs_CfgInitialize(&Watchdog,
			ConfigPtr,
			ConfigPtr->BaseAddress);
	if (Status != XFSBL_SUCCESS) {
		XFsbl_Printf(DEBUG_INFO, "XFSBL_WDT_INIT_FAILED\n\r");
		UStatus = XFSBL_WDT_INIT_FAILED;
		goto END;
	}

	/**
	 * Setting the divider value
	 */
	XWdtPs_SetControlValue(&Watchdog,
			XWDTPS_CLK_PRESCALE,
			XWDTPS_CCR_PSCALE_4096);
	/**
	 * Convert time to  Watchdog counter reset value
	 */
	CounterValue = XFsbl_ConvertTime_WdtCounter(XFSBL_WDT_EXPIRE_TIME);

	/**
	 * Set the Watchdog counter reset value
	 */
	XWdtPs_SetControlValue(&Watchdog,
			XWDTPS_COUNTER_RESET,
			CounterValue);
	/**
	 * enable reset output, as we are only using this as a basic counter
	 */
	XWdtPs_EnableOutput(&Watchdog, XWDTPS_RESET_SIGNAL);

	/* Enable generation of system reset by PMU due to LPD SWDT */
	RegValue = XFsbl_In32(PMU_GLOBAL_ERROR_SRST_EN_1);
	RegValue |= PMU_GLOBAL_ERROR_SRST_EN_1_LPD_SWDT_MASK;
	XFsbl_Out32(PMU_GLOBAL_ERROR_SRST_EN_1, RegValue);

	/* Enable LPD System Watchdog Timer Error */
	RegValue = XFsbl_In32(PMU_GLOBAL_ERROR_EN_1);
	RegValue |= PMU_GLOBAL_ERROR_EN_1_LPD_SWDT_MASK;
	XFsbl_Out32(PMU_GLOBAL_ERROR_EN_1, RegValue);

	/**
	 * Start the Watchdog timer
	 */
	XWdtPs_Start(&Watchdog);

	XWdtPs_RestartWdt(&Watchdog);

	UStatus = XFSBL_SUCCESS;
END:
	return UStatus;
}
/******************************************************************************
*
* This function is used to notify PMU firmware (if present) that initialization
* of all PM related register is completed
*
* @param	None
*
* @return	Success or XFSBL_ERROR_PM_INIT in case of any error
*
* @note		None
*
*******************************************************************************/
u32 XFsbl_PmInit(void)
{
	u32 UStatus;
/* Proceed only if SYSCFG is enabled */
#ifdef XPAR_XILPM_ENABLED
#ifdef XPAR_XIPIPSU_0_DEVICE_ID
	s32 Status ;
	XIpiPsu IpiInstance;
	XIpiPsu_Config *Config;
	u32 Response = 0U;

	#ifdef __aarch64__
	u32 CfgCmd[2U] = {PM_SET_CONFIGURATION, (u32)((u64)&XPm_ConfigObject[0])};
	#else
	u32 CfgCmd[2U] = {PM_SET_CONFIGURATION, (u32)&XPm_ConfigObject[0]};
	#endif
#endif

	/**
	 * Mark to the PMU that FSBL has completed with system initialization
	 * This is needed for the JTAG boot mode
	 */
	Xil_Out32(PMU_GLOBAL_PERS_GLOB_GEN_STORAGE5, PM_INIT_COMPLETED_KEY);

	/**
	 * Check if PMU FW is present
	 * If PMU FW is present, but IPI device does not exist, report an error
	 * If IPI device exists, but PMU FW is not present, do not issue IPI
	 */
	if ((XFsbl_In32(PMU_GLOBAL_GLOBAL_CNTRL) &
			PMU_GLOBAL_GLOBAL_CNTRL_FW_IS_PRESENT_MASK) !=
				PMU_GLOBAL_GLOBAL_CNTRL_FW_IS_PRESENT_MASK) {
		XFsbl_Printf(DEBUG_PRINT_ALWAYS,"PMU-FW is not running, certain applications may not be supported.\n\r");
		UStatus = XFSBL_SUCCESS;
		goto END;
	}
#ifndef XPAR_XIPIPSU_0_DEVICE_ID
	else {
		UStatus = XFSBL_ERROR_PM_INIT;
		XFsbl_Printf(DEBUG_GENERAL,
			"PMU firmware is present, but IPI is disabled\r\n");
		goto END;
	}
#endif

#ifdef XPAR_XIPIPSU_0_DEVICE_ID
	/* Initialize IPI peripheral */
	Config = XIpiPsu_LookupConfig(IPI_DEVICE_ID);
	if (Config == NULL) {
		UStatus = XFSBL_ERROR_PM_INIT;
		goto END;
	}

	Status = XIpiPsu_CfgInitialize(&IpiInstance, Config,
			Config->BaseAddress);
	if (XFSBL_SUCCESS != Status) {
		UStatus = XFSBL_ERROR_PM_INIT;
		goto END;
	}
	/* Send PM_SET_CONFIGURATION API to the PMU */
	Status = XIpiPsu_WriteMessage(&IpiInstance, IPI_PMU_PM_INT_MASK,
					&CfgCmd[0], 2U, XIPIPSU_BUF_TYPE_MSG);
	if (XFSBL_SUCCESS != Status) {
		UStatus = XFSBL_ERROR_PM_INIT;
		goto END;
	}

	Status = XIpiPsu_TriggerIpi(&IpiInstance, IPI_PMU_PM_INT_MASK);
	if (XFSBL_SUCCESS != Status) {
		UStatus = XFSBL_ERROR_PM_INIT;
		goto END;
	}


	/* This is a blocking call, wait until IPI is handled by the PMU */
	Status = XIpiPsu_PollForAck(&IpiInstance, IPI_PMU_PM_INT_MASK,
				  PM_IPI_TIMEOUT);
	if (XFSBL_SUCCESS != Status) {
		UStatus = XFSBL_ERROR_PM_INIT;
		goto END;
	}

	Status = XIpiPsu_ReadMessage(&IpiInstance, IPI_PMU_PM_INT_MASK,
					&Response, 1, XIPIPSU_BUF_TYPE_RESP);
	if ((Status != XFSBL_SUCCESS) || (Response != XFSBL_SUCCESS)) {
		UStatus = XFSBL_ERROR_PM_INIT;
		goto END;
	}
#endif /** end of IPI related code */
#endif /* End of XPAR_XILPM_ENABLED */
	XFsbl_Printf(DEBUG_DETAILED,"PM Init Success\r\n");
	UStatus = XFSBL_SUCCESS;
END:
	return UStatus;
}
/**
* This function authenticates the bitstream in blocks. Sends the data to PCAP
* in blocks via AES engine if encryption exists or directly to PCAP by CSUDMA
* if an encryption is not enabled.
*
* @param	PartitionParams is pointer to XFsblPs_PlPartition structure
* 		which has to be initialized by required parameters.
*
* @return
* 		- XFSBL_SUCCESS on success
* 		- Returns error code on failure
*
* @note		Currently SHA2 is not been supported but gave option in
* 		structure and will be used later
*
******************************************************************************/
u32 XFsbl_SecPlPartition(XFsblPs * FsblInstancePtr,
			XFsblPs_PlPartition *PartitionParams)
{
	u32 Status = XFSBL_SUCCESS;
	u8 Index;
	u32 Len;
	UINTPTR SrcAddress = (u64)PartitionParams->StartAddress;
	UINTPTR CurrentAcOffset = PartitionParams->PlAuth.AcOfset;
	u8 IsLastBlock = FALSE;
	u32 RegVal;

	if (PartitionParams->IsAuthenticated != TRUE) {
		XFsbl_Printf(DEBUG_GENERAL,"XFSBL_ERROR_SECURE_NOT_ENABLED"
				" for PL partition\r\n");
		Status = XFSBL_ERROR_SECURE_NOT_ENABLED;
		goto END;
	}

	/* AES initialization expects IV in required format */
	if (PartitionParams->IsEncrypted == TRUE) {
		PartitionParams->PlEncrypt.NextBlkLen = 0;
		if (PartitionParams->PlEncrypt.SecureAes == NULL) {
			XFsbl_Printf(DEBUG_GENERAL,
				"XFSBL_ERROR_SECURE_NOT_ENABLED"
					" for PL partition \r\n");
			Status = XFSBL_ERROR_SECURE_NOT_ENABLED;
			goto END;
		}
		if ((PartitionParams->PlEncrypt.SecureAes->KeySel
				== XSECURE_CSU_AES_KEY_SRC_KUP) &&
			(PartitionParams->PlEncrypt.SecureAes->Key == NULL)) {
			XFsbl_Printf(DEBUG_GENERAL,
				"KUP key is not been provided"
					" for PL partition\r\n");
			Status = XFSBL_FAILURE;
			goto END;
		}
	}

	/* Enable Simple DMA Mode for ADMA channel 0 */
	RegVal = XFsbl_In32(ADMA_CH0_ZDMA_CH_CTRL0);
	RegVal &= (ADMA_CH0_ZDMA_CH_CTRL0_POINT_TYPE_MASK |
			ADMA_CH0_ZDMA_CH_CTRL0_MODE_MASK);
	XFsbl_Out32(ADMA_CH0_ZDMA_CH_CTRL0, RegVal);

	Xil_DCacheDisable();
	/* Loop for traversing all blocks */
	for (Len = PartitionParams->PlAuth.BlockSize, Index = 1;
			SrcAddress < PartitionParams->PlAuth.AcOfset; Index++) {
		Status = XFsbl_CopyData(PartitionParams,
			PartitionParams->PlAuth.AuthCertBuf,
			(u8 *)CurrentAcOffset, XFSBL_AUTH_CERT_MIN_SIZE);
		if (Status != XFSBL_SUCCESS) {
			XFsbl_Printf(DEBUG_GENERAL,
			"Copy of chunk from flash/DDR to OCM failed \r\n");
			return Status;
		}

		/*
		 * If the block start address + block size exceeds
		 * first AC address it is last block
		 */
		if (SrcAddress + PartitionParams->PlAuth.BlockSize >
					PartitionParams->PlAuth.AcOfset) {
			/*
			 * Data placed in last block might not be full block size
			 * TotalLen -
			 * (NoofBlocks)*(AC size) - (NoOfBlocks - 1)*BlockSize
			 */
			Len = (PartitionParams->TotalLen) -
				(Index) * (XFSBL_AUTH_CERT_MIN_SIZE) -
				(Index -1)*(PartitionParams->PlAuth.BlockSize);
			IsLastBlock = TRUE;
		}

		Status = XFsbl_PlBlockAuthentication(FsblInstancePtr,
				PartitionParams, SrcAddress, Len,
				(u8 *)PartitionParams->PlAuth.AuthCertBuf);
		if (Status != XFSBL_SUCCESS) {
			return Status;
		}

		if (IsLastBlock == FALSE) {
			CurrentAcOffset =
				CurrentAcOffset + XFSBL_AUTH_CERT_MIN_SIZE;
			SrcAddress =
				SrcAddress + PartitionParams->PlAuth.BlockSize;
		}
		else {
			/* Completed last block of bitstream */
			break;
		}
	}
	Xil_DCacheEnable();
#ifdef XFSBL_PS_DDR
	/* Restore reset values for the DMA registers used */
	XFsbl_Out32(ADMA_CH0_ZDMA_CH_CTRL0, 0x00000080U);
	XFsbl_Out32(ADMA_CH0_ZDMA_CH_DST_DSCR_WORD0, 0x00000000U);
	XFsbl_Out32(ADMA_CH0_ZDMA_CH_DST_DSCR_WORD1, 0x00000000U);
	XFsbl_Out32(ADMA_CH0_ZDMA_CH_DST_DSCR_WORD2, 0x00000000U);
	XFsbl_Out32(ADMA_CH0_ZDMA_CH_DST_DSCR_WORD3, 0x00000000U);
	XFsbl_Out32(ADMA_CH0_ZDMA_CH_SRC_DSCR_WORD0, 0x00000000U);
	XFsbl_Out32(ADMA_CH0_ZDMA_CH_SRC_DSCR_WORD1, 0x00000000U);
	XFsbl_Out32(ADMA_CH0_ZDMA_CH_SRC_DSCR_WORD2, 0x00000000U);
	XFsbl_Out32(ADMA_CH0_ZDMA_CH_SRC_DSCR_WORD3, 0x00000000U);
#endif

END:

	return Status;

}