c4850efab5
DisableDeepSleepIRQ() disables the NVIC irq, while PINT need it to fire an event when the interrupt is used. So while the interrupt is not used in DeepSleep mode, it should still be available to be used. Signed-off-by: Hessel van der Molen <hvandermolen@dexels.com>
215 lines
5.2 KiB
C
215 lines
5.2 KiB
C
/*
|
|
* Copyright 2023 NXP
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/* Based on STM32 EXTI driver, which is (c) 2016 Open-RnD Sp. z o.o. */
|
|
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/irq.h>
|
|
#include <errno.h>
|
|
#include <zephyr/drivers/interrupt_controller/nxp_pint.h>
|
|
|
|
#include <fsl_inputmux.h>
|
|
#include <fsl_power.h>
|
|
|
|
#define DT_DRV_COMPAT nxp_pint
|
|
|
|
static PINT_Type *pint_base = (PINT_Type *)DT_INST_REG_ADDR(0);
|
|
|
|
/* Describes configuration of PINT IRQ slot */
|
|
struct pint_irq_slot {
|
|
nxp_pint_cb_t callback;
|
|
void *user_data;
|
|
uint8_t pin: 6;
|
|
uint8_t used: 1;
|
|
uint8_t irq;
|
|
};
|
|
|
|
#define NO_PINT_ID 0xFF
|
|
|
|
/* Tracks IRQ configuration for each pint interrupt source */
|
|
static struct pint_irq_slot pint_irq_cfg[DT_INST_PROP(0, num_lines)];
|
|
/* Tracks pint interrupt source selected for each pin */
|
|
static uint8_t pin_pint_id[DT_INST_PROP(0, num_inputs)];
|
|
|
|
#define PIN_TO_INPUT_MUX_CONNECTION(pin) \
|
|
((PINTSEL_PMUX_ID << PMUX_SHIFT) + (pin))
|
|
|
|
/* Attaches pin to PINT IRQ slot using INPUTMUX */
|
|
static void attach_pin_to_pint(uint8_t pin, uint8_t pint_slot)
|
|
{
|
|
INPUTMUX_Init(INPUTMUX);
|
|
|
|
/* Three parameters here- INPUTMUX base, the ID of the PINT slot,
|
|
* and a integer describing the GPIO pin.
|
|
*/
|
|
INPUTMUX_AttachSignal(INPUTMUX, pint_slot,
|
|
PIN_TO_INPUT_MUX_CONNECTION(pin));
|
|
|
|
/* Disable INPUTMUX after making changes, this gates clock and
|
|
* saves power.
|
|
*/
|
|
INPUTMUX_Deinit(INPUTMUX);
|
|
}
|
|
|
|
/**
|
|
* @brief Enable PINT interrupt source.
|
|
*
|
|
* @param pin: pin to use as interrupt source
|
|
* 0-64, corresponding to GPIO0 pin 1 - GPIO1 pin 31)
|
|
* @param trigger: one of nxp_pint_trigger flags
|
|
* @param wake: indicates if the pin should wakeup the system
|
|
* @return 0 on success, or negative value on error
|
|
*/
|
|
int nxp_pint_pin_enable(uint8_t pin, enum nxp_pint_trigger trigger, bool wake)
|
|
{
|
|
uint8_t slot = 0U;
|
|
|
|
if (pin > ARRAY_SIZE(pin_pint_id)) {
|
|
/* Invalid pin ID */
|
|
return -EINVAL;
|
|
}
|
|
/* Find unused IRQ slot */
|
|
if (pin_pint_id[pin] != NO_PINT_ID) {
|
|
slot = pin_pint_id[pin];
|
|
} else {
|
|
for (slot = 0; slot < ARRAY_SIZE(pint_irq_cfg); slot++) {
|
|
if (!pint_irq_cfg[slot].used) {
|
|
break;
|
|
}
|
|
}
|
|
if (slot == ARRAY_SIZE(pint_irq_cfg)) {
|
|
/* No free IRQ slots */
|
|
return -EBUSY;
|
|
}
|
|
pin_pint_id[pin] = slot;
|
|
}
|
|
pint_irq_cfg[slot].used = true;
|
|
pint_irq_cfg[slot].pin = pin;
|
|
/* Attach pin to interrupt slot using INPUTMUX */
|
|
attach_pin_to_pint(pin, slot);
|
|
/* Now configure the interrupt. No need to install callback, this
|
|
* driver handles the IRQ
|
|
*/
|
|
PINT_PinInterruptConfig(pint_base, slot, trigger, NULL);
|
|
#if !(defined(FSL_FEATURE_POWERLIB_EXTEND) && (FSL_FEATURE_POWERLIB_EXTEND != 0))
|
|
if (wake) {
|
|
EnableDeepSleepIRQ(pint_irq_cfg[slot].irq);
|
|
} else {
|
|
DisableDeepSleepIRQ(pint_irq_cfg[slot].irq);
|
|
irq_enable(pint_irq_cfg[slot].irq);
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief disable PINT interrupt source.
|
|
*
|
|
* @param pin: pin interrupt source to disable
|
|
*/
|
|
void nxp_pint_pin_disable(uint8_t pin)
|
|
{
|
|
uint8_t slot;
|
|
|
|
if (pin > ARRAY_SIZE(pin_pint_id)) {
|
|
return;
|
|
}
|
|
|
|
slot = pin_pint_id[pin];
|
|
if (slot == NO_PINT_ID) {
|
|
return;
|
|
}
|
|
/* Remove this pin from the PINT slot if one was in use */
|
|
pint_irq_cfg[slot].used = false;
|
|
PINT_PinInterruptConfig(pint_base, slot, kPINT_PinIntEnableNone, NULL);
|
|
}
|
|
|
|
/**
|
|
* @brief Install PINT callback
|
|
*
|
|
* @param pin: interrupt source to install callback for
|
|
* @param cb: callback to install
|
|
* @param data: user data to include in callback
|
|
* @return 0 on success, or negative value on error
|
|
*/
|
|
int nxp_pint_pin_set_callback(uint8_t pin, nxp_pint_cb_t cb, void *data)
|
|
{
|
|
uint8_t slot;
|
|
|
|
if (pin > ARRAY_SIZE(pin_pint_id)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
slot = pin_pint_id[pin];
|
|
if (slot == NO_PINT_ID) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
pint_irq_cfg[slot].callback = cb;
|
|
pint_irq_cfg[slot].user_data = data;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Remove PINT callback
|
|
*
|
|
* @param pin: interrupt source to remove callback for
|
|
*/
|
|
void nxp_pint_pin_unset_callback(uint8_t pin)
|
|
{
|
|
uint8_t slot;
|
|
|
|
if (pin > ARRAY_SIZE(pin_pint_id)) {
|
|
return;
|
|
}
|
|
|
|
slot = pin_pint_id[pin];
|
|
if (slot == NO_PINT_ID) {
|
|
return;
|
|
}
|
|
|
|
pint_irq_cfg[slot].callback = NULL;
|
|
}
|
|
|
|
/* NXP PINT ISR handler- called with PINT slot ID */
|
|
static void nxp_pint_isr(uint8_t *slot)
|
|
{
|
|
PINT_PinInterruptClrStatus(pint_base, *slot);
|
|
if (pint_irq_cfg[*slot].used && pint_irq_cfg[*slot].callback) {
|
|
pint_irq_cfg[*slot].callback(pint_irq_cfg[*slot].pin,
|
|
pint_irq_cfg[*slot].user_data);
|
|
}
|
|
}
|
|
|
|
|
|
/* Defines PINT IRQ handler for a given irq index */
|
|
#define NXP_PINT_IRQ(idx, node_id) \
|
|
IF_ENABLED(DT_IRQ_HAS_IDX(node_id, idx), \
|
|
(static uint8_t nxp_pint_idx_##idx = idx; \
|
|
do { \
|
|
IRQ_CONNECT(DT_IRQ_BY_IDX(node_id, idx, irq), \
|
|
DT_IRQ_BY_IDX(node_id, idx, priority), \
|
|
nxp_pint_isr, &nxp_pint_idx_##idx, 0); \
|
|
irq_enable(DT_IRQ_BY_IDX(node_id, idx, irq)); \
|
|
pint_irq_cfg[idx].irq = DT_IRQ_BY_IDX(node_id, idx, irq); \
|
|
} while (false)))
|
|
|
|
static int intc_nxp_pint_init(const struct device *dev)
|
|
{
|
|
/* First, connect IRQs for each interrupt.
|
|
* The IRQ handler will receive the PINT slot as a
|
|
* parameter.
|
|
*/
|
|
LISTIFY(8, NXP_PINT_IRQ, (;), DT_INST(0, DT_DRV_COMPAT));
|
|
PINT_Init(pint_base);
|
|
memset(pin_pint_id, NO_PINT_ID, ARRAY_SIZE(pin_pint_id));
|
|
return 0;
|
|
}
|
|
|
|
DEVICE_DT_INST_DEFINE(0, intc_nxp_pint_init, NULL, NULL, NULL,
|
|
PRE_KERNEL_1, CONFIG_INTC_INIT_PRIORITY, NULL);
|