summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-12-13 17:27:06 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-12-13 17:27:06 -0600
commitb6eaf1dd639dedf3c4206e4b59e0538c977d3e99 (patch)
tree9b43e517147a8376cc993dbee51bfd70a9b91b86
parent8897acab5799264ff04f265189ec9ac1bafc6c83 (diff)
downloadnuttx-b6eaf1dd639dedf3c4206e4b59e0538c977d3e99.tar.gz
nuttx-b6eaf1dd639dedf3c4206e4b59e0538c977d3e99.tar.bz2
nuttx-b6eaf1dd639dedf3c4206e4b59e0538c977d3e99.zip
Add GPIO interrupt capability for the KL architecture. The patch is almost the same as kinetis_pinirq.c, just minor modifications and rename kl_pinirq to kl_gpioirq to make it more generic to developers. From Alan Carvalho de Assis
-rw-r--r--nuttx/arch/arm/src/kl/Kconfig16
-rw-r--r--nuttx/arch/arm/src/kl/Make.defs4
-rw-r--r--nuttx/arch/arm/src/kl/kl_gpioirq.c388
3 files changed, 392 insertions, 16 deletions
diff --git a/nuttx/arch/arm/src/kl/Kconfig b/nuttx/arch/arm/src/kl/Kconfig
index 34f86b150..1b7f7cc3d 100644
--- a/nuttx/arch/arm/src/kl/Kconfig
+++ b/nuttx/arch/arm/src/kl/Kconfig
@@ -348,25 +348,9 @@ config KL_PORTAINTS
---help---
Enable support for 32 interrupts from GPIO port A pins
-config KL_PORTBINTS
- bool "GPIOB interrupts"
- ---help---
- Enable support for 32 interrupts from GPIO port B pins
-
-config KL_PORTCINTS
- bool "GPIOC interrupts"
- ---help---
- Enable support for 32 interrupts from GPIO port C pins
-
config KL_PORTDINTS
bool "GPIOD interrupts"
---help---
Enable support for 32 interrupts from GPIO port D pins
-config KL_PORTEINTS
- bool "GPIOE interrupts"
- ---help---
- Enable support for 32 interrupts from GPIO port E pins
-
endif
-
diff --git a/nuttx/arch/arm/src/kl/Make.defs b/nuttx/arch/arm/src/kl/Make.defs
index e7a4bceaa..430ad3665 100644
--- a/nuttx/arch/arm/src/kl/Make.defs
+++ b/nuttx/arch/arm/src/kl/Make.defs
@@ -79,6 +79,10 @@ ifeq ($(CONFIG_BUILD_PROTECTED),y)
CHIP_CSRCS += kl_userspace.c
endif
+ifeq ($(CONFIG_GPIO_IRQ),y)
+CHIP_CSRCS += kl_gpioirq.c
+endif
+
ifeq ($(CONFIG_ARCH_IRQPRIO),y)
CHIP_CSRCS += kl_irqprio.c
endif
diff --git a/nuttx/arch/arm/src/kl/kl_gpioirq.c b/nuttx/arch/arm/src/kl/kl_gpioirq.c
new file mode 100644
index 000000000..6bf71c66b
--- /dev/null
+++ b/nuttx/arch/arm/src/kl/kl_gpioirq.c
@@ -0,0 +1,388 @@
+/****************************************************************************
+ * arch/arm/src/kl/kl_gpioirq.c
+ *
+ * Copyright (C) 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 <arch/board/board.h>
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+
+#include "up_arch.h"
+
+#include "chip/kl_port.h"
+#include "kl_gpio.h"
+
+#ifdef CONFIG_GPIO_IRQ
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Configuration ************************************************************/
+/* The Kinetis port interrupt logic is very flexible and will program
+ * interrupts on most all pin events. In order to keep the memory usage to
+ * a minimum, the NuttX port supports enabling interrupts on a per-port
+ * basis.
+ */
+
+#if defined(CONFIG_KL_PORTAINTS) || defined(CONFIG_KL_PORTDINTS)
+# define HAVE_PORTINTS 1
+#endif
+
+#if defined(CONFIG_KL_PORTBINTS) || defined(CONFIG_KL_PORTCINTS) || \
+ defined(CONFIG_KL_PORTEINTS)
+# error Kinetis KL25 only supports interrupt on PORTA or PORTD
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+/* Per pin port interrupt vectors. NOTE: Not all pins in each port
+ * correspond to externally available GPIOs. However, I believe that the
+ * Kinetis will support interrupts even if the pin is not available as
+ * a GPIO. Hence, we need to support all 32 pins for each port. To keep the
+ * memory usage at a minimum, the logic may be configure per port.
+ */
+
+#ifdef CONFIG_KL_PORTAINTS
+static xcpt_t g_portaisrs[32];
+#endif
+
+#ifdef CONFIG_KL_PORTDINTS
+static xcpt_t g_portdisrs[32];
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: kl_portinterrupt
+ *
+ * Description:
+ * Common port interrupt handling.
+ *
+ ****************************************************************************/
+
+#ifdef HAVE_PORTINTS
+static int kl_portinterrupt(int irq, FAR void *context,
+ uintptr_t addr, xcpt_t *isrtab)
+{
+ uint32_t isfr = getreg32(addr);
+ int i;
+
+ /* Examine each pin in the port */
+
+ for (i = 0; i < 32 && isfr != 0; i++)
+ {
+ /* A bit set in the ISR means that an interrupt is pending for this
+ * pin. If the pin is programmed for level sensitive inputs, then
+ * the interrupt handling logic MUST disable the interrupt (or cause
+ * the level to change) to prevent infinite interrupts.
+ */
+
+ uint32_t bit = (1 << i);
+ if ((isfr & bit ) != 0)
+ {
+ /* I think that bits may be set in the ISFR for DMA activities
+ * well. So, no error is declared if there is no registered
+ * interrupt handler for the pin.
+ */
+
+ if (isrtab[i])
+ {
+ /* There is a registered interrupt handler... invoke it */
+
+ (void)isrtab[i](irq, context);
+ }
+
+ /* Writing a one to the ISFR register will clear the pending
+ * interrupt. If pin is configured to generate a DMA request
+ * then the ISFR bit will be cleared automatically at the
+ * completion of the requested DMA transfer. If configured for
+ * a level sensitive interrupt and the pin remains asserted and
+ * the bit will set again immediately after it is cleared.
+ */
+
+ isfr &= ~bit;
+ putreg32(bit, addr);
+ }
+ }
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: kl_portXinterrupt
+ *
+ * Description:
+ * Handle interrupts arriving on individual ports
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_KL_PORTAINTS
+static int kl_portainterrupt(int irq, FAR void *context)
+{
+ return kl_portinterrupt(irq, context, KL_PORTA_ISFR, g_portaisrs);
+}
+#endif
+
+#ifdef CONFIG_KL_PORTDINTS
+static int kl_portdinterrupt(int irq, FAR void *context)
+{
+ return kl_portinterrupt(irq, context, KL_PORTD_ISFR, g_portdisrs);
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: kl_gpioirqinitialize
+ *
+ * Description:
+ * Initialize logic to support a second level of interrupt decoding for
+ * GPIO pins.
+ *
+ ****************************************************************************/
+
+void kl_gpioirqinitialize(void)
+{
+#ifdef CONFIG_KL_PORTAINTS
+ (void)irq_attach(KL_IRQ_PORTA, kl_portainterrupt);
+ putreg32(0xffffffff, KL_PORTA_ISFR);
+ up_enable_irq(KL_IRQ_PORTA);
+#endif
+
+#ifdef CONFIG_KL_PORTDINTS
+ (void)irq_attach(KL_IRQ_PORTD, kl_portdinterrupt);
+ putreg32(0xffffffff, KL_PORTD_ISFR);
+ up_enable_irq(KL_IRQ_PORTD);
+#endif
+}
+
+/****************************************************************************
+ * Name: kl_gpioirqattach
+ *
+ * Description:
+ * Attach a pin interrupt handler. The normal initialization sequence is:
+ *
+ * 1. Call kl_gpioconfig() to configure the interrupting pin (pin interrupts
+ * will be disabled.
+ * 2. Call kl_gpioirqattach() to attach the pin interrupt handling function.
+ * 3. Call kl_gpioirqenable() to enable interrupts on the pin.
+ *
+ * Parameters:
+ * - pinset: Pin configuration
+ * - pinisr: Pin interrupt service routine
+ *
+ * Returns:
+ * The previous value of the interrupt handler function pointer. This
+ * value may, for example, be used to restore the previous handler when
+ * multiple handlers are used.
+ *
+ ****************************************************************************/
+
+xcpt_t kl_gpioirqattach(uint32_t pinset, xcpt_t pinisr)
+{
+#ifdef HAVE_PORTINTS
+ xcpt_t *isrtab;
+ xcpt_t oldisr;
+ irqstate_t flags;
+ unsigned int port;
+ unsigned int pin;
+
+ /* It only makes sense to call this function for input pins that are
+ * configured as interrupts.
+ */
+
+ DEBUGASSERT((pinset & _PIN_INTDMA_MASK) == _PIN_INTERRUPT);
+ DEBUGASSERT((pinset & _PIN_IO_MASK) == _PIN_INPUT);
+
+ /* Get the port number and pin number */
+
+ port = (pinset & _PIN_PORT_MASK) >> _PIN_PORT_SHIFT;
+ pin = (pinset & _PIN_MASK) >> _PIN_SHIFT;
+
+ /* Get the table associated with this port */
+
+ DEBUGASSERT(port < KL_NPORTS);
+ flags = irqsave();
+ switch (port)
+ {
+#ifdef CONFIG_KL_PORTAINTS
+ case KL_PORTA :
+ isrtab = g_portaisrs;
+ break;
+#endif
+#ifdef CONFIG_KL_PORTDINTS
+ case KL_PORTD :
+ isrtab = g_portdisrs;
+ break;
+#endif
+ default:
+ return NULL;
+ }
+
+ /* Get the old PIN ISR and set the new PIN ISR */
+
+ oldisr = isrtab[pin];
+ isrtab[pin] = pinisr;
+
+ /* And return the old PIN isr address */
+
+ return oldisr;
+
+#else
+ return NULL;
+#endif /* HAVE_PORTINTS */
+}
+
+/************************************************************************************
+ * Name: kl_gpioirqenable
+ *
+ * Description:
+ * Enable the interrupt for specified pin IRQ
+ *
+ ************************************************************************************/
+
+void kl_gpioirqenable(uint32_t pinset)
+{
+#ifdef HAVE_PORTINTS
+ uintptr_t base;
+ uint32_t regval;
+ unsigned int port;
+ unsigned int pin;
+
+ /* Get the port number and pin number */
+
+ port = (pinset & _PIN_PORT_MASK) >> _PIN_PORT_SHIFT;
+ pin = (pinset & _PIN_MASK) >> _PIN_SHIFT;
+
+ DEBUGASSERT(port < KL_NPORTS);
+ if (port < KL_NPORTS)
+ {
+ /* Get the base address of PORT block for this port */
+
+ base = KL_PORT_BASE(port);
+
+ /* Modify the IRQC field of the port PCR register in order to enable
+ * the interrupt.
+ */
+
+ regval = getreg32(base + KL_PORT_PCR_OFFSET(pin));
+ regval &= ~PORT_PCR_IRQC_MASK;
+
+ switch (pinset & _PIN_INT_MASK)
+ {
+ case PIN_INT_ZERO : /* Interrupt when logic zero */
+ regval |= PORT_PCR_IRQC_ZERO;
+ break;
+
+ case PIN_INT_RISING : /* Interrupt on rising edge*/
+ regval |= PORT_PCR_IRQC_RISING;
+ break;
+
+ case PIN_INT_BOTH : /* Interrupt on falling edge */
+ regval |= PORT_PCR_IRQC_FALLING;
+ break;
+
+ case PIN_DMA_FALLING : /* nterrupt on either edge */
+ regval |= PORT_PCR_IRQC_BOTH;
+ break;
+
+ case PIN_INT_ONE : /* IInterrupt when logic one */
+ regval |= PORT_PCR_IRQC_ONE;
+ break;
+
+ default:
+ return;
+ }
+
+ putreg32(regval, base + KL_PORT_PCR_OFFSET(pin));
+ }
+#endif /* HAVE_PORTINTS */
+}
+
+/************************************************************************************
+ * Name: kl_gpioirqdisable
+ *
+ * Description:
+ * Disable the interrupt for specified pin
+ *
+ ************************************************************************************/
+
+void kl_gpioirqdisable(uint32_t pinset)
+{
+#ifdef HAVE_PORTINTS
+ uintptr_t base;
+ uint32_t regval;
+ unsigned int port;
+ unsigned int pin;
+
+ /* Get the port number and pin number */
+
+ port = (pinset & _PIN_PORT_MASK) >> _PIN_PORT_SHIFT;
+ pin = (pinset & _PIN_MASK) >> _PIN_SHIFT;
+
+ DEBUGASSERT(port < KL_NPORTS);
+ if (port < KL_NPORTS)
+ {
+ /* Get the base address of PORT block for this port */
+
+ base = KL_PORT_BASE(port);
+
+ /* Clear the IRQC field of the port PCR register in order to disable
+ * the interrupt.
+ */
+
+ regval = getreg32(base + KL_PORT_PCR_OFFSET(pin));
+ regval &= ~PORT_PCR_IRQC_MASK;
+ putreg32(regval, base + KL_PORT_PCR_OFFSET(pin));
+ }
+#endif /* HAVE_PORTINTS */
+}
+#endif /* CONFIG_GPIO_IRQ */