/*
 * 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.
 */

#include "hal/hal_gpio.h"
#include "mcu/cmsis_nvic.h"
#include "mcu/stm32_hal.h"
#include "stm32_common/mcu.h"
#include <assert.h>

 /* XXX: Notes
 * 1) Right now, we are not disabling the NVIC interrupt source; we only
 * disable the external interrupt from occurring. Dont think either way
 * to do it is an issue... when we release we may want to disable the NVIC
 *
 * 2) investigate how thread safe these routines are. HAL_GPIO_Init, for
 * example. Looks like if it gets interrupted while doing config an error
 * may occur. Read/modify write could cause screw-ups.
 *
 * 3) Currently, this code does not change the interrupt priority of the
 * external interrupt vectors in the NVIC. The application developer must
 * decide on the priority level for each external interrupt and program that
 * by using the CMSIS NVIC API  (NVIC_SetPriority and NVIC_SetPriorityGrouping)
 *
 * 4) The code probably does not handle "re-purposing" gpio very well.
 * "Re-purposing" means changing a gpio from input to output, or calling
 * gpio_init_in and expecting previously enabled interrupts to be stopped.
 *
 * 5) Possibly add access to HAL_GPIO_DeInit.
 */

#define GPIO_MASK(pin)      (1 << MCU_GPIO_PIN_NUM(pin))

#ifndef GPIOA
#   define GPIOA    0
#endif
#ifndef GPIOB
#   define GPIOB    0
#endif
#ifndef GPIOC
#   define GPIOC    0
#endif
#ifndef GPIOD
#   define GPIOD    0
#endif
#ifndef GPIOE
#   define GPIOE    0
#endif
#ifndef GPIOF
#   define GPIOF    0
#endif
#ifndef GPIOG
#   define GPIOG    0
#endif
#ifndef GPIOH
#   define GPIOH    0
#endif
#ifndef GPIOI
#   define GPIOI    0
#endif
#ifndef GPIOJ
#   define GPIOJ    0
#endif
#ifndef GPIOK
#   define GPIOK    0
#endif


#if defined GPIOK_BASE
#   define  HAL_GPIO_PORT_COUNT   (11)
#   define  HAL_GPIO_PORT_LIST    GPIOA,GPIOB,GPIOC,GPIOD,GPIOE,GPIOF,GPIOG,GPIOH,GPIOI,GPIOJ,GPIOK
#elif defined GPIOJ_BASE
#   define  HAL_GPIO_PORT_COUNT   (10)
#   define  HAL_GPIO_PORT_LIST    GPIOA,GPIOB,GPIOC,GPIOD,GPIOE,GPIOF,GPIOG,GPIOH,GPIOI,GPIOJ
#elif defined GPIOI_BASE
#   define  HAL_GPIO_PORT_COUNT   (9)
#   define  HAL_GPIO_PORT_LIST    GPIOA,GPIOB,GPIOC,GPIOD,GPIOE,GPIOF,GPIOG,GPIOH,GPIOI
#elif defined GPIOH_BASE
#   define  HAL_GPIO_PORT_COUNT   (8)
#   define  HAL_GPIO_PORT_LIST    GPIOA,GPIOB,GPIOC,GPIOD,GPIOE,GPIOF,GPIOG,GPIOH
#elif defined GPIOG_BASE
#   define  HAL_GPIO_PORT_COUNT   (7)
#   define  HAL_GPIO_PORT_LIST    GPIOA,GPIOB,GPIOC,GPIOD,GPIOE,GPIOF,GPIOG
#elif defined GPIOF_BASE
#   define  HAL_GPIO_PORT_COUNT   (6)
#   define  HAL_GPIO_PORT_LIST    GPIOA,GPIOB,GPIOC,GPIOD,GPIOE,GPIOF
#elif defined GPIOE_BASE
#   define  HAL_GPIO_PORT_COUNT   (5)
#   define  HAL_GPIO_PORT_LIST    GPIOA,GPIOB,GPIOC,GPIOD,GPIOE
#elif defined GPIOD_BASE
#   define  HAL_GPIO_PORT_COUNT   (4)
#   define  HAL_GPIO_PORT_LIST    GPIOA,GPIOB,GPIOC,GPIOD
#elif defined GPIOC_BASE
#   define  HAL_GPIO_PORT_COUNT   (3)
#   define  HAL_GPIO_PORT_LIST    GPIOA,GPIOB,GPIOC
#elif defined GPIOB_BASE
#   define  HAL_GPIO_PORT_COUNT   (2)
#   define  HAL_GPIO_PORT_LIST    GPIOA,GPIOB
#elif defined GPIOA_BASE
#   define  HAL_GPIO_PORT_COUNT   (1)
#   define  HAL_GPIO_PORT_LIST    GPIOA
#else
#   error "No GPIO ports found - MCU not supported!"
#endif


/* Port index to port address map */
static GPIO_TypeDef * const portmap[HAL_GPIO_PORT_COUNT] =
{
    HAL_GPIO_PORT_LIST
};

/* Storage for GPIO callbacks. */
struct gpio_irq_obj
{
    void *arg;
    hal_gpio_irq_handler_t isr;
};

static struct gpio_irq_obj gpio_irq_handlers[16];

struct ext_irqs
{
#if !MYNEWT_VAL(MCU_STM32L0) && !MYNEWT_VAL(MCU_STM32F0) && !MYNEWT_VAL(MCU_STM32G0)
    volatile uint32_t irq0;
    volatile uint32_t irq1;
    volatile uint32_t irq2;
    volatile uint32_t irq3;
    volatile uint32_t irq4;
#if defined(STM32U5) || defined(STM32L5) || defined(STM32H5)
    volatile uint32_t irq5;
    volatile uint32_t irq6;
    volatile uint32_t irq7;
    volatile uint32_t irq8;
    volatile uint32_t irq9;
    volatile uint32_t irq10;
    volatile uint32_t irq11;
    volatile uint32_t irq12;
    volatile uint32_t irq13;
    volatile uint32_t irq14;
    volatile uint32_t irq15;
#else
    volatile uint32_t irq9_5;
    volatile uint32_t irq15_10;
#endif
#else
    volatile uint32_t irq0_1;
    volatile uint32_t irq2_3;
    volatile uint32_t irq4_15;
#endif
};
struct ext_irqs ext_irq_counts;

/**
 * ext irq handler
 *
 * Handles the gpio interrupt attached to a gpio pin.
 *
 * @param index
 */
static void
ext_irq_handler(int index)
{
    uint32_t mask;

    mask = 1 << index;
    if (__HAL_GPIO_EXTI_GET_IT(mask) != RESET) {
        __HAL_GPIO_EXTI_CLEAR_IT(mask);
        gpio_irq_handlers[index].isr(gpio_irq_handlers[index].arg);
    }
}

#if !MYNEWT_VAL(MCU_STM32L0) && !MYNEWT_VAL(MCU_STM32F0) && !MYNEWT_VAL(MCU_STM32G0)
/* External interrupt 0 */
static void
ext_irq0(void)
{
    ++ext_irq_counts.irq0;
    ext_irq_handler(0);
}

/* External interrupt 1 */
static void
ext_irq1(void)
{
    ++ext_irq_counts.irq1;
    ext_irq_handler(1);
}

/* External interrupt 2 */
static void
ext_irq2(void)
{
    ++ext_irq_counts.irq2;
    ext_irq_handler(2);
}

/* External interrupt 3 */
static void
ext_irq3(void)
{
    ++ext_irq_counts.irq3;
    ext_irq_handler(3);
}

/**
 * ext irq4
 *
 *  External interrupt handler for external interrupt 4.
 *
 */
static void
ext_irq4(void)
{
    ++ext_irq_counts.irq4;
    ext_irq_handler(4);
}

#if defined(STM32U5) || defined(STM32L5) || defined(STM32H5)

static void
ext_irq5(void)
{
    ++ext_irq_counts.irq5;
    ext_irq_handler(5);
}

static void
ext_irq6(void)
{
    ++ext_irq_counts.irq6;
    ext_irq_handler(6);
}

static void
ext_irq7(void)
{
    ++ext_irq_counts.irq7;
    ext_irq_handler(7);
}

static void
ext_irq8(void)
{
    ++ext_irq_counts.irq8;
    ext_irq_handler(8);
}

static void
ext_irq9(void)
{
    ++ext_irq_counts.irq9;
    ext_irq_handler(9);
}

static void
ext_irq10(void)
{
    ++ext_irq_counts.irq10;
    ext_irq_handler(10);
}

static void
ext_irq11(void)
{
    ++ext_irq_counts.irq11;
    ext_irq_handler(11);
}

static void
ext_irq12(void)
{
    ++ext_irq_counts.irq12;
    ext_irq_handler(12);
}

static void
ext_irq13(void)
{
    ++ext_irq_counts.irq13;
    ext_irq_handler(13);
}

static void
ext_irq14(void)
{
    ++ext_irq_counts.irq14;
    ext_irq_handler(14);
}

static void
ext_irq15(void)
{
    ++ext_irq_counts.irq15;
    ext_irq_handler(15);
}

#else

/**
 * ext irq9_5
 *
 *  External interrupt handler for irqs 9 through 5.
 *
 */
static void
ext_irq9_5(void)
{
    int index;

    ++ext_irq_counts.irq9_5;
    for (index = 5; index <= 9; ++index) {
        ext_irq_handler(index);
    }
}

/**
 * ext irq15_10
 *
 *  External interrupt handler for irqs 15 through 10.
 *
 */
static void
ext_irq15_10(void)
{
    int index;

    ++ext_irq_counts.irq15_10;
    for (index = 10; index <= 15; ++index) {
        ext_irq_handler(index);
    }
}
#endif

#else /* MYNEWT_VAL(MCU_STM32L0) && MYNEWT_VAL(MCU_STM32F0)*/

/**
 * ext irq0_1
 *
 *  External interrupt handler for irqs 0 through 1.
 *
 */
static void
ext_irq0_1(void)
{
    int index;

    ++ext_irq_counts.irq0_1;
    for (index = 0; index <= 1; ++index) {
        ext_irq_handler(index);
    }
}

/**
 * ext irq2_3
 *
 *  External interrupt handler for irqs 2 through 3.
 *
 */
static void
ext_irq2_3(void)
{
    int index;

    ++ext_irq_counts.irq2_3;
    for (index = 2; index <= 3; ++index) {
        ext_irq_handler(index);
    }
}

/**
 * ext irq4_15
 *
 *  External interrupt handler for irqs 4 through 15.
 *
 */
static void
ext_irq4_15(void)
{
    int index;

    ++ext_irq_counts.irq4_15;
    for (index = 4; index <= 15; ++index) {
        ext_irq_handler(index);
    }
}

#endif /* MYNEWT_VAL(MCU_STM32L0) && MYNEWT_VAL(MCU_STM32F0)*/

/**
 * hal gpio clk enable
 *
 * Enable the port peripheral clock
 *
 * @param port_idx
 */
static void
hal_gpio_clk_enable(uint32_t port_idx)
{
    switch (port_idx) {
#if defined GPIOA_BASE
    case 0:
        __HAL_RCC_GPIOA_CLK_ENABLE();
        break;
#endif
#if defined GPIOB_BASE
    case 1:
        __HAL_RCC_GPIOB_CLK_ENABLE();
        break;
#endif
#if defined GPIOC_BASE
    case 2:
        __HAL_RCC_GPIOC_CLK_ENABLE();
        break;
#endif
#if defined GPIOD_BASE
    case 3:
        __HAL_RCC_GPIOD_CLK_ENABLE();
        break;
#endif
#if defined GPIOE_BASE
    case 4:
        __HAL_RCC_GPIOE_CLK_ENABLE();
        break;
#endif
#if defined GPIOF_BASE
    case 5:
        __HAL_RCC_GPIOF_CLK_ENABLE();
        break;
#endif
#if defined GPIOG_BASE
    case 6:
        __HAL_RCC_GPIOG_CLK_ENABLE();
        break;
#endif
#if defined GPIOH_BASE
    case 7:
        __HAL_RCC_GPIOH_CLK_ENABLE();
        break;
#endif
#if defined GPIOI_BASE
    case 8:
        __HAL_RCC_GPIOI_CLK_ENABLE();
        break;
#endif
#if defined GPIOJ_BASE
    case 9:
        __HAL_RCC_GPIOJ_CLK_ENABLE();
        break;
#endif
#if defined GPIOK_BASE
    case 10:
        __HAL_RCC_GPIOK_CLK_ENABLE();
        break;
#endif
    default:
        assert(0);
        break;
    }
}

/**
 * hal gpio pin to irq
 *
 * Converts the logical pin number to the IRQ number associated with the
 * external interrupt for that particular GPIO.
 *
 * @param pin
 *
 * @return IRQn_Type
 */
static IRQn_Type
hal_gpio_pin_to_irq(int pin)
{
    int index;
    IRQn_Type irqn;

    index = MCU_GPIO_PIN_NUM(pin);

#if defined(STM32L0) || defined(STM32F0) || defined(STM32G0)
    if (index <= 1) {
        irqn = EXTI0_1_IRQn;
    } else if (index <= 3) {
        irqn = EXTI2_3_IRQn;
    } else {
        irqn = EXTI4_15_IRQn;
    }
#elif defined(STM32U5) || defined(STM32L5) || defined(STM32H5)
    irqn = EXTI0_IRQn + index;
#else
    if (index <= 4) {
        irqn = EXTI0_IRQn + index;
    } else if (index <= 9) {
        irqn = EXTI9_5_IRQn;
    } else {
        irqn = EXTI15_10_IRQn;
    }
#endif

    return irqn;
}

#if MYNEWT_VAL(MCU_STM32F3)
#   define EXTI2_IRQn   EXTI2_TSC_IRQn
#endif

static void
hal_gpio_set_nvic(IRQn_Type irqn)
{
    uint32_t isr;

    switch (irqn) {
#if MYNEWT_VAL(MCU_STM32L0) || MYNEWT_VAL(MCU_STM32F0) || defined(STM32G0)
    case EXTI0_1_IRQn:
        isr = (uint32_t)&ext_irq0_1;
        break;

    case EXTI2_3_IRQn:
        isr = (uint32_t)&ext_irq2_3;
        break;

    case EXTI4_15_IRQn:
        isr = (uint32_t)&ext_irq4_15;
        break;
#else
    case EXTI0_IRQn:
        isr = (uint32_t)&ext_irq0;
        break;
    case EXTI1_IRQn:
        isr = (uint32_t)&ext_irq1;
        break;
    case EXTI2_IRQn:
        isr = (uint32_t)&ext_irq2;
        break;
    case EXTI3_IRQn:
        isr = (uint32_t)&ext_irq3;
        break;
    case EXTI4_IRQn:
        isr = (uint32_t)&ext_irq4;
        break;
#if defined(STM32U5) || defined(STM32L5) || defined(STM32H5)
    case EXTI5_IRQn:
        isr = (uint32_t)&ext_irq5;
        break;
    case EXTI6_IRQn:
        isr = (uint32_t)&ext_irq6;
        break;
    case EXTI7_IRQn:
        isr = (uint32_t)&ext_irq7;
        break;
    case EXTI8_IRQn:
        isr = (uint32_t)&ext_irq8;
        break;
    case EXTI9_IRQn:
        isr = (uint32_t)&ext_irq9;
        break;
    case EXTI10_IRQn:
        isr = (uint32_t)&ext_irq10;
        break;
    case EXTI11_IRQn:
        isr = (uint32_t)&ext_irq11;
        break;
    case EXTI12_IRQn:
        isr = (uint32_t)&ext_irq12;
        break;
    case EXTI13_IRQn:
        isr = (uint32_t)&ext_irq13;
        break;
    case EXTI14_IRQn:
        isr = (uint32_t)&ext_irq14;
        break;
    case EXTI15_IRQn:
        isr = (uint32_t)&ext_irq15;
        break;
#else
    case EXTI9_5_IRQn:
        isr = (uint32_t)&ext_irq9_5;
        break;
    case EXTI15_10_IRQn:
        isr = (uint32_t)&ext_irq15_10;
        break;
#endif
#endif

    default:
        assert(0);
        break;
    }

    /* Set isr in vector table if not yet set */
    if (NVIC_GetVector(irqn) != isr) {
        NVIC_SetVector(irqn, isr);
        NVIC_EnableIRQ(irqn);
    }
}

/**
 * hal gpio init
 *
 * Called to initialize a gpio.
 *
 * @param pin
 * @param cfg
 *
 * @return int
 */
int
hal_gpio_init_stm(int pin, GPIO_InitTypeDef *cfg)
{
    int port;
    uint32_t mcu_pin_mask;

    /* Is this a valid pin? */
    port = MCU_GPIO_PIN_PORT(pin);
    if (port >= HAL_GPIO_PORT_COUNT) {
        return -1;
    }

    mcu_pin_mask = GPIO_MASK(pin);
    cfg->Pin = mcu_pin_mask;

    /* Enable the GPIO clock */
    hal_gpio_clk_enable(port);

    /* Initialize pin as an input, setting proper mode */
    HAL_GPIO_Init(portmap[port], cfg);

    return 0;
}

/**
 * hal gpio deinit
 *
 * Called to deinitialize a gpio.
 *
 * @param pin
 * @param cfg
 *
 * @return int
 */
int
hal_gpio_deinit_stm(int pin, GPIO_InitTypeDef *cfg)
{
    int port;
    uint32_t mcu_pin_mask;

    /* Is this a valid pin? */
    port = MCU_GPIO_PIN_PORT(pin);
    if (port >= HAL_GPIO_PORT_COUNT) {
        return -1;
    }

    mcu_pin_mask = GPIO_MASK(pin);
    cfg->Pin = mcu_pin_mask;

    /* Initialize pin as an input, setting proper mode */
    HAL_GPIO_DeInit(portmap[port], cfg->Pin);

    return 0;
}

/**
 * gpio init in
 *
 * Initializes the specified pin as an input
 *
 * @param pin   Pin number to set as input
 * @param pull  pull type
 *
 * @return int  0: no error; -1 otherwise.
 */
int
hal_gpio_init_in(int pin, hal_gpio_pull_t pull)
{
    int rc;
    GPIO_InitTypeDef init_cfg;

    init_cfg.Mode = GPIO_MODE_INPUT;
    init_cfg.Pull = pull;

    rc = hal_gpio_init_stm(pin, &init_cfg);
    return rc;
}

/**
 * gpio init out
 *
 * Initialize the specified pin as an output, setting the pin to the specified
 * value.
 *
 * @param pin Pin number to set as output
 * @param val Value to set pin
 *
 * @return int  0: no error; -1 otherwise.
 */
int hal_gpio_init_out(int pin, int val)
{
    GPIO_InitTypeDef cfg;
    int port;

    /* Is this a valid pin? */
    port = MCU_GPIO_PIN_PORT(pin);
    if (port >= HAL_GPIO_PORT_COUNT) {
        return -1;
    }

    /* Enable the GPIO clock */
    hal_gpio_clk_enable(port);

    /* Write initial output value */
    hal_gpio_write(pin, val);

    cfg.Pin = GPIO_MASK(pin);
    cfg.Mode = GPIO_MODE_OUTPUT_PP;
    cfg.Pull = GPIO_NOPULL;
#if defined(GPIO_SPEED_FREQ_VERY_HIGH)
    cfg.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
#elif defined(GPIO_SPEED_HIGH)
    cfg.Speed = GPIO_SPEED_HIGH;
#else
    cfg.Speed = GPIO_SPEED_FREQ_HIGH;
#endif
#if !MYNEWT_VAL(MCU_STM32F1)
    cfg.Alternate = 0;
#endif

    /* Initialize pin as an output, setting proper mode */
    HAL_GPIO_Init(portmap[port], &cfg);

    return 0;
}

/**
 * gpio init af
 *
 * Configure the specified pin for AF.
 */
int
hal_gpio_init_af(int pin, uint8_t af_type, enum hal_gpio_pull pull, uint8_t od)
{
    GPIO_InitTypeDef gpio;

    if (!od) {
        gpio.Mode = GPIO_MODE_AF_PP;
    } else {
        gpio.Mode = GPIO_MODE_AF_OD;
    }
    gpio.Pull = pull;
#if defined(GPIO_SPEED_FREQ_VERY_HIGH)
    gpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
#elif defined(GPIO_SPEED_HIGH)
    gpio.Speed = GPIO_SPEED_HIGH;
#else
    gpio.Speed = GPIO_SPEED_FREQ_HIGH;
#endif
#if !MYNEWT_VAL(MCU_STM32F1)
    gpio.Alternate = af_type;
#endif

    return hal_gpio_init_stm(pin, &gpio);
}

/**
 * gpio init af
 *
 * Configure the specified pin for AF.
 */
int
hal_gpio_init_fun(uint32_t pin)
{
    return hal_gpio_init_af(pin & 0xFF, (pin >> 12) & 0xF, (pin >> 8) & 3,
                            (pin >> 10) & 1);
}

/**
 * Deinitialize the specified pin to revert to default configuration
 *
 * @param pin Pin number to unset
 *
 * @return int  0: no error; -1 otherwise.
 */
int
hal_gpio_deinit(int pin)
{
    GPIO_InitTypeDef gpio;
    return hal_gpio_deinit_stm(pin, &gpio);
}

/**
 * gpio write
 *
 * Write a value (either high or low) to the specified pin.
 *
 * @param pin Pin to set
 * @param val Value to set pin (0:low 1:high)
 */
void hal_gpio_write(int pin, int val)
{
    int port;
    uint32_t mcu_pin_mask;
    GPIO_PinState state;

    port = MCU_GPIO_PIN_PORT(pin);
    mcu_pin_mask = GPIO_MASK(pin);

    if (val) {
        state = GPIO_PIN_SET;
    } else {
        state = GPIO_PIN_RESET;
    }

    HAL_GPIO_WritePin(portmap[port], mcu_pin_mask, state);
}

/**
 * gpio read
 *
 * Reads the specified pin.
 *
 * @param pin Pin number to read
 *
 * @return int 0: low, 1: high
 */
int hal_gpio_read(int pin)
{
    int port;
    uint32_t mcu_pin_mask;

    port = MCU_GPIO_PIN_PORT(pin);
    mcu_pin_mask = GPIO_MASK(pin);
    return HAL_GPIO_ReadPin(portmap[port], mcu_pin_mask);
}

/**
 * gpio toggle
 *
 * Toggles the specified pin
 *
 * @param pin Pin number to toggle
 *
 * @return current pin state int 0: low 1 : high
 */
int hal_gpio_toggle(int pin)
{
    int pin_state = (hal_gpio_read(pin) != 1);
    hal_gpio_write(pin, pin_state);
    return pin_state;
}

/**
 * gpio irq init
 *
 * Initialize an external interrupt on a gpio pin
 *
 * @param pin       Pin number to enable gpio.
 * @param handler   Interrupt handler
 * @param arg       Argument to pass to interrupt handler
 * @param trig      Trigger mode of interrupt
 * @param pull      Push/pull mode of input.
 *
 * @return int
 */
int
hal_gpio_irq_init(int pin, hal_gpio_irq_handler_t handler, void *arg,
                  hal_gpio_irq_trig_t trig, hal_gpio_pull_t pull)
{
    int rc;
    int irqn;
    int index;
    uint32_t pin_mask;
    uint32_t mode;
    GPIO_InitTypeDef init_cfg;

    /* Configure the gpio for an external interrupt */
    rc = 0;
    switch (trig) {
    case HAL_GPIO_TRIG_NONE:
        rc = -1;
        break;
    case HAL_GPIO_TRIG_RISING:
        mode = GPIO_MODE_IT_RISING;
        break;
    case HAL_GPIO_TRIG_FALLING:
        mode = GPIO_MODE_IT_FALLING;
        break;
    case HAL_GPIO_TRIG_BOTH:
        mode = GPIO_MODE_IT_RISING_FALLING;
        break;
    case HAL_GPIO_TRIG_LOW:
        rc = -1;
        break;
    case HAL_GPIO_TRIG_HIGH:
        rc = -1;
        break;
    default:
        rc = -1;
        break;
    }

    /* Check to make sure no error has occurred */
    if (!rc) {
        /* Disable interrupt and clear any pending */
        hal_gpio_irq_disable(pin);
        pin_mask = GPIO_MASK(pin);
        __HAL_GPIO_EXTI_CLEAR_FLAG(pin_mask);

        /* Set the gpio irq handler */
        index = MCU_GPIO_PIN_NUM(pin);
        gpio_irq_handlers[index].isr = handler;
        gpio_irq_handlers[index].arg = arg;

        /* Configure the GPIO */
        init_cfg.Mode = mode;
        init_cfg.Pull = pull;
        rc = hal_gpio_init_stm(pin, &init_cfg);
        if (!rc) {
            /* Enable interrupt vector in NVIC */
            irqn = hal_gpio_pin_to_irq(pin);
            hal_gpio_set_nvic(irqn);
        }
    }

    return rc;
}

/**
 * gpio irq release
 *
 * No longer interrupt when something occurs on the pin. NOTE: this function
 * does not change the GPIO push/pull setting nor does it change the
 * SYSCFG EXTICR registers. It also does not disable the NVIC interrupt enable
 * setting for the irq.
 *
 * @param pin
 */
void
hal_gpio_irq_release(int pin)
{
    int index;
    uint32_t pin_mask;

    /* Disable the interrupt */
    hal_gpio_irq_disable(pin);

    /* Clear any pending interrupts */
    pin_mask = GPIO_MASK(pin);
    __HAL_GPIO_EXTI_CLEAR_FLAG(pin_mask);

    /* Clear out the irq handler */
    index = MCU_GPIO_PIN_NUM(pin);
    gpio_irq_handlers[index].arg = NULL;
    gpio_irq_handlers[index].isr = NULL;
}

/**
 * gpio irq enable
 *
 * Enable the irq on the specified pin
 *
 * @param pin
 */
void
hal_gpio_irq_enable(int pin)
{
    uint32_t ctx;
    uint32_t mask;

    mask = GPIO_MASK(pin);

    __HAL_DISABLE_INTERRUPTS(ctx);
#if MYNEWT_VAL(MCU_STM32L4) || MYNEWT_VAL(MCU_STM32WB) ||                     \
    MYNEWT_VAL(MCU_STM32H5) || MYNEWT_VAL(MCU_STM32H7) ||                     \
    MYNEWT_VAL(MCU_STM32G0) || MYNEWT_VAL(MCU_STM32G4) || MYNEWT_VAL(MCU_STM32U5)
    EXTI->IMR1 |= mask;
#else
    EXTI->IMR |= mask;
#endif
    __HAL_ENABLE_INTERRUPTS(ctx);
}

/**
 * gpio irq disable
 *
 *
 * @param pin
 */
void
hal_gpio_irq_disable(int pin)
{
    uint32_t ctx;
    uint32_t mask;

    mask = GPIO_MASK(pin);
    __HAL_DISABLE_INTERRUPTS(ctx);
#if MYNEWT_VAL(MCU_STM32L4) || MYNEWT_VAL(MCU_STM32WB) ||                     \
    MYNEWT_VAL(MCU_STM32H5) || MYNEWT_VAL(MCU_STM32H7) ||                     \
    MYNEWT_VAL(MCU_STM32G0) || MYNEWT_VAL(MCU_STM32G4) || MYNEWT_VAL(MCU_STM32U5)

    EXTI->IMR1 |= mask;
#else
    EXTI->IMR &= ~mask;
#endif
    __HAL_ENABLE_INTERRUPTS(ctx);
}
