/****************************************************************************
* arch/avr/src/at91uc3/at91uc3_gpioirq.c
* arch/avr/src/chip/at91uc3_gpioirq.c
*
* Copyright (C) 2010 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
*
* 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. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include "at91uc3_config.h"
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <arch/irq.h>
#include "up_arch.h"
#include "os_internal.h"
#include "irq_internal.h"
#include "at91uc3_internal.h"
#include "at91uc3_gpio.h"
#ifdef CONFIG_AVR32_GPIOIRQ
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Data
****************************************************************************/
/* A table of handlers for each GPIO interrupt */
static FAR xcpt_t g_gpiohandler[NR_GPIO_IRQS];
/****************************************************************************
* Public Data
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: gpio_baseaddress
*
* Input:
* irq - A IRQ number in the range of 0 to NR_GPIO_IRQS.
*
* Description:
* Given a IRQ number, return the base address of the associated GPIO
* registers.
*
****************************************************************************/
static inline uint32_t gpio_baseaddress(unsigned int irq)
{
#if CONFIG_AVR32_GPIOIRQSETA != 0
if (irq < __IRQ_GPIO_PB0)
{
return AVR32_GPIO0_BASE;
}
else
#endif
#if CONFIG_AVR32_GPIOIRQSETB != 0
if (irq < NR_GPIO_IRQS)
{
return AVR32_GPIO1_BASE;
}
else
#endif
{
return 0;
}
}
/****************************************************************************
* Name: gpio_pin
*
* Input:
* irq - A IRQ number in the range of 0 to NR_GPIO_IRQS.
*
* Description:
* Given a GPIO number, return the pin number in the range of 0-31 on the
* corresponding port
*
****************************************************************************/
static inline int gpio_pin(unsigned int irq)
{
uint32_t pinset;
int pinirq;
int pin;
#if CONFIG_AVR32_GPIOIRQSETA != 0
if (irq < __IRQ_GPIO_PB0)
{
pinset = CONFIG_AVR32_GPIOIRQSETA;
pinirq = __IRQ_GPIO_PA0;
}
else
#endif
#if CONFIG_AVR32_GPIOIRQSETB != 0
if (irq < NR_GPIO_IRQS)
{
pinset = CONFIG_AVR32_GPIOIRQSETB;
pinirq = __IRQ_GPIO_PB0;
}
else
#endif
{
return -EINVAL;
}
/* Now we have to search for the pin with matching IRQ. Yech! We made
* life difficult here by choosing a sparse representation of IRQs on
* GPIO pins.
*/
for (pin = 0; pin < 32 && pinset != 0; pin++)
{
/* Is this pin at bit 0 configured for interrupt support? */
if ((pinset & 1) != 0)
{
/* Is it the on IRQ we are looking for? */
if (pinirq == irq)
{
/* Yes, return the associated pin number */
return pin;
}
/* No.. Increment the IRQ number for the next configured pin */
pinirq++;
}
/* Shift the next pin to position bit 0 */
pinset >>= 1;
}
return -EINVAL;
}
/****************************************************************************
* Name: gpio_porthandler
*
* Description:
* Dispatch GPIO interrupts on a specific GPIO port
*
****************************************************************************/
static void gpio_porthandler(uint32_t regbase, int irqbase, uint32_t irqset, void *context)
{
uint32_t ifr;
int irq;
int pin;
/* Check each bit and dispatch each pending interrupt in the interrupt flag
* register for this port.
*/
ifr = getreg32(regbase + AVR32_GPIO_IFR_OFFSET);
/* Dispatch each pending interrupt */
irq = irqbase;
for (pin = 0; pin < 32 && ifr != 0; pin++)
{
/* Is this pin configured for interrupt support? */
uint32_t bit = (1 << pin);
if ((irqset & bit) != 0)
{
/* Is an interrupt pending on this pin? */
if ((ifr & bit) != 0)
{
/* Yes.. Clear the pending interrupt */
putreg32(bit, regbase + AVR32_GPIO_IFRC_OFFSET);
ifr &= ~bit;
/* Dispatch handling for this pin */
xcpt_t handler = g_gpiohandler[irq];
if (handler)
{
handler(irq, context);
}
else
{
lldbg("No handler: pin=%d ifr=%08x irqset=%08x",
pin, ifr, irqset);
}
}
/* Increment the IRQ number on all configured pins */
irq++;
}
/* Not configured. An interrupt on this pin would be an error. */
else if ((ifr & bit) != 0)
{
/* Clear the pending interrupt */
putreg32(bit, regbase + AVR32_GPIO_IFRC_OFFSET);
ifr &= ~bit;
lldbg("IRQ on unconfigured pin: pin=%d ifr=%08x irqset=%08x",
pin, ifr, irqset);
}
}
}
/****************************************************************************
* Name: gpio0/1_interrupt
*
* Description:
* Handle GPIO0/1 interrupts
*
****************************************************************************/
#if CONFIG_AVR32_GPIOIRQSETA != 0
static int gpio0_interrupt(int irq, FAR void *context)
{
gpio_porthandler(AVR32_GPIO0_BASE, __IRQ_GPIO_PA0,
CONFIG_AVR32_GPIOIRQSETA, context);
return 0;
}
#endif
#if CONFIG_AVR32_GPIOIRQSETB != 0
static int gpio1_interrupt(int irq, FAR void *context)
{
gpio_porthandler(AVR32_GPIO1_BASE, __IRQ_GPIO_PB0,
CONFIG_AVR32_GPIOIRQSETB, context);
return 0;
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: gpio_irqinitialize
*
* Description:
* Initialize all vectors to the unexpected interrupt handler.
*
* Assumptions:
* Called during the early boot sequence before global interrupts have
* been enabled.
*
****************************************************************************/
void gpio_irqinitialize(void)
{
int i;
/* Point all interrupt vectors to the unexpected interrupt */
for (i = 0; i < NR_GPIO_IRQS; i++)
{
g_gpiohandler[i] = irq_unexpected_isr;
}
/* Then attach the GPIO interrupt handlers */
#if CONFIG_AVR32_GPIOIRQSETA != 0
irq_attach(AVR32_IRQ_GPIO0, gpio0_interrupt);
#endif
#if CONFIG_AVR32_GPIOIRQSETB != 0
irq_attach(AVR32_IRQ_GPIO1, gpio1_interrupt);
#endif
}
/****************************************************************************
* Name: gpio_irqattach
*
* Description:
* Attach in GPIO interrupt to the provide 'isr'
*
****************************************************************************/
int gpio_irqattach(int irq, xcpt_t newisr, xcpt_t *oldisr)
{
irqstate_t flags;
int ret = -EINVAL;
if ((unsigned)irq < NR_GPIO_IRQS)
{
/* If the new ISR is NULL, then the ISR is being detached. In this
* case, disable the ISR and direct any interrupts
* to the unexpected interrupt handler.
*/
flags = irqsave();
if (newisr == NULL)
{
gpio_irqdisable(irq);
newisr = irq_unexpected_isr;
}
/* Return the old ISR (in case the caller ever wants to restore it) */
if (oldisr)
{
*oldisr = g_gpiohandler[irq];
}
/* Then save the new ISR in the table. */
g_gpiohandler[irq] = newisr;
irqrestore(flags);
ret = OK;
}
return ret;
}
/****************************************************************************
* Name: gpio_irqenable
*
* Description:
* Enable the GPIO IRQ specified by 'irq'
*
****************************************************************************/
void gpio_irqenable(int irq)
{
uint32_t base;
int pin;
if ((unsigned)irq < NR_GPIO_IRQS)
{
/* Get the base address of the GPIO module associated with this IRQ */
base = gpio_baseaddress(irq);
/* Get the pin number associated with this IRQ. We made life difficult
* here by choosing a sparse representation of IRQs on GPIO pins.
*/
pin = gpio_pin(irq);
DEBUGASSERT(pin >= 0);
/* Enable the GPIO interrupt. */
putreg32((1 << pin), base + AVR32_GPIO_IERS_OFFSET);
}
}
/****************************************************************************
* Name: gpio_irqdisable
*
* Description:
* Disable the GPIO IRQ specified by 'irq'
*
****************************************************************************/
void gpio_irqdisable(int irq)
{
uint32_t base;
int pin;
if ((unsigned)irq < NR_GPIO_IRQS)
{
/* Get the base address of the GPIO module associated with this IRQ */
base = gpio_baseaddress(irq);
/* Get the pin number associated with this IRQ. We made life difficult
* here by choosing a sparse representation of IRQs on GPIO pins.
*/
pin = gpio_pin(irq);
DEBUGASSERT(pin >= 0);
/* Disable the GPIO interrupt. */
putreg32((1 << pin), base + AVR32_GPIO_IERC_OFFSET);
}
}
#endif /* CONFIG_AVR32_GPIOIRQ */