/*****************************************************************************
*
* \file
*
* \brief GPIO software driver interface for AVR UC3.
*
* Copyright (c) 2010-2012 Atmel Corporation. All rights reserved.
*
* \asf_license_start
*
* \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 microcontroller 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.
*
* \asf_license_stop
*
*****************************************************************************/

#include "gpio.h"

/** \name Peripheral Bus Interface
 *
 * @{
 */

/** \brief Enables specific module modes for a set of pins.
 *
 * \param gpiomap The pin map.
 * \param size The number of pins in \a gpiomap.
 *
 * \return \ref GPIO_SUCCESS or \ref GPIO_INVALID_ARGUMENT.
 */
uint32_t gpio_enable_module(const gpio_map_t gpiomap, uint32_t size)
{
	uint32_t status = GPIO_SUCCESS;
	uint32_t i;

	for (i = 0; i < size; i++) {
		status |= gpio_enable_module_pin(gpiomap->pin, gpiomap->function);
		gpiomap++;
	}

	return status;
}

/** \brief Enables a specific module mode for a pin.
 *
 * \note A pin and pin function index can be found in the device part header
 *       file. The \c AVR32_*_PIN constants map a GPIO number from the device
 *       datasheet to the appropriate pin function, while the corresponding
 *       \c AVR32_*_FUNCTION macro contains the appropriate function index.
 *       \n\n
 *       For example, the constants \c AVR32_PWM_3_PIN and
 *       \c AVR32_PWM_3_FUNCTION contain the pin and function index of the PWM
 *       module, channel 3, for the current device (if available).
 *
 * \param pin The pin number.
 * \param function The pin function.
 *
 * \return \ref GPIO_SUCCESS or \ref GPIO_INVALID_ARGUMENT.
 */
uint32_t gpio_enable_module_pin(uint32_t pin, uint32_t function)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];

	/* Enable the correct function. */
	switch (function) {
	case 0: /* A function. */
		gpio_port->pmr0c = 1 << (pin & 0x1F);
		gpio_port->pmr1c = 1 << (pin & 0x1F);
#if (AVR32_GPIO_H_VERSION >= 210)
		gpio_port->pmr2c = 1 << (pin & 0x1F);
#endif
		break;

	case 1: /* B function. */
		gpio_port->pmr0s = 1 << (pin & 0x1F);
		gpio_port->pmr1c = 1 << (pin & 0x1F);
#if (AVR32_GPIO_H_VERSION >= 210)
		gpio_port->pmr2c = 1 << (pin & 0x1F);
#endif
		break;

	case 2: /* C function. */
		gpio_port->pmr0c = 1 << (pin & 0x1F);
		gpio_port->pmr1s = 1 << (pin & 0x1F);
#if (AVR32_GPIO_H_VERSION >= 210)
		gpio_port->pmr2c = 1 << (pin & 0x1F);
#endif
		break;

	case 3: /* D function. */
		gpio_port->pmr0s = 1 << (pin & 0x1F);
		gpio_port->pmr1s = 1 << (pin & 0x1F);
#if (AVR32_GPIO_H_VERSION >= 210)
		gpio_port->pmr2c = 1 << (pin & 0x1F);
#endif
		break;

#if (AVR32_GPIO_H_VERSION >= 210)
	case 4: /* E function. */
		gpio_port->pmr0c = 1 << (pin & 0x1F);
		gpio_port->pmr1c = 1 << (pin & 0x1F);
		gpio_port->pmr2s = 1 << (pin & 0x1F);
		break;

	case 5: /* F function. */
		gpio_port->pmr0s = 1 << (pin & 0x1F);
		gpio_port->pmr1c = 1 << (pin & 0x1F);
		gpio_port->pmr2s = 1 << (pin & 0x1F);
		break;

	case 6: /* G function. */
		gpio_port->pmr0c = 1 << (pin & 0x1F);
		gpio_port->pmr1s = 1 << (pin & 0x1F);
		gpio_port->pmr2s = 1 << (pin & 0x1F);
		break;

	case 7: /* H function. */
		gpio_port->pmr0s = 1 << (pin & 0x1F);
		gpio_port->pmr1s = 1 << (pin & 0x1F);
		gpio_port->pmr2s = 1 << (pin & 0x1F);
		break;
#endif

	default:
		return GPIO_INVALID_ARGUMENT;
	}

	/* Disable GPIO control. */
	gpio_port->gperc = 1 << (pin & 0x1F);

	return GPIO_SUCCESS;
}

/** \brief Enables the GPIO mode of a set of pins.
 *
 * \param gpiomap The pin map.
 * \param size The number of pins in \a gpiomap.
 */
void gpio_enable_gpio(const gpio_map_t gpiomap, uint32_t size)
{
	uint32_t i;

	for (i = 0; i < size; i++) {
		gpio_enable_gpio_pin(gpiomap->pin);
		gpiomap++;
	}
}

/** \brief Enables the GPIO mode of a pin.
 *
 * \param pin The pin number.\n
 *            Refer to the product header file `uc3x.h' (where x is the part
 *            number; e.g. x = a0512) for pin definitions. E.g., to enable the
 *            GPIO mode of PX21, AVR32_PIN_PX21 can be used. Module pins such as
 *            AVR32_PWM_3_PIN for PWM channel 3 can also be used to release
 *            module pins for GPIO.
 */
void gpio_enable_gpio_pin(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];
	
	gpio_port->oderc = 1 << (pin & 0x1F);
	gpio_port->gpers = 1 << (pin & 0x1F);
}

/** \brief Enables the pull-up resistor of a pin.
 *
 * \param pin The pin number.
 */
void gpio_enable_pin_pull_up(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];
	
	gpio_port->puers = 1 << (pin & 0x1F);
#if defined(AVR32_GPIO_200_H_INCLUDED) || defined(AVR32_GPIO_210_H_INCLUDED) ||	\
	defined(AVR32_GPIO_212_H_INCLUDED)
	gpio_port->pderc = 1 << (pin & 0x1F);
#endif
}

/** \brief Disables the pull-up resistor of a pin.
 *
 * \param pin The pin number.
 */
void gpio_disable_pin_pull_up(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];
	
	gpio_port->puerc = 1 << (pin & 0x1F);
}

#if defined(AVR32_GPIO_200_H_INCLUDED) || defined(AVR32_GPIO_210_H_INCLUDED) ||	\
	defined(AVR32_GPIO_212_H_INCLUDED)
/* Added support of Pull-up Resistor, Pull-down Resistor and Buskeeper Control. */

/** \brief Enables the pull-down resistor of a pin.
 *
 * \param pin The pin number.
 */
void gpio_enable_pin_pull_down(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];
	
	gpio_port->puerc = 1 << (pin & 0x1F);
	gpio_port->pders = 1 << (pin & 0x1F);
}

/** \brief Disables the pull-down resistor of a pin.
 *
 * \param pin The pin number.
 */
void gpio_disable_pin_pull_down(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];
	
	gpio_port->pderc = 1 << (pin & 0x1F);
}

/** \brief Enables the buskeeper functionality on a pin.
 *
 * \param pin The pin number.
 */
void gpio_enable_pin_buskeeper(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];
	
	gpio_port->puers = 1 << (pin & 0x1F);
	gpio_port->pders = 1 << (pin & 0x1F);
}

/** \brief Disables the buskeeper functionality on a pin.
 *
 * \param pin The pin number.
 */
void gpio_disable_pin_buskeeper(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];
	
	gpio_port->puerc = 1 << (pin & 0x1F);
	gpio_port->pderc = 1 << (pin & 0x1F);
}

#endif

/** \brief Configuration functionality on a pin.
 *
 * \param pin The pin number.
 * \param flags The configuration.
 */
void gpio_configure_pin(uint32_t pin, uint32_t flags)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];

	/* Both pull-up and pull-down set means buskeeper */
#if defined(AVR32_GPIO_200_H_INCLUDED) || defined(AVR32_GPIO_210_H_INCLUDED) ||	\
	defined(AVR32_GPIO_212_H_INCLUDED)
	if (flags & GPIO_PULL_DOWN) {
		gpio_port->pders = 1 << (pin & 0x1F);
	} else {
		gpio_port->pderc = 1 << (pin & 0x1F);
	}

#endif
	if (flags & GPIO_PULL_UP) {
		gpio_port->puers = 1 << (pin & 0x1F);
	} else {
		gpio_port->puerc = 1 << (pin & 0x1F);
	}

	/* Enable open-drain mode if requested */
#if defined(AVR32_GPIO_200_H_INCLUDED) || defined(AVR32_GPIO_210_H_INCLUDED) ||	\
	defined(AVR32_GPIO_212_H_INCLUDED)
	if (flags & GPIO_OPEN_DRAIN) {
		gpio_port->odmers = 1 << (pin & 0x1F);
	} else {
		gpio_port->odmerc = 1 << (pin & 0x1F);
	}

#endif

#if defined(AVR32_GPIO_200_H_INCLUDED) || defined(AVR32_GPIO_210_H_INCLUDED) ||	\
	defined(AVR32_GPIO_212_H_INCLUDED)
	/* Select drive strength */
	if (flags & GPIO_DRIVE_LOW) {
		gpio_port->odcr0s = 1 << (pin & 0x1F);
	} else {
		gpio_port->odcr0c = 1 << (pin & 0x1F);
	}

	if (flags & GPIO_DRIVE_HIGH) {
		gpio_port->odcr1s = 1 << (pin & 0x1F);
	} else {
		gpio_port->odcr1c = 1 << (pin & 0x1F);
	}

#endif

	/* Select interrupt level for group */
	if (flags & GPIO_INTERRUPT) {
		if (flags & GPIO_BOTHEDGES) {
			gpio_port->imr0c = 1 << (pin & 0x1F);
			gpio_port->imr1c = 1 << (pin & 0x1F);
		} else if (flags & GPIO_RISING) {
			gpio_port->imr0s = 1 << (pin & 0x1F);
			gpio_port->imr1c = 1 << (pin & 0x1F);
		} else if (flags & GPIO_FALLING) {
			gpio_port->imr0c = 1 << (pin & 0x1F);
			gpio_port->imr1s = 1 << (pin & 0x1F);
		}
	}

	/* Select direction and initial pin state */
	if (flags & GPIO_DIR_OUTPUT) {
		if (flags & GPIO_INIT_HIGH) {
			gpio_port->ovrs = 1 << (pin & 0x1F);
		} else {
			gpio_port->ovrc = 1 << (pin & 0x1F);
		}

		gpio_port->oders = 1 << (pin & 0x1F);
	} else {
		gpio_port->oderc = 1 << (pin & 0x1F);
	}

	/* Enable GPIO */
	gpio_port->gpers = 1 << (pin & 0x1F);
}

/** \brief Configuration functionality on a port.
 *
 * \param port The port number.
 * \param mask The mask.
 * \param flags The configuration.
 */
void gpio_configure_group(uint32_t port, uint32_t mask, uint32_t flags)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[port];

	/* Both pull-up and pull-down set means buskeeper */
#if defined(AVR32_GPIO_200_H_INCLUDED) || defined(AVR32_GPIO_210_H_INCLUDED) ||	\
	defined(AVR32_GPIO_212_H_INCLUDED)
	if (flags & GPIO_PULL_DOWN) {
		gpio_port->pders = mask;
	} else {
		gpio_port->pderc = mask;
	}

#endif
	if (flags & GPIO_PULL_UP) {
		gpio_port->puers = mask;
	} else {
		gpio_port->puerc = mask;
	}

	/* Enable open-drain mode if requested */
#if defined(AVR32_GPIO_200_H_INCLUDED) || defined(AVR32_GPIO_210_H_INCLUDED) ||	\
	defined(AVR32_GPIO_212_H_INCLUDED)
	if (flags & GPIO_OPEN_DRAIN) {
		gpio_port->odmers = mask;
	} else {
		gpio_port->odmerc = mask;
	}

	if (flags & GPIO_OPEN_DRAIN) {
		gpio_port->pders = mask;
	} else {
		gpio_port->pderc = mask;
	}

#endif

#if defined(AVR32_GPIO_200_H_INCLUDED) || defined(AVR32_GPIO_210_H_INCLUDED) ||	\
	defined(AVR32_GPIO_212_H_INCLUDED)
	/* Select drive strength */
	if (flags & GPIO_DRIVE_LOW) {
		gpio_port->odcr0s = mask;
	} else {
		gpio_port->odcr0c = mask;
	}

	if (flags & GPIO_DRIVE_HIGH) {
		gpio_port->odcr1s = mask;
	} else {
		gpio_port->odcr1c = mask;
	}

#endif

	/* Select interrupt level for group */
	if (flags & GPIO_INTERRUPT) {
		if (flags & GPIO_BOTHEDGES) {
			gpio_port->imr0c = mask;
			gpio_port->imr1c = mask;
		} else if (flags & GPIO_RISING) {
			gpio_port->imr0s = mask;
			gpio_port->imr1c = mask;
		} else if (flags & GPIO_FALLING) {
			gpio_port->imr0c = mask;
			gpio_port->imr1s = mask;
		}
	}

	/* Select direction and initial pin state */
	if (flags & GPIO_DIR_OUTPUT) {
		if (flags & GPIO_INIT_HIGH) {
			gpio_port->ovrs = mask;
		} else {
			gpio_port->ovrc = mask;
		}

		gpio_port->oders = mask;
	} else {
		gpio_port->oderc = mask;
	}

	/* Enable GPIO */
	gpio_port->gpers = mask;
}

/** \brief Returns the value of a pin.
 *
 * \param pin The pin number.
 *
 * \return The pin value.
 */
bool gpio_get_pin_value(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];
	
	return (gpio_port->pvr >> (pin & 0x1F)) & 1;
}

/** \brief Returns the output value set for a GPIO pin.
 *
 * \param pin The pin number.
 *
 * \return The pin output value.
 *
 * \note This function must be used in conjunction with \ref gpio_set_gpio_pin,
 *       \ref gpio_clr_gpio_pin and \ref gpio_tgl_gpio_pin.
 */
bool gpio_get_gpio_pin_output_value(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];
	
	return (gpio_port->ovr >> (pin & 0x1F)) & 1;
}

/** \brief Returns the output value set for a GPIO pin using open drain.
 *
 * \param pin The pin number.
 *
 * \return The pin output value.
 *
 * \note This function must be used in conjunction with
 *       \ref gpio_set_gpio_open_drain_pin, \ref gpio_clr_gpio_open_drain_pin
 *       and \ref gpio_tgl_gpio_open_drain_pin.
 */
bool gpio_get_gpio_open_drain_pin_output_value(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];
	
	return ((gpio_port->oder >> (pin & 0x1F)) & 1) ^ 1;
}

/** \brief Drives a GPIO pin to 1.
 *
 * \param pin The pin number.
 */
void gpio_set_gpio_pin(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];
	
	/* Value to be driven on the I/O line: 1. */
	gpio_port->ovrs  = 1 << (pin & 0x1F);
	/* The GPIO output driver is enabled for that pin. */ 
	gpio_port->oders = 1 << (pin & 0x1F);
	/* The GPIO module controls that pin. */
	gpio_port->gpers = 1 << (pin & 0x1F);
}

/** \brief Drives a GPIO pin to 1.
 *
 * \param pin The pin number.
 *
 * \note The function \ref gpio_configure_pin must be called before.
 */
void gpio_set_pin_high(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];
	
	/* Value to be driven on the I/O line: 1. */
	gpio_port->ovrs  = 1 << (pin & 0x1F);
}

/** \brief Drives a GPIO port to 1.
 *
 * \param port The port number.
 * \param mask The mask.
 */
void gpio_set_group_high(uint32_t port, uint32_t mask)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[port];
	
	/* Value to be driven on the I/O group: 1. */
	gpio_port->ovrs  = mask;
}

/** \brief Drives a GPIO pin to 0.
 *
 * \param pin The pin number.
 *
 * \note The function \ref gpio_configure_pin must be called before.
 */
void gpio_set_pin_low(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];
	
	/* Value to be driven on the I/O line: 0. */
	gpio_port->ovrc  = 1 << (pin & 0x1F);
}

/** \brief Drives a GPIO pin to 0.
 *
 * \param pin The pin number.
 */
void gpio_clr_gpio_pin(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];
	
	/* Value to be driven on the I/O line: 0. */
	gpio_port->ovrc  = 1 << (pin & 0x1F);
	/* The GPIO output driver is enabled for that pin. */
	gpio_port->oders = 1 << (pin & 0x1F);
	/* The GPIO module controls that pin. */
	gpio_port->gpers = 1 << (pin & 0x1F);
}

/** \brief Drives a GPIO port to 0.
 *
 * \param port The port number.
 * \param mask The mask.
 */
void gpio_set_group_low(uint32_t port, uint32_t mask)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[port];
	
	/* Value to be driven on the I/O group: 0. */
	gpio_port->ovrc  = mask;
}

/** \brief Toggles a GPIO pin.
 *
 * \param pin The pin number.
 */
void gpio_tgl_gpio_pin(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];
	
	/* Toggle the I/O line. */
	gpio_port->ovrt  = 1 << (pin & 0x1F);
	/* The GPIO output driver is enabled for that pin. */
	gpio_port->oders = 1 << (pin & 0x1F);
	/* The GPIO module controls that pin. */
	gpio_port->gpers = 1 << (pin & 0x1F);
}

/** \brief Toggles a GPIO pin.
 *
 * \param pin The pin number.
 *
 * \note The function \ref gpio_configure_pin must be called before.
 */
void gpio_toggle_pin(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];
	
	/* Toggle the I/O line. */
	gpio_port->ovrt  = 1 << (pin & 0x1F);
}

/** \brief Toggles a GPIO group.
 *
 * \param port The port number.
 * \param mask The mask.
 */
void gpio_toggle_group(uint32_t port, uint32_t mask)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[port];
	
	/* Toggle the I/O port. */
	gpio_port->ovrt  = mask;
}

/** \brief Drives a GPIO pin to 1 using open drain.
 *
 * \param pin The pin number.
 */
void gpio_set_gpio_open_drain_pin(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];

	/* The GPIO output driver is disabled for that pin. */
	gpio_port->oderc = 1 << (pin & 0x1F);
	/* The GPIO module controls that pin. */
	gpio_port->gpers = 1 << (pin & 0x1F);
}

/** \brief Drives a GPIO pin to 0 using open drain.
 *
 * \param pin The pin number.
 */
void gpio_clr_gpio_open_drain_pin(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];

	/* Value to be driven on the I/O line: 0. */
	gpio_port->ovrc  = 1 << (pin & 0x1F);
	/* The GPIO output driver is enabled for that pin. */
	gpio_port->oders = 1 << (pin & 0x1F);
	/* The GPIO module controls that pin. */
	gpio_port->gpers = 1 << (pin & 0x1F);
}

/** \brief Toggles a GPIO pin using open drain.
 *
 * \param pin The pin number.
 */
void gpio_tgl_gpio_open_drain_pin(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];

	/* Value to be driven on the I/O line if the GPIO output driver is
	 * enabled: 0. */
	gpio_port->ovrc  = 1 << (pin & 0x1F);
	/* The GPIO output driver is toggled for that pin. */
	gpio_port->odert = 1 << (pin & 0x1F);
	/* The GPIO module controls that pin. */
	gpio_port->gpers = 1 << (pin & 0x1F);
}

/** \brief Enables the glitch filter of a pin.
 *
 * When the glitch filter is enabled, a glitch with duration of less than 1
 * clock cycle is automatically rejected, while a pulse with duration of 2 clock
 * cycles or more is accepted. For pulse durations between 1 clock cycle and 2
 * clock cycles, the pulse may or may not be taken into account, depending on
 * the precise timing of its occurrence. Thus for a pulse to be guaranteed
 * visible it must exceed 2 clock cycles, whereas for a glitch to be reliably
 * filtered out, its duration must not exceed 1 clock cycle. The filter
 * introduces 2 clock cycles latency.
 *
 * \param pin The pin number.
 */
void gpio_enable_pin_glitch_filter(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];
	
	gpio_port->gfers = 1 << (pin & 0x1F);
}

/** \brief Disables the glitch filter of a pin.
 *
 * \param pin The pin number.
 */
void gpio_disable_pin_glitch_filter(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];
	
	gpio_port->gferc = 1 << (pin & 0x1F);
}

/** \brief Configure the edge detector of an input pin
 *
 * \param pin The pin number.
 * \param mode The edge detection mode (\ref GPIO_PIN_CHANGE,
 *             \ref GPIO_RISING_EDGE or \ref GPIO_FALLING_EDGE).
 *
 * \return \ref GPIO_SUCCESS or \ref GPIO_INVALID_ARGUMENT.
 */
static uint32_t gpio_configure_edge_detector(uint32_t pin, uint32_t mode)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];

	/* Configure the edge detector. */
	switch (mode) {
	case GPIO_PIN_CHANGE:
		gpio_port->imr0c = 1 << (pin & 0x1F);
		gpio_port->imr1c = 1 << (pin & 0x1F);
		break;

	case GPIO_RISING_EDGE:
		gpio_port->imr0s = 1 << (pin & 0x1F);
		gpio_port->imr1c = 1 << (pin & 0x1F);
		break;

	case GPIO_FALLING_EDGE:
		gpio_port->imr0c = 1 << (pin & 0x1F);
		gpio_port->imr1s = 1 << (pin & 0x1F);
		break;

	default:
		return GPIO_INVALID_ARGUMENT;
	}

	return GPIO_SUCCESS;
}

/** \brief Enables the interrupt of a pin with the specified settings.
 *
 * \param pin The pin number.
 * \param mode The trigger mode (\ref GPIO_PIN_CHANGE, \ref GPIO_RISING_EDGE or
 *             \ref GPIO_FALLING_EDGE).
 *
 * \return \ref GPIO_SUCCESS or \ref GPIO_INVALID_ARGUMENT.
 */
uint32_t gpio_enable_pin_interrupt(uint32_t pin, uint32_t mode)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];

	/* Enable the glitch filter. */
	gpio_port->gfers = 1 << (pin & 0x1F);

	/* Configure the edge detector. */
	if (GPIO_INVALID_ARGUMENT == gpio_configure_edge_detector(pin, mode)) {
		return(GPIO_INVALID_ARGUMENT);
	}

	/* Enable interrupt. */
	gpio_port->iers = 1 << (pin & 0x1F);

	return GPIO_SUCCESS;
}

/** \brief Disables the interrupt of a pin.
 *
 * \param pin The pin number.
 */
void gpio_disable_pin_interrupt(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];
	
	gpio_port->ierc = 1 << (pin & 0x1F);
}

/** \brief Gets the interrupt flag of a pin.
 *
 * \param pin The pin number.
 *
 * \return The pin interrupt flag.
 */
bool gpio_get_pin_interrupt_flag(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];
	
	return (gpio_port->ifr >> (pin & 0x1F)) & 1;
}

/** \brief Clears the interrupt flag of a pin.
 *
 * \param pin The pin number.
 */
void gpio_clear_pin_interrupt_flag(uint32_t pin)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];

#if (AVR32_GPIO_H_VERSION == 211)
	/* GPIO erratum - Writing a one to the GPIO.IFRC register */
	/* to clear an interrupt will be ignored if interrupt is enabled for the */
	/* corresponding port. */
	/* Work around for the erratum - Disable the interrupt, clear it by
	 * writing */
	/* a one to GPIO.IFRC, then enable the interrupt. */

	/* Save interrupt enable register. */
	uint32_t const gpio_ier = gpio_port->ier;

	/* Disable interrupt. */
	gpio_port->ierc = gpio_ier;

	/* Clear pin interrupt. */
	gpio_port->ifrc = 1 << (pin & 0x1F);

	/* Restore interrupt enable register. */
	gpio_port->ier = gpio_ier;
#else
	gpio_port->ifrc = 1 << (pin & 0x1F);
#endif
}

#if UC3L

/** \brief Configure the peripheral event trigger mode of a pin
 *
 * \param pin The pin number.
 * \param mode The trigger mode (\ref GPIO_PIN_CHANGE, \ref GPIO_RISING_EDGE or
 *             \ref GPIO_FALLING_EDGE).
 * \param use_igf use the Input Glitch Filter (true) or not (false).
 *
 * \return \ref GPIO_SUCCESS or \ref GPIO_INVALID_ARGUMENT.
 */
uint32_t gpio_configure_pin_periph_event_mode(uint32_t pin, uint32_t mode,
		uint32_t use_igf)
{
	volatile avr32_gpio_port_t *gpio_port = &AVR32_GPIO.port[pin >> 5];

	if (true == use_igf) {
		/* Enable the glitch filter. */
		gpio_port->gfers = 1 << (pin & 0x1F);
	} else {
		/* Disable the glitch filter. */
		gpio_port->gferc = 1 << (pin & 0x1F);
	}

	/* Configure the edge detector. */
	return gpio_configure_edge_detector(pin, mode);
}

#endif

/** @} */
