/******************************************************************************
*
* Copyright (C) 2002 - 2014 Xilinx, Inc.  All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/
/*****************************************************************************/
/**
* @file xgpio_intr_example.c
*
* This file contains a design example using the GPIO driver (XGpio) in an
* interrupt driven mode of operation. This example does assume that there is
* an interrupt controller in the hardware system and the GPIO device is
* connected to the interrupt controller.
*
* This example is designed to work on the Xilinx ML300 board using the PowerPC
* 405 processor present in the VirtexIIPro device. The example uses the
* interrupt capability of the GPIO to detect push button presses and control the
* LEDs on the board. When a button is pressed it will turn on and LED located
* closest to it. When the button is released it will turn off the LED.
* This examples uses two channels of a GPIO such that it is necessary to have
* dual channel capabilities.
*
* The buttons and LEDs are on 2 seperate channels of the GPIO so that interrupts
* are not caused when the LEDs are turned on and off.
*
* At the start of execution all LEDs will be turned on, then each one by itself,
* and then all on again followed by all turned off. After this sequence, button
* presses are processed by interrupts.
*
* The following snippet from the UCF file of the hardware build indicates the
* way the GPIO channels are connected to the ML300 for the LEDs and buttons.
*
*<pre>
* Net LEDs_Push_Buttons_GPIO_IO<0> LOC=C4;
* Net LEDs_Push_Buttons_GPIO_IO<1> LOC=L8;
* Net LEDs_Push_Buttons_GPIO_IO<2> LOC=F8;
* Net LEDs_Push_Buttons_GPIO_IO<3> LOC=J7;
* Net LEDs_Push_Buttons_GPIO_IO<4> LOC=K7;
* Net LEDs_Push_Buttons_GPIO_IO<5> LOC=E7;
* Net LEDs_Push_Buttons_GPIO_IO<6> LOC=D3;
* Net LEDs_Push_Buttons_GPIO_IO<7> LOC=C6;
* Net LEDs_Push_Buttons_GPIO_IO<8> LOC=E8;
* Net LEDs_Push_Buttons_GPIO_IO<9> LOC=B3;
* Net LEDs_Push_Buttons_GPIO_IO<10> LOC=E9;
* Net LEDs_Push_Buttons_GPIO_IO<11> LOC=G9;
* Net LEDs_Push_Buttons_GPIO_IO<12> LOC=A3;
* Net LEDs_Push_Buttons_GPIO_IO<13> LOC=F9;
* Net LEDs_Push_Buttons_GPIO_IO<14> LOC=D6;
* Net LEDs_Push_Buttons_GPIO_IO<15> LOC=G10;
* Net LEDs_Push_Buttons_GPIO2_IO<0> LOC=G6;
* Net LEDs_Push_Buttons_GPIO2_IO<1> LOC=L7;
* Net LEDs_Push_Buttons_GPIO2_IO<2> LOC=G5;
* Net LEDs_Push_Buttons_GPIO2_IO<3> LOC=M8;
* Net LEDs_Push_Buttons_GPIO2_IO<4> LOC=H6;
* Net LEDs_Push_Buttons_GPIO2_IO<5> LOC=M7;
* Net LEDs_Push_Buttons_GPIO2_IO<6> LOC=H5;
* Net LEDs_Push_Buttons_GPIO2_IO<7> LOC=N8;
* Net LEDs_Push_Buttons_GPIO2_IO<8> LOC=J6;
* Net LEDs_Push_Buttons_GPIO2_IO<9> LOC=M5;
* Net LEDs_Push_Buttons_GPIO2_IO<10> LOC=J5;
* Net LEDs_Push_Buttons_GPIO2_IO<11> LOC=M2;
* Net LEDs_Push_Buttons_GPIO2_IO<12> LOC=K6;
* Net LEDs_Push_Buttons_GPIO2_IO<13> LOC=M1;
* Net LEDs_Push_Buttons_GPIO2_IO<14> LOC=K5;
* Net LEDs_Push_Buttons_GPIO2_IO<15> LOC=P6;
*
* MODIFICATION HISTORY:
*
* Ver   Who  Date	 Changes
* ----- ---- -------- -----------------------------------------------
* 2.00a jhl  12/01/03 First release
* 2.00a sv   04/15/05 Minor changes to comply to Doxygen and coding guidelines
* 3.00a ktn  11/21/09 Updated to use HAL Processor APIs and minior changes
*		      as per coding guidelines.
* 3.00a sdm  02/16/11 Updated to support ARM Generic Interrupt Controller
*</pre>
*
******************************************************************************/

/***************************** Include Files *********************************/

#include "xparameters.h"
#include "xgpio.h"
#include "xil_exception.h"

#ifdef XPAR_INTC_0_DEVICE_ID
 #include "xintc.h"
#else
 #include "xscugic.h"
#endif

/************************** Constant Definitions *****************************/

/*
 * The following constants map to the names of the hardware instances that
 * were created in the EDK XPS system.  They are only defined here such that
 * a user can easily change all the needed device IDs in one place.
 */
#define GPIO_DEVICE_ID		XPAR_PUSH_BUTTONS_4BITS_DEVICE_ID
#define INTC_GPIO_INTERRUPT_ID	XPAR_INTC_0_GPIO_2_VEC_ID

#ifdef XPAR_INTC_0_DEVICE_ID
 #define INTC_DEVICE_ID	XPAR_INTC_0_DEVICE_ID
 #define INTC		XIntc
 #define INTC_HANDLER	XIntc_InterruptHandler
#else
 #define INTC_DEVICE_ID	XPAR_SCUGIC_SINGLE_DEVICE_ID
 #define INTC		XScuGic
 #define INTC_HANDLER	XScuGic_InterruptHandler
#endif

/*
 * The following constants define the positions of the buttons and LEDs each
 * channel of the GPIO
 */
#define GPIO_ALL_LEDS		0xFFFF
#define GPIO_ALL_BUTTONS	0xFFFF

/*
 * The following constants define the GPIO channel that is used for the buttons
 * and the LEDs. They allow the channels to be reversed easily.
 */
#define BUTTON_CHANNEL	 1	/* Channel 1 of the GPIO Device */
#define LED_CHANNEL	 2	/* Channel 2 of the GPIO Device */
#define BUTTON_INTERRUPT XGPIO_IR_CH1_MASK  /* Channel 1 Interrupt Mask */


/*
 * The following constant is used to wait after an LED is turned on to make
 * sure that it is visible to the human eye.  This constant might need to be
 * tuned for faster or slower processor speeds.
 */
#define LED_DELAY	 1000000

/**************************** Type Definitions *******************************/

typedef struct
{
	u32 ButtonMask;	 /* The bit corresponding to the button */
	u32 LedMask;	 /* The bit corresponding to the LED */
} MapButtonTable;

/***************** Macros (Inline Functions) Definitions *********************/


/************************** Function Prototypes ******************************/

int MapButton2Led(u32 Buttons, u32 *ButtonFoundPtr);

void SequenceLeds();

void GpioIsr(void *InstancePtr);

int SetupInterruptSystem();

/************************** Variable Definitions *****************************/

/*
 * The following are declared globally so they are zeroed and so they are
 * easily accessible from a debugger
 */
static XGpio Gpio; /* The Instance of the GPIO Driver */

static INTC Intc; /* The Instance of the Interrupt Controller Driver */

volatile int InterruptCount; /* Count of interrupts that have occured */


/*
 * The following table contains the masks for the buttons and LEDS
 * that are connected on the board. It's purpose is to map a button
 * to a specific LED.
 */
 MapButtonTable Button2LedTable[] =
	{ { 0x1, 0x1 },
	  { 0x2, 0x2 },
	  { 0x4, 0x4 },
	  { 0x8, 0x8 },
	  { 0x10, 0x8 },
	  { 0x20, 0x8 },
	  { 0x40, 0x8 },
	  { 0x100, 0x10 },
	  { 0x200, 0x20 },
	  { 0x400, 0x40 },
	  { 0x800, 0x80 },
	  { 0x1000, 0x80 },
	  { 0x2000, 0x80 },
	  { 0x4000, 0x80 } };

/****************************************************************************/
/**
* This function is the main function of the GPIO example.  It is responsible
* for initializing the GPIO device, setting up interrupts and providing a
* foreground loop such that interrupt can occur in the background.
*
* @param	None.
*
* @return
*		- XST_SUCCESS to indicate success.
*		- XST_FAILURE to indicate Failure.
*
* @note		None.
*
*
*****************************************************************************/
int main(void)
{
	int Status;

	/* Initialize the GPIO driver. If an error occurs then exit */

	Status = XGpio_Initialize(&Gpio, GPIO_DEVICE_ID);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * Perform a self-test on the GPIO.  This is a minimal test and only
	 * verifies that there is not any bus error when reading the data
	 * register
	 */
	XGpio_SelfTest(&Gpio);

	/*
	 * Setup direction register so the switch is an input and the LED is
	 * an output of the GPIO
	 */
	XGpio_SetDataDirection(&Gpio, BUTTON_CHANNEL, GPIO_ALL_BUTTONS);
	XGpio_SetDataDirection(&Gpio, LED_CHANNEL, ~GPIO_ALL_LEDS);

	/* Sequence the LEDs to show this example is starting to run */

	SequenceLeds();

	/*
	 * Setup the interrupts such that interrupt processing can occur. If
	 * an error occurs then exit
	 */
	Status = SetupInterruptSystem();
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * Loop forever while the button changes are handled by the interrupt
	 * level processing
	 */
	while (1) {
	}

	return XST_SUCCESS;
}

/****************************************************************************/
/**
* This function sequences the LEDs by turning them all on, the turning each
* one on individually, then turning them all on, and finally off.
*
* @param	None.
*
* @return	None.
*
* @note		None.
*
*****************************************************************************/
void SequenceLeds()
{
	u32 Mask = 0x8000;
	int Led;
	volatile int Delay;

	/* Turn on all the LEDS to show starting the sequence */

	XGpio_DiscreteWrite(&Gpio, LED_CHANNEL, GPIO_ALL_LEDS);
	for (Delay = 0; Delay < LED_DELAY; Delay++);

	/* Sequence thru turning each LED on one at a time */

	for (Led = 1; Led <= 16; Led++) {
		XGpio_DiscreteWrite(&Gpio, LED_CHANNEL, Mask);
		Mask >>= 1;

		/* Wait a small amount of time so the LED is visible */

		for (Delay = 0; Delay < LED_DELAY; Delay++);
	}

	/* Turn on all LEDS to show stopping the sequence */

	XGpio_DiscreteWrite(&Gpio, LED_CHANNEL, GPIO_ALL_LEDS);
	for (Delay = 0; Delay < LED_DELAY; Delay++);

	/* Turn off all the LEDs */

	XGpio_DiscreteWrite(&Gpio, LED_CHANNEL, 0);

	XGpio_InterruptClear(&Gpio, XGPIO_IR_MASK);
}

/****************************************************************************/
/**
* This function maps each button on the board to an LED.
*
* @param	Buttons contains the buttons that have changed.
* @param	ButtonFoundPtr is a pointer to allow this function to indicate
*		the button that was associated with the returned LED. This
*		input is needed to allow muliple buttons to change
*		simulataneously.
*
* @return
*
* The Led that is associated with the first button that was found to be
* changed.  A value of zero indicates no LED was found.
*
* @note		None.
*
*****************************************************************************/
int MapButton2Led(u32 Buttons, u32 *ButtonFoundPtr)
{
	int Index;

	/* Look thru the table to map the button to an LED */

	for (Index = 0; Index < sizeof(Button2LedTable)/ sizeof(MapButtonTable);
		Index++) {

		/*
		 * Determine which LED corresponds to the button being careful
		 * because more than one button could have changed
		 */
		if (Button2LedTable[Index].ButtonMask ==
			(Buttons & Button2LedTable[Index].ButtonMask)) {
			/*
			 * If the button was found then return the
			 * associated LED
			 */
			*ButtonFoundPtr = Button2LedTable[Index].ButtonMask;
			return Button2LedTable[Index].LedMask;
		}
	}

	/* If no button was found in the table, then indicate no LED */

	return 0;
}

/****************************************************************************/
/**
* This function is the Interrupt Service Routine for the GPIO device.  It
* will be called by the processor whenever an interrupt is asserted by the
* device.
*
* This function will detect the push button on the board has changed state
* and then turn on or off the LED.
*
* @param	InstancePtr is the GPIO instance pointer to operate on.
*		It is a void pointer to meet the interface of an interrupt
*		processing function.
*
* @return	None.
*
* @note		None.
*
*****************************************************************************/
void GpioIsr(void *InstancePtr)
{
	XGpio *GpioPtr = (XGpio *)InstancePtr;
	u32 Led;
	u32 LedState;
	u32 Buttons;
	u32 ButtonFound;
	u32 ButtonsChanged = 0;
	static u32 PreviousButtons;

	/*
	 * Disable the interrupt
	 */
	XGpio_InterruptDisable(GpioPtr, BUTTON_INTERRUPT);

	/* Keep track of the number of interrupts that occur */

	InterruptCount++;

	/*
	 * There should not be any other interrupts occuring other than the
	 * the button changes
	 */
	if ((XGpio_InterruptGetStatus(GpioPtr) & BUTTON_INTERRUPT) !=
		BUTTON_INTERRUPT) {
		return;
	}


	/*
	 * Read state of push buttons and determine which ones changed
	 * states from the previous interrupt. Save a copy of the buttons
	 * for the next interrupt
	 */
	Buttons = XGpio_DiscreteRead(GpioPtr, BUTTON_CHANNEL);
	ButtonsChanged = Buttons ^ PreviousButtons;
	PreviousButtons = Buttons;

	/*
	 * Handle all button state changes that occurred since the last
	 * interrupt
	 */
	while (ButtonsChanged != 0) {
		/*
		 * Determine which button changed state and then get
		 * the current state of the associated LED
		 */
		 Led = MapButton2Led(ButtonsChanged, &ButtonFound);
		 LedState = XGpio_DiscreteRead(GpioPtr, LED_CHANNEL) & Led;

		 /*
		  * Clear the button that is being processed so that it is
		  * done and others can be handled also
		  */
		 ButtonsChanged &= ~ButtonFound;

		 /* Toggle the state of the LED */
		 if (LedState) {
			 XGpio_DiscreteClear(GpioPtr, LED_CHANNEL, Led);
		 } else {
			 XGpio_DiscreteSet(GpioPtr, LED_CHANNEL, Led);
		 }
	 }

	 /* Clear the interrupt such that it is no longer pending in the GPIO */

	 (void)XGpio_InterruptClear(GpioPtr, BUTTON_INTERRUPT);

	 /*
	  * Enable the interrupt
	  */
	 XGpio_InterruptEnable(GpioPtr, BUTTON_INTERRUPT);

}

/****************************************************************************/
/**
* This function sets up the interrupt system for the example.  The processing
* contained in this funtion assumes the hardware system was built with
* and interrupt controller.
*
* @param	None.
*
* @return	A status indicating XST_SUCCESS or a value that is contained in
*		xstatus.h.
*
* @note		None.
*
*****************************************************************************/
int SetupInterruptSystem()
{
	int Result;
	INTC *IntcInstancePtr = &Intc;

#ifdef XPAR_INTC_0_DEVICE_ID
	/*
	 * Initialize the interrupt controller driver so that it's ready to use.
	 * specify the device ID that was generated in xparameters.h
	 */
	Result = XIntc_Initialize(IntcInstancePtr, INTC_DEVICE_ID);
	if (Result != XST_SUCCESS) {
		return Result;
	}

	/* Hook up interrupt service routine */
	XIntc_Connect(IntcInstancePtr, INTC_GPIO_INTERRUPT_ID,
		      (Xil_ExceptionHandler)GpioIsr, &Gpio);

	/* Enable the interrupt vector at the interrupt controller */

	XIntc_Enable(IntcInstancePtr, INTC_GPIO_INTERRUPT_ID);

	/*
	 * Start the interrupt controller such that interrupts are recognized
	 * and handled by the processor
	 */
	Result = XIntc_Start(IntcInstancePtr, XIN_REAL_MODE);
	if (Result != XST_SUCCESS) {
		return Result;
	}

#else
	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;
	}

	Result = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
					IntcConfig->CpuBaseAddress);
	if (Result != XST_SUCCESS) {
		return XST_FAILURE;
	}

	XScuGic_SetPriorityTriggerType(IntcInstancePtr, INTC_GPIO_INTERRUPT_ID,
					0xA0, 0x3);

	/*
	 * Connect the interrupt handler that will be called when an
	 * interrupt occurs for the device.
	 */
	Result = XScuGic_Connect(IntcInstancePtr, INTC_GPIO_INTERRUPT_ID,
				 (Xil_ExceptionHandler)GpioIsr, &Gpio);
	if (Result != XST_SUCCESS) {
		return Result;
	}

	/*
	 * Enable the interrupt for the GPIO device.
	 */
	XScuGic_Enable(IntcInstancePtr, INTC_GPIO_INTERRUPT_ID);
#endif

	/*
	 * Enable the GPIO channel interrupts so that push button can be
	 * detected and enable interrupts for the GPIO device
	 */
	XGpio_InterruptEnable(&Gpio, BUTTON_INTERRUPT);
	XGpio_InterruptGlobalEnable(&Gpio);

	/*
	 * Initialize the exception table and register the interrupt
	 * controller handler with the exception table
	 */
	Xil_ExceptionInit();

	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
			 (Xil_ExceptionHandler)INTC_HANDLER, IntcInstancePtr);

	/* Enable non-critical exceptions */
	Xil_ExceptionEnable();

	return XST_SUCCESS;
}