blob: 7290ff6852e24ae91d487913f60708d9aa1642c2 [file] [log] [blame]
/****************************************************************************
* arch/arm64/src/imx9/imx9_gpioirq.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/irq.h>
#include "arm64_internal.h"
#include "imx9_gpio.h"
#ifdef CONFIG_IMX9_GPIO_IRQ
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Types
****************************************************************************/
struct imx9_portisr_s
{
struct
{
xcpt_t isr; /* The interrupt service routine */
void *arg; /* Argument passed to it */
}
pins[IMX9_GPIO_NPINS];
};
/****************************************************************************
* Private Data
****************************************************************************/
static struct imx9_portisr_s g_isrtab[IMX9_GPIO_NPORTS];
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: imx9_gpio_interrupt
*
* Description:
* GPIO interrupt handlers. iMX9 has two interrupt sources for each pin,
* the NuttX driver uses source 0.
*
****************************************************************************/
static int imx9_gpio_interrupt(int irq, void *context, void *arg)
{
uint32_t port = (uint32_t)((uintptr_t)arg) >> GPIO_PORT_SHIFT;
uint32_t status;
uint32_t pin;
uint32_t regaddr;
/* Get the pending interrupt indications */
regaddr = IMX9_GPIO_ISFR0(port);
status = getreg32(regaddr);
/* Decode the pending interrupts */
for (pin = 0; pin < 32 && status != 0; pin++)
{
/* Is the IRQ associated with this pin pending? */
uint32_t mask = (1 << pin);
if ((status & mask) != 0)
{
struct imx9_portisr_s *isrtab;
/* Yes, clear the status bit and dispatch the interrupt */
putreg32(mask, regaddr);
status &= ~mask;
/* Get the interrupt table for this port */
isrtab = &g_isrtab[port];
if (isrtab->pins[pin].isr != NULL)
{
/* Run the user handler with the user's argument */
isrtab->pins[pin].isr(irq, context, isrtab->pins[pin].arg);
}
}
}
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: imx9_gpioirq_initialize
*
* Description:
* Initialize logic to support a second level of interrupt decoding for
* GPIO pins.
*
****************************************************************************/
void imx9_gpioirq_initialize(void)
{
uint32_t port;
uint32_t pin;
/* Disable all GPIO interrupts at the source */
for (port = 0; port < IMX9_GPIO_NPORTS; port++)
{
for (pin = 0; pin < IMX9_GPIO_NPINS; pin++)
{
/* Reset the interrupt configuration, disabling the interrupt */
putreg32(0, IMX9_GPIO_ICRN(port, pin));
}
}
/* Disable all GPIO interrupts */
up_disable_irq(IMX9_IRQ_GPIO1_0);
up_disable_irq(IMX9_IRQ_GPIO1_1);
up_disable_irq(IMX9_IRQ_GPIO2_0);
up_disable_irq(IMX9_IRQ_GPIO2_1);
up_disable_irq(IMX9_IRQ_GPIO3_0);
up_disable_irq(IMX9_IRQ_GPIO3_1);
up_disable_irq(IMX9_IRQ_GPIO4_0);
up_disable_irq(IMX9_IRQ_GPIO4_1);
/* Attach the common GPIO interrupt handler and enable the interrupt */
DEBUGVERIFY(irq_attach(IMX9_IRQ_GPIO1_0,
imx9_gpio_interrupt, (void *)GPIO_PORT1));
up_enable_irq(IMX9_IRQ_GPIO1_0);
DEBUGVERIFY(irq_attach(IMX9_IRQ_GPIO2_0,
imx9_gpio_interrupt, (void *)GPIO_PORT2));
up_enable_irq(IMX9_IRQ_GPIO2_0);
DEBUGVERIFY(irq_attach(IMX9_IRQ_GPIO3_0,
imx9_gpio_interrupt, (void *)GPIO_PORT3));
up_enable_irq(IMX9_IRQ_GPIO3_0);
DEBUGVERIFY(irq_attach(IMX9_IRQ_GPIO4_0,
imx9_gpio_interrupt, (void *)GPIO_PORT4));
up_enable_irq(IMX9_IRQ_GPIO4_0);
}
/****************************************************************************
* Name: imx9_gpioirq_attach
*
* Description:
* Attach a pin interrupt handler.
*
****************************************************************************/
int imx9_gpioirq_attach(gpio_pinset_t pinset, xcpt_t isr, void *arg)
{
uint32_t port = (pinset & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT;
uint32_t pin = (pinset & GPIO_PIN_MASK) >> GPIO_PIN_SHIFT;
/* Atomically change the handler */
irqstate_t flags = enter_critical_section();
g_isrtab[port].pins[pin].isr = isr;
g_isrtab[port].pins[pin].arg = arg;
leave_critical_section(flags);
return OK;
}
/****************************************************************************
* Name: imx9_gpioirq_configure
*
* Description:
* Configure an interrupt for the specified GPIO pin.
*
****************************************************************************/
int imx9_gpioirq_configure(gpio_pinset_t pinset)
{
uint32_t port = (pinset & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT;
uint32_t pin = (pinset & GPIO_PIN_MASK) >> GPIO_PIN_SHIFT;
/* Nothing much to do here, just reset the IRQ config */
putreg32(0, IMX9_GPIO_ICRN(port, pin));
return OK;
}
/****************************************************************************
* Name: imx9_gpioirq_enable
*
* Description:
* Enable the interrupt for specified GPIO IRQ
*
****************************************************************************/
int imx9_gpioirq_enable(gpio_pinset_t pinset)
{
uint32_t port = (pinset & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT;
uint32_t pin = (pinset & GPIO_PIN_MASK) >> GPIO_PIN_SHIFT;
uint32_t both = (pinset & GPIO_INTBOTHCFG_MASK) >> GPIO_INTBOTHCFG_SHIFT;
uint32_t icr = (pinset & GPIO_INTCFG_MASK);
uint32_t regval;
uintptr_t regaddr;
/* Perform RMW to the specific pin */
regaddr = IMX9_GPIO_ICRN(port, pin);
regval = getreg32(regaddr);
regval &= ~IMX9_GPIO_ICRN_MASK;
if (both)
{
regval |= IMX9_GPIO_ICRN_BOTH;
}
else if (icr == GPIO_INT_LOWLEVEL)
{
regval |= IMX9_GPIO_ICRN_ZERO;
}
else if (icr == GPIO_INT_HIGHLEVEL)
{
regval |= IMX9_GPIO_ICRN_ONE;
}
else if (icr == GPIO_INT_RISINGEDGE)
{
regval |= IMX9_GPIO_ICRN_RISING;
}
else /* GPIO_INT_FALLINGEDGE */
{
regval |= IMX9_GPIO_ICRN_FALLING;
}
putreg32(regval, regaddr);
return OK;
}
/****************************************************************************
* Name: imx9_gpioirq_disable
*
* Description:
* Disable the interrupt for specified GPIO IRQ
*
****************************************************************************/
int imx9_gpioirq_disable(gpio_pinset_t pinset)
{
uint32_t port = (pinset & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT;
uint32_t pin = (pinset & GPIO_PIN_MASK) >> GPIO_PIN_SHIFT;
uint32_t regval;
uintptr_t regaddr;
/* Perform RMW to the specific pin */
regaddr = IMX9_GPIO_ICRN(port, pin);
regval = getreg32(regaddr);
regval &= ~IMX9_GPIO_ICRN_MASK;
putreg32(regval, regaddr);
return OK;
}
#endif /* CONFIG_IMX9_GPIO_IRQ */