/**
 * \file
 *
 * Copyright (C) 2009 Atmel Corporation. All rights reserved.
 *
 * \page License
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. The name of Atmel may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an
 * Atmel AVR product.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */
/*!
 * \file
 *
 * \brief Interrupt handler support.
 *
 * This file includes macros and helper functions for setting up
 * interrupt handlers.
 *
 * - Compiler:           GNU GCC for AVR32
 * - Supported devices:  All AVR32 devices
 * - AppNote:
 *
 * \author               Atmel Corporation: http://www.atmel.com \n
 *                       Support and FAQ: http://support.atmel.no/
 *
 * \page License
 *
 * Copyright (C) 2008, Atmel Corporation All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. The name of ATMEL may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY AND
 * SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#ifndef AVR32_IRQ_HANDLER_H_INCLUDED
#define AVR32_IRQ_HANDLER_H_INCLUDED

#include <io.h>
#include <util.h>
#include <chip/memory-map.h>
#include <cpu/irq_handler.h>

#define irq_entry_symbol(name)		name##_irq_entry
#define irq_entry_symbol_str(name)	xstr(name##_irq_entry)
#define irq_data_symbol(name)		name##_irq_data
#define irq_data_symbol_str(name)	xstr(name##_irq_data)

#define __irq_entry_section(name)					\
	".libavr32.text.irq." #name ", \"ax\", @progbits"
#define __irq_data_section(name)					\
	".libavr32.bss.irq." #name ", \"aw\", @nobits"
#define __irq_entry_size(name)						\
	irq_entry_symbol_str(name) ", . - " irq_entry_symbol_str(name)
#define __irq_data_size(name)						\
	irq_data_symbol_str(name) ", . - " irq_data_symbol_str(name)

/**
 * \brief Define an interrupt handler for an integrated peripheral.
 *
 * This binds an interrupt handler function to a low-level entry
 * point, which takes care of saving/restoring registers, finding the
 * handler-specific data, and running any code dealing with
 * scheduling, soft interrupt handling, etc.  before returning.
 *
 * \note This is not a function-like macro; it should not be called
 * from within functions. Instead, it should be called from the
 * top-level right after the definition of the interrupt handler
 * function.
 *
 * \param name An arbitrary name associated with this interrupt.
 * \param handler The interrupt handler function.
 * \param level The interrupt priority level for this interrupt.
 */
#define DEFINE_IRQ_HANDLER(name, handler, level)			\
	extern void irq_entry_symbol(name)(void);			\
	extern void *irq_data_symbol(name);				\
	static void __used __dummy_irq_ref_##name(void)			\
	{								\
		handler(&handler);					\
	}								\
	asm(".section " __irq_entry_section(name) "\n"			\
		"	.global	" irq_entry_symbol_str(name) "\n"	\
		"	.type	" irq_entry_symbol_str(name) ", @function\n" \
		irq_entry_symbol_str(name) ":\n"			\
		CPU_SAVE_IRQ_REGS(level)				\
		"	lda.w	r8, " irq_data_symbol_str(name) "\n"	\
		"	ld.w	r12, r8[0]\n"				\
		"	rcall	" #handler "\n"				\
		"	rjmp	irq_level" #level "_return\n"		\
		"	.size	" __irq_entry_size(name) "\n"		\
		"	.previous\n"					\
		"	.section " __irq_data_section(name) "\n"	\
		"	.global	" irq_data_symbol_str(name) "\n"	\
		"	.type	" irq_data_symbol_str(name) ", @object\n" \
		irq_data_symbol_str(name) ":\n"				\
		"	.long	0\n"					\
		"	.size	" __irq_data_size(name) "\n"		\
		"	.previous")

/**
 * \brief Associate a data pointer with an interrupt.
 *
 * \param name The name of the interrupt, as given to define_irq_handler().
 * \param data Data to be associated with the interrupt.
 */
#define set_irq_data(name, data)					\
	do {								\
		irq_data_symbol(name) = (data);				\
	} while (0)

extern void __setup_irq_handler(int irq, void (*entry)(void),
		void **pdata, unsigned int level, void *data);

/**
 * \brief Set up an interrupt handler.
 *
 * This function sets up the internal interrupt controller for
 * handling a given interrupt through the specified handler.
 *
 * \note Unlike define_irq_handler(), this _is_ a function-like macro.
 *
 * \note Usually, this is the only reference to the handler function
 * and associated low-level entry code. This means that it is
 * perfectly ok to define handlers for more interrupts than needed --
 * if they aren't actually enabled by calling this function, they will
 * get discarded at link-time when --gc-sections is used.
 *
 * \param irq The interrupt number for the handler.
 * \param name The name associated with the interrupt.
 * \param level The priority level for this interrupt.
 * \param data Data to be associated with this interrupt.
 *
 * \pre A handler for this interrupt has not already been set up.
 * \pre \a level must be between 0 and 3 inclusive.
 * \pre The low-level entry point must be placed less than 16k after EVBA.
 */
#define setup_irq_handler(irq, name, level, data)			\
	do {								\
		extern void irq_entry_symbol(name)(void);		\
		extern void *irq_data_symbol(name);			\
		__setup_irq_handler(irq, &irq_entry_symbol(name),	\
				&irq_data_symbol(name), level, data);	\
	} while (0)

/**
 * \brief Remove an interrupt handler.
 *
 * This will remove a registered interrupt handler, making it possible
 * to set up a different one by calling setup_irq_handler().
 *
 * \note This function doesn't actually reprogram the interrupt
 * controller; it just makes it possible to register a different
 * handler for this interrupt later.
 *
 * \param name The name associated with the interrupt.
 */
#define remove_irq_handler(name)					\
	do {								\
		extern void *irq_data_symbol(name);			\
		irq_data_symbol(name) = 0;				\
	} while (0)

/**
 * \brief Get the pending interrupt mask for \a group
 */
static inline unsigned long get_irq_group_requests(unsigned int group)
{
	return mmio_read32((void *)(INTC_BASE + 256 + 4 * group));
}

#endif /* AVR32_IRQ_HANDLER_H_INCLUDED */
