nuttx/arch/arm/src/tiva/tiva_gpioirq.c

428 lines
12 KiB
C

/****************************************************************************
* arch/arm/src/tiva/tiva_gpioirq.c
*
* Copyright (C) 2009-2010, 2012, 2014 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* 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 <stdint.h>
#include <string.h>
#include <assert.h>
#include <debug.h>
#include <arch/irq.h>
#include "up_arch.h"
#include "irq/irq.h"
#include "tiva_gpio.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Data
****************************************************************************/
/* A table of handlers for each GPIO interrupt */
static FAR xcpt_t g_gpioirqvector[NR_GPIO_IRQS];
/* A table that maps a GPIO group to a GPIO base address. Overly complicated
* because we support disabling interrupt support for arbitrary ports. This
* must carefully match the IRQ numbers assigned in arch/arm/include/lm3s/irq.h
*/
static const uintptr_t g_gpiobase[] =
{
#ifndef CONFIG_TIVA_DISABLE_GPIOA_IRQS
TIVA_GPIOA_BASE
#else
0
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOB_IRQS
, TIVA_GPIOB_BASE
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOC_IRQS
, TIVA_GPIOC_BASE
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOD_IRQS
, TIVA_GPIOD_BASE
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOE_IRQS
, TIVA_GPIOE_BASE
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOF_IRQS
, TIVA_GPIOF_BASE
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOG_IRQS
, TIVA_GPIOG_BASE
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOH_IRQS
, TIVA_GPIOH_BASE
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOJ_IRQS
, TIVA_GPIOJ_BASE
#endif
};
#define GPIO_NADDRS (sizeof(g_gpiobase)/sizeof(uintptr_t))
/****************************************************************************
* Public Data
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: tiva_gpiobaseaddress
*
* Input:
* gpioirq - A pin number in the range of 0 to NR_GPIO_IRQS.
*
* Description:
* Given a GPIO enumeration value, return the base address of the
* associated GPIO registers. NOTE that range checking was provided by
* callee
*
****************************************************************************/
static uintptr_t tiva_gpiobaseaddress(unsigned int gpioirq)
{
unsigned int ndx = gpioirq >> 3;
if (ndx < GPIO_NADDRS)
{
return g_gpiobase[ndx];
}
return 0;
}
/****************************************************************************
* Name: tiva_gpio*handler
*
* Description:
* Handle interrupts on each enabled GPIO port
*
****************************************************************************/
static int tiva_gpiohandler(uint32_t regbase, int irqbase, void *context)
{
uint32_t mis;
int irq;
int pin;
/* Handle each pending GPIO interrupt. "The GPIO MIS register is the masked
* interrupt status register. Bits read High in GPIO MIS reflect the status
* of input lines triggering an interrupt. Bits read as Low indicate that
* either no interrupt has been generated, or the interrupt is masked."
*/
mis = getreg32(regbase + TIVA_GPIO_MIS_OFFSET) & 0xff;
/* Clear all GPIO interrupts that we are going to process. "The GPIO ICR
* register is the interrupt clear register. Writing a 1 to a bit in this
* register clears the corresponding interrupt edge detection logic register.
* Writing a 0 has no effect."
*/
putreg32(mis, regbase + TIVA_GPIO_ICR_OFFSET);
/* Now process each IRQ pending in the MIS */
for (pin = 0; pin < 8 && mis != 0; pin++, mis >>= 1)
{
if ((mis & 1) != 0)
{
irq = irqbase + pin;
g_gpioirqvector[irq - NR_IRQS](irq, context);
}
}
return OK;
}
#ifndef CONFIG_TIVA_DISABLE_GPIOA_IRQS
static int tiva_gpioahandler(int irq, FAR void *context)
{
return tiva_gpiohandler(TIVA_GPIOA_BASE, TIVA_IRQ_GPIOA_0, context);
}
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOB_IRQS
static int tiva_gpiobhandler(int irq, FAR void *context)
{
return tiva_gpiohandler(TIVA_GPIOB_BASE, TIVA_IRQ_GPIOB_0, context);
}
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOC_IRQS
static int tiva_gpiochandler(int irq, FAR void *context)
{
return tiva_gpiohandler(TIVA_GPIOC_BASE, TIVA_IRQ_GPIOC_0, context);
}
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOD_IRQS
static int tiva_gpiodhandler(int irq, FAR void *context)
{
return tiva_gpiohandler(TIVA_GPIOD_BASE, TIVA_IRQ_GPIOD_0, context);
}
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOE_IRQS
static int tiva_gpioehandler(int irq, FAR void *context)
{
return tiva_gpiohandler(TIVA_GPIOE_BASE, TIVA_IRQ_GPIOE_0, context);
}
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOF_IRQS
static int tiva_gpiofhandler(int irq, FAR void *context)
{
return tiva_gpiohandler(TIVA_GPIOF_BASE, TIVA_IRQ_GPIOF_0, context);
}
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOG_IRQS
static int tiva_gpioghandler(int irq, FAR void *context)
{
return tiva_gpiohandler(TIVA_GPIOG_BASE, TIVA_IRQ_GPIOG_0, context);
}
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOH_IRQS
static int tiva_gpiohhandler(int irq, FAR void *context)
{
return tiva_gpiohandler(TIVA_GPIOH_BASE, TIVA_IRQ_GPIOH_0, context);
}
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOJ_IRQS
static int tiva_gpiojhandler(int irq, FAR void *context)
{
return tiva_gpiohandler(TIVA_GPIOJ_BASE, TIVA_IRQ_GPIOJ_0, context);
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: gpio_irqinitialize
*
* Description:
* Initialize all vectors to the unexpected interrupt handler
*
****************************************************************************/
int gpio_irqinitialize(void)
{
int i;
/* Point all interrupt vectors to the unexpected interrupt */
for (i = 0; i < NR_GPIO_IRQS; i++)
{
g_gpioirqvector[i] = irq_unexpected_isr;
}
/* Then attach each GPIO interrupt handlers and enable corresponding GPIO
* interrupts
*/
#ifndef CONFIG_TIVA_DISABLE_GPIOA_IRQS
irq_attach(TIVA_IRQ_GPIOA, tiva_gpioahandler);
up_enable_irq(TIVA_IRQ_GPIOA);
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOB_IRQS
irq_attach(TIVA_IRQ_GPIOB, tiva_gpiobhandler);
up_enable_irq(TIVA_IRQ_GPIOB);
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOC_IRQS
irq_attach(TIVA_IRQ_GPIOC, tiva_gpiochandler);
up_enable_irq(TIVA_IRQ_GPIOC);
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOD_IRQS
irq_attach(TIVA_IRQ_GPIOD, tiva_gpiodhandler);
up_enable_irq(TIVA_IRQ_GPIOD);
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOE_IRQS
irq_attach(TIVA_IRQ_GPIOE, tiva_gpioehandler);
up_enable_irq(TIVA_IRQ_GPIOE);
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOF_IRQS
irq_attach(TIVA_IRQ_GPIOF, tiva_gpiofhandler);
up_enable_irq(TIVA_IRQ_GPIOF);
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOG_IRQS
irq_attach(TIVA_IRQ_GPIOG, tiva_gpioghandler);
up_enable_irq(TIVA_IRQ_GPIOG);
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOH_IRQS
irq_attach(TIVA_IRQ_GPIOH, tiva_gpiohhandler);
up_enable_irq(TIVA_IRQ_GPIOH);
#endif
#ifndef CONFIG_TIVA_DISABLE_GPIOJ_IRQS
irq_attach(TIVA_IRQ_GPIOJ, tiva_gpiojhandler);
up_enable_irq(TIVA_IRQ_GPIOJ);
#endif
return OK;
}
/****************************************************************************
* Name: gpio_irqattach
*
* Description:
* Attach in GPIO interrupt to the provide 'isr'
*
****************************************************************************/
int gpio_irqattach(int irq, xcpt_t isr)
{
irqstate_t flags;
int gpioirq = irq - NR_IRQS;
int ret = ERROR;
if ((unsigned)gpioirq < NR_GPIO_IRQS)
{
flags = irqsave();
/* 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.
*/
if (isr == NULL)
{
#ifndef CONFIG_ARCH_NOINTC
gpio_irqdisable(gpioirq);
#endif
isr = irq_unexpected_isr;
}
/* Save the new ISR in the table. */
g_irqvector[gpioirq] = isr;
irqrestore(flags);
ret = OK;
}
return ret;
}
/****************************************************************************
* Name: gpio_irqenable
*
* Description:
* Enable the GPIO IRQ specified by 'irq'
*
****************************************************************************/
void gpio_irqenable(int irq)
{
irqstate_t flags;
int gpioirq = irq - NR_IRQS;
uintptr_t base;
uint32_t regval;
int pin;
if ((unsigned)gpioirq < NR_GPIO_IRQS)
{
/* Get the base address of the GPIO module associated with this IRQ */
base = tiva_gpiobaseaddress(gpioirq);
DEBUGASSERT(base != 0);
pin = (1 << (gpioirq & 7));
/* Disable the GPIO interrupt. "The GPIO IM register is the interrupt
* mask register. Bits set to High in GPIO IM allow the corresponding
* pins to trigger their individual interrupts and the combined GPIO INTR
* line. Clearing a bit disables interrupt triggering on that pin. All
* bits are cleared by a reset.
*/
flags = irqsave();
regval = getreg32(base + TIVA_GPIO_IM_OFFSET);
regval |= pin;
putreg32(regval, base + TIVA_GPIO_IM_OFFSET);
irqrestore(flags);
}
}
/****************************************************************************
* Name: gpio_irqdisable
*
* Description:
* Disable the GPIO IRQ specified by 'irq'
*
****************************************************************************/
void gpio_irqdisable(int irq)
{
irqstate_t flags;
int gpioirq = irq - NR_IRQS;
uintptr_t base;
uint32_t regval;
int pin;
if ((unsigned)gpioirq < NR_GPIO_IRQS)
{
/* Get the base address of the GPIO module associated with this IRQ */
base = tiva_gpiobaseaddress(gpioirq);
DEBUGASSERT(base != 0);
pin = (1 << (gpioirq & 7));
/* Disable the GPIO interrupt. "The GPIO IM register is the interrupt
* mask register. Bits set to High in GPIO IM allow the corresponding
* pins to trigger their individual interrupts and the combined GPIO INTR
* line. Clearing a bit disables interrupt triggering on that pin. All
* bits are cleared by a reset.
*/
flags = irqsave();
regval = getreg32(base + TIVA_GPIO_IM_OFFSET);
regval &= ~pin;
putreg32(regval, base + TIVA_GPIO_IM_OFFSET);
irqrestore(flags);
}
}