text: /******************************************************************************
  * Copyright (C) 2007 - 2020 Xilinx, Inc.  All rights reserved.
  * SPDX-License-Identifier: MIT
  ******************************************************************************/
  
  /****************************************************************************/
  /**
  *
  * @file xsysmon_intr_example.c
  *
  * This file contains a design example using the driver functions
  * of the System Monitor/ADC driver. This example here shows the usage of the
  * driver/device in interrupt mode to handle on-chip temperature and voltage
  * alarm interrupts.
  *
  *
  * @note
  *
  * This code assumes that no Operating System is being used.
  *
  * The values of the on-chip temperature and the on-chip Vccaux voltage are read
  * from the device and then the alarm thresholds are set in such a manner that
  * the alarms occur.
  *
  * <pre>
  *
  * MODIFICATION HISTORY:
  *
  * Ver   Who    Date     Changes
  * ----- -----  -------- -----------------------------------------------------
  * 1.00a xd/sv  05/22/07 First release
  * 2.00a sv     06/22/08 Modified the function description of the interrupt
  *			handler
  * 2.00a sdm    09/26/08 Added code to return temperature value to the main
  *			function. TestappPeripheral prints the temperature
  * 4.00a ktn    10/22/09 Updated the example to use HAL processor APIs/macros.
  *		        Updated the example to use macros that have been
  *		        renamed to remove _m from the name of the macro.
  * 5.03a bss    04/25/13 Modified SysMonIntrExample function to set
  *			Sequencer Mode as Safe mode instead of Single
  *			channel mode before configuring Sequencer registers.
  *			CR #703729
  * 7.2   adk    29/02/16 Updated example to support Zynq and ZynqMP.
  * 7.3   ms   01/23/17 Added xil_printf statement in main function to
  *                     ensure that "Successfully ran" and "Failed" strings
  *                     are available in all examples. This is a fix for
  *                     CR-965028.
  * </pre>
  *
  *****************************************************************************/
  
  /***************************** Include Files ********************************/
  
  #include "xsysmon.h"
  #include "xparameters.h"
  #include "xstatus.h"
  #include "xil_exception.h"
  #include "xil_printf.h"
  
  #ifdef XPAR_INTC_0_DEVICE_ID
  #include "xintc.h"
  #else
  #include "xscugic.h"
  #endif
  
  /************************** Constant Definitions ****************************/
  
  /*
   * The following constants map to the XPAR parameters created in the
   * xparameters.h file. They are defined here such that a user can easily
   * change all the needed parameters in one place.
   */
  #ifndef TESTAPP_GEN
  #ifdef XPAR_INTC_0_DEVICE_ID
  #define SYSMON_DEVICE_ID	XPAR_SYSMON_0_DEVICE_ID
  #define INTC_DEVICE_ID		XPAR_INTC_0_DEVICE_ID
  #define INTR_ID			XPAR_INTC_0_SYSMON_0_VEC_ID
  #else
  #define SYSMON_DEVICE_ID	XPAR_SYSMON_0_DEVICE_ID
  #define INTC_DEVICE_ID		XPAR_SCUGIC_SINGLE_DEVICE_ID
  #define INTR_ID			XPAR_FABRIC_SYSTEM_MANAGEMENT_WIZ_0_IP2INTC_IRPT_INTR
  #endif
  #endif
  
  #ifdef XPAR_INTC_0_DEVICE_ID
  #define INTC		XIntc
  #define INTC_HANDLER	XIntc_InterruptHandler
  #else
  #define INTC		XScuGic
  #define INTC_HANDLER	XScuGic_InterruptHandler
  #endif
  /**************************** Type Definitions ******************************/
  
  /***************** Macros (Inline Functions) Definitions ********************/
  
  /************************** Function Prototypes *****************************/
  
  int SysMonIntrExample(INTC* IntcInstancePtr,
  			XSysMon* SysMonInstPtr,
  			u16 SysMonDeviceId,
  			u16 SysMonIntrId,
  			int *Temp);
  
  static void SysMonInterruptHandler(void *CallBackRef);
  
  static int SysMonSetupInterruptSystem(INTC* IntcInstancePtr,
  				      XSysMon *SysMonPtr,
  				      u16 IntrId );
  
  /************************** Variable Definitions ****************************/
  
  #ifndef TESTAPP_GEN
  static XSysMon SysMonInst; 	  /* System Monitor driver instance */
  static INTC InterruptController; /* Instance of the XIntc driver. */
  #endif
  
  /*
   * Shared variables used to test the callbacks.
   */
  volatile static int TemperatureIntr = FALSE; 	/* Temperature alarm intr */
  volatile static int VccauxIntr = FALSE;	  	/* VCCAUX alarm interrupt */
  
  #ifndef TESTAPP_GEN
  /****************************************************************************/
  /**
  *
  * Main function that invokes the Interrupt example.
  *
  * @param	None.
  *
  * @return
  *		- XST_SUCCESS if the example has completed successfully.
  *		- XST_FAILURE if the example has failed.
  *
  * @note		None.
  *
  *****************************************************************************/
  int main(void)
  {
  
  	int Status;
  	int Temp;
  
  	/*
  	 * Run the SysMonitor interrupt example, specify the parameters that
  	 * are generated in xparameters.h.
  	 */
  	Status = SysMonIntrExample(&InterruptController,
  				   &SysMonInst,
  				   SYSMON_DEVICE_ID,
  				   INTR_ID,
  				   &Temp);
  	if (Status != XST_SUCCESS) {
  		xil_printf("Sysmon interrupt Example Failed\r\n");
  		return XST_FAILURE;
  	}
  	xil_printf("Successfully ran Sysmon interrupt Example\r\n");
  	return XST_SUCCESS;
  
  }
  #endif /* TESTAPP_GEN */
  
  /****************************************************************************/
  /**
  *
  * This function runs a test on the System Monitor/ADC device using the
  * driver APIs.
  *
  * The function does the following tasks:
  *	- Initiate the System Monitor/ADC device driver instance
  *	- Run self-test on the device
  *	- Reset the device
  *	- Set up alarms for on-chip temperature and VCCAUX
  *	- Set up sequence registers to continuously monitor on-chip temperature
  *	and VCCAUX
  *	- Setup interrupt system
  *	- Enable interrupts
  *	- Set up configuration registers to start the sequence
  *	- Wait until temperature alarm interrupt or VCCAUX alarm interrupt
  *	occurs
  *
  * @param	IntcInstancePtr is a pointer to the Interrupt Controller
  *		driver Instance.
  * @param	SysMonInstPtr is a pointer to the XSysMon driver Instance.
  * @param	SysMonDeviceId is the XPAR_<SYSMON_ADC_instance>_DEVICE_ID value
  *		from xparameters.h.
  * @param	SysMonIntrId is
  *		XPAR_<INTC_instance>_<SYSMON_ADC_instance>_VEC_ID value from
  *		xparameters.h
  * @param	Temp is an output parameter, it is a pointer through which the
  *		current temperature value is returned to the main function.
  *
  * @return
  *		- XST_SUCCESS if the example has completed successfully.
  *		- XST_FAILURE if the example has failed.
  *
  * @note		This function may never return if no interrupt occurs.
  *
  ****************************************************************************/
  int SysMonIntrExample(INTC* IntcInstancePtr, XSysMon* SysMonInstPtr,
  			u16 SysMonDeviceId, u16 SysMonIntrId, int *Temp)
  {
  	int Status;
  	XSysMon_Config *ConfigPtr;
  	u16 TempData;
  	u16 VccauxData;
  	u32 IntrStatus;
  
  	/*
  	 * Initialize the SysMon driver.
  	 */
  	ConfigPtr = XSysMon_LookupConfig(SysMonDeviceId);
  	if (ConfigPtr == NULL) {
  		return XST_FAILURE;
  	}
  	XSysMon_CfgInitialize(SysMonInstPtr, ConfigPtr, ConfigPtr->BaseAddress);
  
  	/*
  	 * Self Test the System Monitor/ADC device.
  	 */
  	Status = XSysMon_SelfTest(SysMonInstPtr);
  	if (Status != XST_SUCCESS) {
  		return XST_FAILURE;
  	}
  
  	/*
  	 * Disable the Channel Sequencer before configuring the Sequence
  	 * registers.
  	 */
  	XSysMon_SetSequencerMode(SysMonInstPtr, XSM_SEQ_MODE_SAFE);
  
  	/*
  	 * Setup the Averaging to be done for the channels in the
  	 * Configuration 0 register as 16 samples:
  	 */
  	XSysMon_SetAvg(SysMonInstPtr, XSM_AVG_16_SAMPLES);
  
  	/*
  	 * Setup the Sequence register for 1st Auxiliary channel
  	 * Setting is:
  	 *	- Add acquisition time by 6 ADCCLK cycles.
  	 *	- Bipolar Mode
  	 *
  	 * Setup the Sequence register for 16th Auxiliary channel
  	 * Setting is:
  	 *	- Add acquisition time by 6 ADCCLK cycles.
  	 *	- Unipolar Mode
  	 */
  	Status = XSysMon_SetSeqInputMode(SysMonInstPtr, XSM_SEQ_CH_AUX00);
  	if (Status != XST_SUCCESS) {
  		return XST_FAILURE;
  	}
  
  	Status = XSysMon_SetSeqAcqTime(SysMonInstPtr, XSM_SEQ_CH_AUX15 |
  						XSM_SEQ_CH_AUX00);
  	if (Status != XST_SUCCESS) {
  		return XST_FAILURE;
  	}
  
  	/*
  	 * Enable the averaging on the following channels in the Sequencer
  	 * registers:
  	 * 	- On-chip Temperature
  	 * 	- On-chip VCCAUX supply sensor
  	 * 	- 1st Auxiliary Channel
  	 * 	- 16th Auxiliary Channel
  	 */
  	Status =  XSysMon_SetSeqAvgEnables(SysMonInstPtr, XSM_SEQ_CH_TEMP |
  						XSM_SEQ_CH_VCCAUX |
  						XSM_SEQ_CH_AUX00 |
  						XSM_SEQ_CH_AUX15);
  	if (Status != XST_SUCCESS) {
  		return XST_FAILURE;
  	}
  
  	/*
  	 * Enable the following channels in the Sequencer registers:
  	 * 	- On-chip Temperature
  	 * 	- On-chip VCCAUX supply sensor
  	 * 	- 1st Auxiliary Channel
  	 * 	- 16th Auxiliary Channel
  	 */
  	Status =  XSysMon_SetSeqChEnables(SysMonInstPtr, XSM_SEQ_CH_TEMP |
  						XSM_SEQ_CH_VCCAUX |
  						XSM_SEQ_CH_AUX00 |
  						XSM_SEQ_CH_AUX15);
  	if (Status != XST_SUCCESS) {
  		return XST_FAILURE;
  	}
  
  	/*
  	 * Set the ADCCLK frequency equal to 1/32 of System clock for the System
  	 * Monitor/ADC in the Configuration Register 2.
  	 */
  	XSysMon_SetAdcClkDivisor(SysMonInstPtr, 32);
  
  
  	/*
  	 * Enable the Channel Sequencer in continuous sequencer cycling mode.
  	 */
  	XSysMon_SetSequencerMode(SysMonInstPtr, XSM_SEQ_MODE_CONTINPASS);
  
  	/*
  	 * Wait till the End of Sequence occurs
  	 */
  	XSysMon_GetStatus(SysMonInstPtr); /* Clear the old status */
  	while ((XSysMon_GetStatus(SysMonInstPtr) & XSM_SR_EOS_MASK) !=
  			XSM_SR_EOS_MASK);
  
  	/*
  	 * Read the ADC converted Data from the data registers for on-chip
  	 * temperature and on-chip VCCAUX voltage.
  	 */
  	TempData = XSysMon_GetAdcData(SysMonInstPtr, XSM_CH_TEMP);
  	VccauxData = XSysMon_GetAdcData(SysMonInstPtr, XSM_CH_VCCAUX);
  
  	/*
  	 * Convert the ADC data into temperature
  	 */
  	*Temp = XSysMon_RawToTemperature(TempData);
  
  	/*
  	 * Disable all the alarms in the Configuration Register 1.
  	 */
  	XSysMon_SetAlarmEnables(SysMonInstPtr, 0x0);
  
  
  	/*
  	 * Set up Alarm threshold registers for the on-chip temperature and
  	 * VCCAUX High limit and lower limit so that the alarms DONOT occur.
  	 */
  	XSysMon_SetAlarmThreshold(SysMonInstPtr, XSM_ATR_TEMP_UPPER, 0xFFFF);
  	XSysMon_SetAlarmThreshold(SysMonInstPtr, XSM_ATR_TEMP_LOWER, 0xFFFF);
  
  	XSysMon_SetAlarmThreshold(SysMonInstPtr, XSM_ATR_VCCAUX_UPPER, 0xFFFF);
  	XSysMon_SetAlarmThreshold(SysMonInstPtr, XSM_ATR_VCCAUX_LOWER, 0x0);
  
  	/*
  	 * Setup the interrupt system.
  	 */
  	Status = SysMonSetupInterruptSystem(IntcInstancePtr,
  					    SysMonInstPtr,
  					    SysMonIntrId);
  	if (Status != XST_SUCCESS) {
  		return XST_FAILURE;
  	}
  
  	/*
  	 * Clear any bits set in the Interrupt Status Register.
  	 */
  	IntrStatus = XSysMon_IntrGetStatus(SysMonInstPtr);
  	XSysMon_IntrClear(SysMonInstPtr, IntrStatus);
  
  
  	/*
  	 * Enable Alarm 0 interrupt for on-chip temperature and Alarm 2
  	 * interrupt for on-chip VCCAUX.
  	 */
  	XSysMon_IntrEnable(SysMonInstPtr,
  				XSM_IPIXR_TEMP_MASK |
  				XSM_IPIXR_VCCAUX_MASK);
  
  	/*
  	 * Enable global interrupt of System Monitor.
  	 */
  	XSysMon_IntrGlobalEnable(SysMonInstPtr);
  
  	/*
  	 * Set up Alarm threshold registers for
  	 * On-chip Temperature High limit
  	 * On-chip Temperature Low limit
  	 * VCCAUX High limit
  	 * VCCAUX Low limit
  	 */
  	XSysMon_SetAlarmThreshold(SysMonInstPtr, XSM_ATR_TEMP_UPPER,
  						TempData - 0x007F);
  	XSysMon_SetAlarmThreshold(SysMonInstPtr, XSM_ATR_TEMP_LOWER,
  						TempData - 0x007F);
  	XSysMon_SetAlarmThreshold(SysMonInstPtr, XSM_ATR_VCCAUX_UPPER,
  						VccauxData - 0x007F);
  	XSysMon_SetAlarmThreshold(SysMonInstPtr, XSM_ATR_VCCAUX_LOWER,
  						VccauxData + 0x007F);
  
  
  	/*
  	 * Enable Alarm 0 for on-chip temperature and Alarm 2 for on-chip
  	 * VCCAUX in the Configuration Register 1.
  	 */
  	XSysMon_SetAlarmEnables(SysMonInstPtr, (XSM_CFR1_ALM_VCCAUX_MASK |
  						XSM_CFR1_ALM_TEMP_MASK));
  
  
  	/*
  	 * Wait until an Alarm 0 or Alarm 2 interrupt occurs.
  	 */
  	while (1) {
  		if (TemperatureIntr == TRUE) {
  			/*
  			 * Alarm 0 - Temperature alarm interrupt has occurred.
  			 * The required processing should be put here.
  			 */
  			break;
  		}
  
  		if (VccauxIntr == TRUE) {
  			/*
  			 * Alarm 2 - VCCAUX alarm interrupt has occurred.
  			 * The required processing should be put here.
  			 */
  			break;
  		}
  	}
  
  	/*
  	 * Disable global interrupt of System Monitor.
  	 */
  	XSysMon_IntrGlobalDisable(SysMonInstPtr);
  
  	return XST_SUCCESS;
  }
  
  /*****************************************************************************/
  /**
  *
  * This function is the Interrupt Service Routine for the System Monitor device.
  * It will be called by the processor whenever an interrupt is asserted
  * by the device.
  *
  * There are 10 different interrupts supported
  *	- Over Temperature
  *	- ALARM 0
  *	- ALARM 1
  *	- ALARM 2
  *	- End of Sequence
  *	- End of Conversion
  *	- JTAG Locked
  *	- JATG Modified
  *	- Over Temperature deactivate
  *	- ALARM 0 deactivate
  *
  * This function only handles ALARM 0 and ALARM 2 interrupts. User of this
  * code may need to modify the code to meet needs of the application.
  *
  * @param	CallBackRef is the callback reference passed from the Interrupt
  *		controller driver, which in our case is a pointer to the
  *		driver instance.
  *
  * @return	None.
  *
  * @note		This function is called within interrupt context.
  *
  ******************************************************************************/
  static void SysMonInterruptHandler(void *CallBackRef)
  {
  	u32 IntrStatusValue;
  	XSysMon *SysMonPtr = (XSysMon *)CallBackRef;
  
  	/*
  	 * Get the interrupt status from the device and check the value.
  	 */
  	IntrStatusValue = XSysMon_IntrGetStatus(SysMonPtr);
  
  	if (IntrStatusValue & XSM_IPIXR_TEMP_MASK) {
  		/*
  		 * Set Temperature interrupt flag so the code
  		 * in application context can be aware of this interrupt.
  		 */
  		TemperatureIntr = TRUE;
  	}
  
  	if (IntrStatusValue & XSM_IPIXR_VCCAUX_MASK) {
  		/*
  		 * Set VCCAUX interrupt flag so the code in application context
  		 * can be aware of this interrupt.
  		 */
  
  		VccauxIntr = TRUE;
  	}
  
  	/*
  	 * Clear all bits in Interrupt Status Register.
  	 */
  	XSysMon_IntrClear(SysMonPtr, IntrStatusValue);
   }
  
  /****************************************************************************/
  /**
  *
  * This function sets up the interrupt system so interrupts can occur for the
  * System Monitor/ADC.  The function is application-specific since the actual
  * system may or may not have an interrupt controller. The System Monitor/ADC
  * device could be directly connected to a processor without an interrupt
  * controller. The user should modify this function to fit the application.
  *
  * @param	IntcInstancePtr is a pointer to the Interrupt Controller
  *		driver Instance.
  * @param	SysMonPtr is a pointer to the driver instance for the System
  * 		Monitor device which is going to be connected to the interrupt
  *		controller.
  * @param	IntrId is XPAR_<INTC_instance>_<SYSMON_ADC_instance>_VEC_ID
  *		value from xparameters.h.
  *
  * @return	XST_SUCCESS if successful, or XST_FAILURE.
  *
  * @note		None.
  *
  *
  ****************************************************************************/
  static int SysMonSetupInterruptSystem(INTC* IntcInstancePtr,
  				      XSysMon *SysMonPtr,
  				      u16 IntrId )
  {
  	int Status;
  
  #ifdef XPAR_INTC_0_DEVICE_ID
  #ifndef TESTAPP_GEN
  	/*
  	 * Initialize the interrupt controller driver so that it's ready to use.
  	 */
  	Status = XIntc_Initialize(IntcInstancePtr, INTC_DEVICE_ID);
  	if (Status != XST_SUCCESS) {
  		return XST_FAILURE;
  	}
  #endif
  	/*
  	 * Connect the handler that will be called when an interrupt
  	 * for the device occurs, the handler defined above performs the
  	 * specific interrupt processing for the device.
  	 */
  	Status = XIntc_Connect(IntcInstancePtr,
  				IntrId,
  				(XInterruptHandler) SysMonInterruptHandler,
  				SysMonPtr);
  	if (Status != XST_SUCCESS) {
  		return XST_FAILURE;
  	}
  
  #ifndef TESTAPP_GEN
  	/*
  	 * Start the interrupt controller so interrupts are enabled for all
  	 * devices that cause interrupts. Specify real mode so that the System
  	 * Monitor/ACD device can cause interrupts through the interrupt
  	 * controller.
  	 */
  	Status = XIntc_Start(IntcInstancePtr, XIN_REAL_MODE);
  	if (Status != XST_SUCCESS) {
  		return XST_FAILURE;
  	}
  #endif
  	/*
  	 * Enable the interrupt for the System Monitor/ADC device.
  	 */
  	XIntc_Enable(IntcInstancePtr, IntrId);
  #else /* SCUGIC */
  
  #ifndef TESTAPP_GEN
  	XScuGic_Config *IntcConfig;
  
  	/*
  	 * Initialize the interrupt controller driver so that it is ready to
  	 * use.
  	 */
  	IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
  	if (NULL == IntcConfig) {
  		return XST_FAILURE;
  	}
  
  	Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
  					IntcConfig->CpuBaseAddress);
  	if (Status != XST_SUCCESS) {
  		return XST_FAILURE;
  	}
  #endif
  
  	XScuGic_SetPriorityTriggerType(IntcInstancePtr, IntrId,
  					0xA0, 0x3);
  
  	/*
  	 * Connect the interrupt handler that will be called when an
  	 * interrupt occurs for the device.
  	 */
  	Status = XScuGic_Connect(IntcInstancePtr, IntrId,
  				 (Xil_ExceptionHandler)SysMonInterruptHandler,
  				 SysMonPtr);
  	if (Status != XST_SUCCESS) {
  		return Status;
  	}
  
  	/*
  	 * Enable the interrupt for the Sysmon device.
  	 */
  	XScuGic_Enable(IntcInstancePtr, IntrId);
  #endif
  
  #ifndef TESTAPP_GEN
  
  	/*
  	 * Initialize the exception table.
  	 */
  	Xil_ExceptionInit();
  
  	/*
  	 * Register the interrupt controller handler with the exception table.
  	 */
  	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
  				(Xil_ExceptionHandler) INTC_HANDLER,
  				IntcInstancePtr);
  	/*
  	 * Enable exceptions.
  	 */
  	Xil_ExceptionEnable();
  
  #endif /* TESTAPP_GEN */
  
  	return XST_SUCCESS;
  }
  