/**
 * \file
 *
 * \brief Architecture-specific soft interrupt definitions
 *
 * - Compiler:           IAR EWAVR32 and 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
 *
 * 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.
 */
#ifndef ARCH_SOFTIRQ_H_INCLUDED
#define ARCH_SOFTIRQ_H_INCLUDED

#include <bitops.h>
#include <compiler.h>
#include <arch/sysreg.h>

/**
 * \brief Soft interrupt handler function.
 * \param data Data pointer that was passed to softirq_set_handler().
 */
typedef void (*softirq_handler_t)(void *data);

extern unsigned long softirq_priv_status;

extern void softirq_set_handler(unsigned int id,
		softirq_handler_t handler, void *data);
extern void softirq_priv_do_pending(void);
extern void softirq_enable(void);

/**
 * \brief Disable softirq processing
 *
 * No pending softirqs will be processed until softirq_enable() is
 * called.
 */
static inline void softirq_disable(void)
{
	avr32_set_sr_bit(SYSREG_SR_T_BIT);
	barrier();
}

/**
 * \brief Raise the soft interrupt \a id
 *
 * Calling this function will flag soft interrupt \a id as active, which
 * will cause the associated handler to be run at the first opportunity.
 *
 * Note that if this function is called from a non-interrupt context
 * with softirqs enabled, it is undefined when the handler will actually
 * be run.
 */
static inline void softirq_raise(unsigned int id)
{
	atomic_set_bit(id, &softirq_priv_status);
}

/**
 * \brief Test if soft interrupts are enabled in \a flags
 * \param flags Saved soft interrupt state
 * \retval true If \a flags indicates soft interrups are enabled
 * \retval false If \a flags indicates soft interrupts are disabled
 */
static inline bool softirq_is_enabled_flags(unsigned long flags)
{
	return !(flags & SYSREG_SR_T);
}

/**
 * \brief Test if soft interrupts are enabled
 * \retval true If soft interrups are enabled
 * \retval false If soft interrupts are disabled
 */
static inline bool softirq_is_enabled(void)
{
	return softirq_is_enabled_flags(sysreg_read(SR));
}

/**
 * \brief Save the current soft interrupt sate and disable soft
 * interrupts.
 *
 * \return A cookie indicating the soft interrupt state before
 * disabling.
 */
static inline unsigned long softirq_save(void)
{
	unsigned long flags;

	flags = sysreg_read(SR);
	softirq_disable();

	return flags;
}

/**
 * \brief Restore saved soft interrupt state
 * \param flags A cookie returned by softirq_save()
 */
static inline void softirq_restore(unsigned long flags)
{
	/*
	 * We can't just write directly to SR as we do for interrupts,
	 * since we must also check for pending softirqs.
	 */
	if (softirq_is_enabled_flags(flags))
		softirq_enable();
}

#endif /* ARCH_SOFTIRQ_H_INCLUDED */
