text: /******************************************************************************
  * Copyright (c) 2015 - 2020 Xilinx, Inc.  All rights reserved.
  * SPDX-License-Identifier: MIT
  ******************************************************************************/
  
  
  /*
   *
   * CONTENT
   * Assumptions: only PROCESSOR core is executing this code,
   * other cores in PROCESSOR subsystem are already powered down.
   * 1) PROCESSOR configures timer0 peripheral to generate interrupts.
   * 2) PROCESSOR waits for few interrupts to be generated by the timer and
   *    then initiates self suspend. Before calling pm_self_suspend APU
   *    has saved its context (which is in this case only tick_count
   *    variable value) in CONTEXT memory. Suspending of the PROCESSOR is followed
   *    by CONTEXT retention.
   * 3) Timer is still counting while PROCESSOR is suspended and the next timer
   *    interrupt causes CONTEXT to be woken up by PMU.
   * 4) Processor resumes its execution, meaning that it restores value of
   *    tick_count from CONTEXT MEM and does not configure timer again because it
   *    is already configured. PROCESSOR enables interrupts at the processor
   *    level (CPSR) and handle timer interrupt that caused wake-up.
   * 5) PROCESSOR waits for few more timer interrupts and repeats the suspend
   *    procedure.
   */
  
  #include <xil_exception.h>
  #include <xil_printf.h>
  #include <xil_io.h>
  #include <xil_cache.h>
  #include <xstatus.h>
  #include <sleep.h>
  #include "pm_api_sys.h"
  #include "timer.h"
  #include "pm_client.h"
  
  #define IPI_INT_ID	XPAR_XIPIPSU_0_INT_ID
  #define TEST_CHANNEL_ID	XPAR_XIPIPSU_0_DEVICE_ID
  
  static XIpiPsu IpiInst;
  
  static XStatus IpiConfigure(XIpiPsu *const IpiInstPtr)
  {
  	XStatus Status;
  	XIpiPsu_Config *IpiCfgPtr;
  
  	/* Look Up the config data */
  	IpiCfgPtr = XIpiPsu_LookupConfig(TEST_CHANNEL_ID);
  	if (NULL == IpiCfgPtr) {
  		Status = XST_FAILURE;
  		pm_dbg("%s ERROR in getting CfgPtr\n", __func__);
  		return Status;
  	}
  
  	/* Init with the Cfg Data */
  	Status = XIpiPsu_CfgInitialize(IpiInstPtr, IpiCfgPtr, IpiCfgPtr->BaseAddress);
  	if (XST_SUCCESS != Status) {
  		pm_dbg("%s ERROR #%d in configuring IPI\n", __func__, Status);
  		return Status;
  	}
  	return Status;
  }
  extern void *_vector_table;
  
  #ifdef __aarch64__
  	/* Use OCM for saving context */
  	#define CONTEXT_MEM_BASE	0xFFFC0000U
  #else
  	/* Use TCM for saving context */
  	#define CONTEXT_MEM_BASE	0x8000U
  #endif
  #if defined (__GNUC__)
  /* The below sections will be saved during suspend */
  extern u8 __data_start;
  extern u8 __bss_start__;
  extern u8 __data_end;
  extern u8 __bss_end__;
  #elif defined (__ICCARM__)
  #pragma section=".bss"
  #pragma section=".data"
  #endif
  /**
   * SaveContext() - called to save context of bss and data sections in OCM
   */
  static void SaveContext(void)
  {
  	u8 *MemPtr;
  	u8 *StartAddr;
  	u8 *EndAddr;
  	u8 *ContextMemPtr = (u8 *)CONTEXT_MEM_BASE;
  
  #if defined (__GNUC__)
  	StartAddr = &__data_start;
  	EndAddr = &__data_end;
  #elif defined (__ICCARM__)
  	StartAddr = __section_begin(".data");
  	EndAddr = __section_end(".data");
  #endif
  	for (MemPtr = StartAddr; MemPtr < EndAddr; MemPtr++, ContextMemPtr++) {
  		*ContextMemPtr = *MemPtr;
  	}
  
  #if defined (__GNUC__)
  	StartAddr = &__bss_start__;
  	EndAddr = &__bss_end__;
  #elif defined (__ICCARM__)
  	StartAddr = __section_begin(".bss");
  	EndAddr = __section_end(".bss");
  #endif
  	for (MemPtr = StartAddr; MemPtr < EndAddr; MemPtr++, ContextMemPtr++) {
  		*ContextMemPtr = *MemPtr;
  	}
  
  	pm_dbg("Saved context (tick_count = %d)\n", TickCount);
  }
  
  /**
   * RestoreContext() - called to restore context of bss and data sections from OCM
   */
  static void RestoreContext(void)
  {
  	u8 *MemPtr;
  	u8 *StartAddr;
  	u8 *EndAddr;
  	u8 *ContextMemPtr = (u8 *)CONTEXT_MEM_BASE;
  
  #if defined (__GNUC__)
  	StartAddr = &__data_start;
  	EndAddr = &__data_end;
  #elif defined (__ICCARM__)
  	StartAddr = __section_begin(".data");
  	EndAddr = __section_end(".data");
  #endif
  	for (MemPtr = StartAddr; MemPtr < EndAddr; MemPtr++, ContextMemPtr++) {
  		*MemPtr = *ContextMemPtr;
  	}
  
  #if defined (__GNUC__)
  	StartAddr = &__bss_start__;
  	EndAddr = &__bss_end__;
  #elif defined (__ICCARM__)
  	StartAddr = __section_begin(".bss");
  	EndAddr = __section_end(".bss");
  #endif
  	for (MemPtr = StartAddr; MemPtr < EndAddr; MemPtr++, ContextMemPtr++) {
  		*MemPtr = *ContextMemPtr;
  	}
  
  	pm_dbg("Restored context (tick_count = %d)\n", TickCount);
  }
  
  
  /**
   * PrepareSuspend() - save context and request suspend
   */
  static void PrepareSuspend(void)
  {
  	SaveContext();
  /* usleep is used to prevents UART prints from overlapping */
  #ifdef __aarch64__
  	u64 vector_base = (u64)&_vector_table;
  
  	/* APU */
  	XPm_SelfSuspend(NODE_APU_0, MAX_LATENCY, 0, vector_base);
  	usleep(100000);
  	XPm_SetRequirement(NODE_OCM_BANK_0, PM_CAP_CONTEXT, 0, REQUEST_ACK_NO);
  	usleep(100000);
  	XPm_SetRequirement(NODE_OCM_BANK_1, PM_CAP_CONTEXT, 0, REQUEST_ACK_NO);
  	usleep(100000);
  	XPm_SetRequirement(NODE_OCM_BANK_2, PM_CAP_CONTEXT, 0, REQUEST_ACK_NO);
  	usleep(100000);
  	XPm_SetRequirement(NODE_OCM_BANK_3, PM_CAP_CONTEXT, 0, REQUEST_ACK_NO);
  	usleep(100000);
  
  #else
  	u32 vector_base = (u32)&_vector_table;
  
  	/* RPU */
  	XPm_SelfSuspend(NODE_RPU_0, MAX_LATENCY, 0, vector_base);
  	usleep(100000);
  	XPm_SetRequirement(NODE_TCM_0_A, PM_CAP_CONTEXT, 0, REQUEST_ACK_NO);
  	usleep(100000);
  	XPm_SetRequirement(NODE_TCM_0_B, PM_CAP_CONTEXT, 0, REQUEST_ACK_NO);
  	usleep(100000);
  	XPm_SetRequirement(NODE_TCM_1_A, PM_CAP_CONTEXT, 0, REQUEST_ACK_NO);
  	usleep(100000);
  	XPm_SetRequirement(NODE_TCM_1_B, PM_CAP_CONTEXT, 0, REQUEST_ACK_NO);
  	usleep(100000);
  
  #endif /* __aarch64__ */
  }
  
  /**
   * InitApp() - initialize interrupts and context
   */
  static u32 InitApp(void)
  {
  	enum XPmBootStatus status = XPm_GetBootStatus();
  
  	pm_dbg("Main\n");
  	if (PM_INITIAL_BOOT == status) {
  		pm_dbg("INITIAL BOOT\n");
  		/* Configure timer, if configuration fails return from main */
  		if (XST_FAILURE == TimerConfigure(TIMER_PERIOD)) {
  			pm_dbg("Exiting main...\n");
  			return XST_FAILURE;
  		}
  		if (XST_SUCCESS != IpiConfigure(&IpiInst)) {
  			pm_dbg("Exiting main...\n");
  			return XST_FAILURE;
  		}
  		if (XST_SUCCESS != XPm_InitXilpm(&IpiInst)) {
  			pm_dbg("Exiting main...\n");
  			return XST_FAILURE;
  		}
  	} else if (PM_RESUME == status) {
  		pm_dbg("RESUMED\n");
  		RestoreContext();
  		/* Timer is already counting, just enable interrupts */
  		Xil_ExceptionEnable();
  	} else {
  		pm_dbg("ERROR cannot identify boot reason\n");
  	}
  
  	return XST_SUCCESS;
  }
  
  int main(void)
  {
  	Xil_DCacheDisable();
  	u32 Status = InitApp();
  
  	if (XST_SUCCESS != Status) {
  		return XST_FAILURE;
  	}
  
  	pm_dbg("Waiting for ticks...\n");
  	/* Wait for 3 timer ticks */
  	while ((TickCount + 1) % 4);
  
  	PrepareSuspend();
  	XPm_SuspendFinalize();
  
  	/*
  	 * Can execute code below only if interrupt is generated between calling
  	 * the PrepareSuspend and executing wfi. Shouldn't happen.
  	 */
  	pm_dbg("Error! WFI exit...\n");
  
  	return XST_FAILURE;
  }
  