/*
* Copyright (c) 2023-2028, SpaceTouch Inc.
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date          Author      Notes
* 2023-04-18    dongx       the first version
* 2023-06-05    weijiac     add gpio interrupt part
* 2023-07-24    weijiac     add spio part
*/

#include <stdio.h>
#include <string.h>

//#include <hal_mem.h>
#include <hal_gpio.h>
#include <hal_log.h>
#include <hal_common.h>
#include <interrupt.h>


//callback function
static hal_gpio_callback_t gpio_interrput_cb[NUM_GPIO_IC] = {0};
static hal_spio_callback_t spio_interrput_cb = {0};

int hal_gpio_set_ininvert(gpio_pin_t pin, bool enable)
{
    return 0;
}

int hal_gpio_set_outinvert(gpio_pin_t pin, bool enable)
{
    return 0;
}


bool hal_gpio_check_valid(gpio_pin_t pin)
{
    if (pin >= GPIONUM || pin < 0)
    {
        return false;
    }

    return true;
}

void hal_gpio_reset_init(gpio_reset_config_type_t *gpio_reset_type)
{
    hal_gpio_set_reset_pullup(gpio_reset_type->reset_pull);
    hal_gpio_reset_set_pin(gpio_reset_type->pin);

    if (gpio_reset_type->reset_enable == GPIO_ENABLE)
        hal_set_gpio_reset_enable();
    else
        hal_set_gpio_reset_disable();
}

/**
 * @brief       set gpio reset pin pull up, highly recommend pull up before enable reset function
 * @param       gpio_pin_t          pin，
 *              gpio_enable_t   enable
 * @return      none
 */
inline void hal_gpio_set_reset_pullup(gpio_rstn_pull_t  gpio_rstn_pull)
{
    if (gpio_rstn_pull == GPIO_RSTN_PU)
        DEV_GPIO->RSTN |= DEV_GPIO_RSTN_PU_ENABLE;
    else if (gpio_rstn_pull == GPIO_RSTN_PU_DISABLE)
        DEV_GPIO->RSTN &= ~(DEV_GPIO_RSTN_PU_ENABLE);
}

/**
 * @brief       set gpio reset pin
 * @param       gpio_pin_t          pin，
 * @return      none
 */
inline void hal_gpio_reset_set_pin(gpio_pin_t pin)
{
    DEV_GPIO->RSTN &= ~(DEV_GPIO_RSTN_SEL_MASK);
    DEV_GPIO->RSTN |= pin;
}

inline void hal_set_gpio_reset_enable(void)
{
    DEV_GPIO->RSTN |= DEV_GPIO_RSTN_EN_ENABLE;
}

inline void hal_set_gpio_reset_disable(void)
{
    DEV_GPIO->RSTN &= ~(DEV_GPIO_RSTN_EN_ENABLE);
}


/**
 * @brief       clear all setting of gpio_reset
 * @param       none
 * @return      none
 */
void hal_gpio_clear_reset(void)
{
    DEV_GPIO->RSTN &= ~(DEV_GPIO_RSTN_SEL_MASK | DEV_GPIO_RSTN_EN_ENABLE | DEV_GPIO_RSTN_EN_ENABLE);
}

/**
 * @brief       when direction is output, can use it to toggle output level
 * @param       none
 * @return      none
 */
inline void hal_gpio_output_toggle(gpio_pin_t pin)
{
    DEV_GPIO->CTL[pin] ^= DEV_GPIO_CTL_O;
}



/**
 * @brief       can get gpio level when gpio pin is output or input
 * @param       gpio_pin_t      pin，
 *              gpio_data_t*    data
 * @return      none
 */
void hal_gpio_get_data(gpio_pin_t pin, gpio_data_t *data)
{
    //uint32_t reg;
    uint32_t pin_offset;
    pin_offset = (uint32_t)pin;
    gpio_direction_t direction = GPIO_DIRECTION_INPUT;
    hal_gpio_get_direction(pin, &direction);

    if (direction == GPIO_DIRECTION_INPUT)
    {
        if (pin_offset < 32)
            *data = (DEV_GPIO->IN0 & (1 << pin_offset)) >> pin_offset ;
        else
            *data = (DEV_GPIO->IN1 & (1 << (pin_offset - 32))) >> (pin_offset - 32) ;
    }
    else if (direction == GPIO_DIRECTION_OUTPUT)
    {
        *data = ((DEV_GPIO->CTL[pin] & DEV_GPIO_CTL_O) >> 9);
    }
}
/**
 * @brief       uint8_t hal_gpio_read(gpio_pin_t pin)
 * @param       gpio_pin_t      pin，
 * @return      none
 */
uint8_t hal_gpio_read(gpio_pin_t pin)
{
    uint8_t data;

    if (pin < 32)
    {
        data = (DEV_GPIO->IN0 & (1 << pin)) >> pin ;
    }
    else
    {
        data = (DEV_GPIO->IN1 & (1 << (pin - 32))) >> (pin - 32) ;
    }

    return data;
}


/**
* @brief        读取GPIO输入寄存器值
* @param        None
* @return       uint64_t GPIO输入寄存器值
*/
uint64_t hal_gpio_read_inputport(void)
{
    uint64_t temp;
    temp = DEV_GPIO->IN1;
    temp <<= 32;
    temp |= DEV_GPIO->IN0;
    return temp;
}


/**
 * @brief       set gpio level, take effect only when directio is output
 * @param       gpio_pin_t          pin，
 *              gpio_data_t         data    gpio level
 * @return      none
 */
void hal_gpio_set_data(gpio_pin_t pin, gpio_data_t data)
{
    uint32_t reg;
    uint32_t pin_offset;
    pin_offset = (uint32_t)pin;
    reg = hal_readl(DEV_GPIO->CTL + pin_offset);

    if (data)
        reg |= DEV_GPIO_CTL_O;
    else
        reg &= ~DEV_GPIO_CTL_O;

    hal_writel(reg, DEV_GPIO->CTL + pin_offset);
}

/**
 * @brief       set GPIO output/input
 * @param       gpio_pin_t              pin，
 *              gpio_direction_t        direction
 * @return      none
 */
void hal_gpio_set_direction(gpio_pin_t pin, gpio_direction_t direction)
{
    uint32_t reg;
    uint32_t pin_offset;
    pin_offset = (uint32_t)pin;
    reg = hal_readl(DEV_GPIO->CTL + pin_offset);
    reg &= ~(DEV_GPIO_CTL_OE_MASK | DEV_GPIO_CTL_IE_MASK);

    if (direction == GPIO_DIRECTION_INPUT)
        reg |= (DEV_GPIO_CTL_IE_ENABLE | DEV_GPIO_CTL_OE_DISABLE);
    else if (direction == GPIO_DIRECTION_OUTPUT)
        reg |= (DEV_GPIO_CTL_IE_DISABLE | DEV_GPIO_CTL_OE_ENABLE);
    else
        reg |= (DEV_GPIO_CTL_IE_DISABLE | DEV_GPIO_CTL_OE_DISABLE);

    hal_writel(reg, DEV_GPIO->CTL + pin_offset);
}
/**
 * @brief       read the output/input direction of the GPIO pin
 * @param       gpio_pin_t pin，
 *              gpio_direction_t    *direction
 * @return      none
 */
void hal_gpio_get_direction(gpio_pin_t pin, gpio_direction_t *direction)
{
    uint32_t reg;
    uint32_t pin_offset;
    pin_offset = (uint32_t)pin;
    reg = hal_readl(DEV_GPIO->CTL + pin_offset);

    if (reg & DEV_GPIO_CTL_OE_MASK)
        *direction = GPIO_DIRECTION_OUTPUT;

    if (reg & DEV_GPIO_CTL_IE_MASK)
        *direction = GPIO_DIRECTION_INPUT;
}
/**
 * @brief       set GPIO pin pull up or pull down
 * @param       gpio_pin_t pin
 *              gpio_pull_status_t pull     pull up/pull down
 * @return      none
 */
void hal_gpio_set_pull(gpio_pin_t pin, gpio_pull_status_t pull)
{
    uint32_t reg;
    uint32_t pin_offset;
    pin_offset = (uint32_t)pin;
    reg = hal_readl(DEV_GPIO->CTL + pin_offset);
    reg &= ~(DEV_GPIO_CTL_PS_MASK | DEV_GPIO_CTL_PE_MASK);

    if (pull == GPIO_PULL_UP)
        reg |= (DEV_GPIO_CTL_PS_PULLUP | DEV_GPIO_CTL_PE_ENABLE);
    else if (pull == GPIO_PULL_DOWN)
        reg |= (DEV_GPIO_CTL_PS_PULLDOWN | DEV_GPIO_CTL_PE_ENABLE);
    else
        reg |= DEV_GPIO_CTL_PE_DISABLE;

    hal_writel(reg, DEV_GPIO->CTL + pin_offset);
}
/**
 * @brief       read gpio pull up or pull down status
 * @param       gpio_pin_t pin，
 *              gpio_pull_status_t *pull
 * @return      none
 */
void hal_gpio_get_pull(gpio_pin_t pin, gpio_pull_status_t *pull)
{
    uint32_t reg;
    uint32_t pin_offset;
    pin_offset = (uint32_t)pin;
    reg = hal_readl(DEV_GPIO->CTL + pin_offset);

    if (!(reg & DEV_GPIO_CTL_PE_MASK))
        *pull =  GPIO_PULL_DOWN_DISABLED;
    else if (reg & DEV_GPIO_CTL_PS_MASK)
        *pull =  GPIO_PULL_UP;
    else
        *pull =  GPIO_PULL_DOWN;
}
/**
 * @brief       set drive strength
 * @param       gpio_pin_t              pin，
 *              gpio_driving_level_t    level
 * @return      none
 */
void hal_gpio_set_driving_level(gpio_pin_t pin, gpio_driving_level_t level)
{
    uint32_t reg;
    uint32_t pin_offset;
    pin_offset = (uint32_t)pin;
    reg = hal_readl(DEV_GPIO->CTL + pin_offset);
    reg &= ~DEV_GPIO_CTL_DS;
    hal_writel(reg | level, DEV_GPIO->CTL + pin_offset);
}
/**
 * @brief       set drive strength
 * @param       gpio_pin_t pin，
 *              gpio_driving_level_t *level
 * @return      none
 */
void hal_gpio_get_driving_level(gpio_pin_t pin, gpio_driving_level_t *level)
{
    uint32_t reg;
    uint32_t pin_offset;
    pin_offset = (uint32_t)pin;
    reg = hal_readl(DEV_GPIO->CTL + pin_offset);
    *level = (reg & DEV_GPIO_CTL_DS) >> 12;
}
/**
 * @brief       set GPIO multiplexing function
 * @param       gpio_pin_t pin，
 *              gpio_muxsel_t function_index
 * @return      none
 */
void hal_gpio_pinmux_set_function(gpio_pin_t pin, gpio_muxsel_t function_index)
{
    uint32_t reg;
    uint32_t pin_offset;
    pin_offset = (uint32_t)pin;
    reg = hal_readl(DEV_GPIO->CTL + pin_offset);
    reg &= ~(DEV_GPIO_CTL_MFP_MASK);
    hal_writel(reg | function_index, DEV_GPIO->CTL + pin_offset);
}
/**
 * @brief       get GPIO multiplexing function
 * @param       gpio_pin_t pin，               GPIO管脚号
 *              gpio_muxsel_t *function_index   读取引脚功能
 * @return      none
 */
void hal_gpio_pinmux_get_function(gpio_pin_t pin, gpio_muxsel_t *function_index)
{
    uint32_t reg;
    uint32_t pin_offset;
    pin_offset = (uint32_t)pin;
    reg = hal_readl(DEV_GPIO->CTL + pin_offset);
    *function_index = reg & DEV_GPIO_CTL_MFP_MASK;
}

/**
 * @brief       the clock of GPIO in SPV40 always on, so the function is empty
 * @param       none
 * @return      none
 */
int hal_gpio_init(void)
{
    //hal_log_info("gpio init success!\n");
    return 0;
}




/**
 * @brief       enable interrupt signal generation
 * @param       none
 * @return      none
 */
inline void hal_gpio_interrupt_init(void)
{
    //初始化时钟,使能中断信号
    DEV_CMU->CLKEN0 |= DEV_CMU_CLKEN0_EINT;
    __nds__plic_enable_interrupt(IRQ_SOURCE_GPIO0);
}

/**
 * @brief       initialize all settings of the gpio interrupt
 * @param       GPIO_Interrupt_ConfigType* gpio_interrupt_struct
 * @return      none
 */
void hal_gpio_interrput_config(gpio_interrupt_config_type_t *gpio_interrupt_struct)
{
    DEV_GPIO->ICn[gpio_interrupt_struct->ic] &= ~(DEV_GPIO_ICn_EVENT_MASK | DEV_GPIO_ICn_SOURCE_MASK);
    DEV_GPIO->ICn[gpio_interrupt_struct->ic] |= gpio_interrupt_struct->pin | (gpio_interrupt_struct->event << 6);

    if ((gpio_interrupt_struct->event == HIGH_LEVEL) || (gpio_interrupt_struct->event == BOTH_EDGE) || (gpio_interrupt_struct->event == RISING_EDGE))
        hal_gpio_interrupt_clear_interrupt_pending(gpio_interrupt_struct->ic);

    DEV_GPIO->IE0 |= (0x1UL << gpio_interrupt_struct->ic);
}

/**
 * @brief       set a trigger event for one interrupt channel
 * @param       gpio_interrupt_control_t        icx     interrupt channel
 *              gpio_interrupt_trigger_event_t  event
 * @return      none
 */
inline void hal_gpio_interrupt_set_event(gpio_interrupt_channel_t icx, gpio_interrupt_trigger_event_t event)
{
    DEV_GPIO->ICn[icx] &= ~DEV_GPIO_ICn_EVENT_MASK;
    DEV_GPIO->ICn[icx] |= (event << 6);

    if ((event == HIGH_LEVEL) || (event == BOTH_EDGE) || (event == RISING_EDGE))
        hal_gpio_interrupt_clear_interrupt_pending(icx);
}

/**
 * @brief       set a pin for a interrupt channel
 * @param       gpio_pin_t
 *              gpio_interrupt_control_t
 * @return      none
 */
inline void hal_gpio_interrupt_set_pin(gpio_pin_t pin, gpio_interrupt_channel_t icx)
{
    DEV_GPIO->ICn[icx] &= ~DEV_GPIO_ICn_SOURCE_MASK;
    DEV_GPIO->ICn[icx] |= pin;
}

/**
 * @brief       enable a interrupt channel
 * @param       gpio_interrupt_control_t        icx
 * @return      none
 */
inline void hal_set_gpio_interrupt_enable(gpio_interrupt_channel_t icx)
{
    DEV_GPIO->IE0 |= (0x1UL << icx);
}

/**
 * @brief       disable interrupt channel
 * @param       gpio_interrupt_control_t        icx
 * @return      none
 */
inline void hal_set_gpio_interrupt_disable(gpio_interrupt_channel_t icx)
{
    DEV_GPIO->IE0 &= ~(0x1UL << icx);
}

/**
 * @brief       get a gpio interrupt status
 * @param       gpio_interrupt_control_t    icx
 * @return      none
 */
inline uint32_t hal_gpio_interrupt_get_interrupt_status(gpio_interrupt_channel_t icx)
{
    return DEV_GPIO->PD & (0x1UL << icx) ? (SET) : (RESET);
}

/**
 * @brief       clear pending
 * @param       gpio_interrupt_control_t    icx
 * @return      none
 */
inline void hal_gpio_interrupt_clear_interrupt_pending(gpio_interrupt_channel_t icx)
{
    DEV_GPIO->PD = (0x1UL << icx);
}

/**
 * @brief       find the idle interrupt channel
 * @param       if the interrupt channel trigger event is none event ,
 *              regarde it as idle channel
 * @return      0-3     Idle interrupt channel
 *              -1      no idle interrupt channel
 */
int hal_gpio_find_free_interrupt_channel(void)
{
    for (int i = 0 ; i < NUM_GPIO_IC; i++)
    {
        if ((DEV_GPIO->ICn[i] & DEV_GPIO_ICn_EVENT_MASK) == 0)
            return  GPIO_IC0 + i ;
    }

    return -1;
}

/**
 * @brief       find the busy interrupt channel
 * @param       if the interrupt channel trigger event is not none event ,
 *              regarde it as busy channel
 * @return      0-3     busy interrupt channel
 *              -1      no busy interrupt channel
 */
int hal_gpio_find_busy_interrupt_channel(void)
{
    for (int i = 0 ; i < NUM_GPIO_IC; i++)
    {
        if (DEV_GPIO->ICn[i] & DEV_GPIO_ICn_EVENT_MASK)
            return  GPIO_IC0 + i ;
    }

    return -1;
}

int hal_gpio_find_interrupt_channel_by_pin(gpio_pin_t pin)
{
    //hal_log_info("get into hal_gpio_find_interrupt_channel_by_pin\n");
    for (int i = 0; i < NUM_GPIO_IC; i++)
    {
        //hal_log_info("inter for , i = %d",i);
        if (((DEV_GPIO->ICn[i] & DEV_GPIO_ICn_SOURCE_MASK) == pin) && (DEV_GPIO->ICn[i] & DEV_GPIO_ICn_EVENT_MASK))
            return GPIO_IC0 + i;
    }

    return -1;
}

/**
 * @brief       set a callback function for an interrupt channel
 * @detail      the callback function will be called in interrupt function when interrupt event is triggered
 *              and if cancel callback function, call this function and set callback = NULL, param = NULL
 * @param       gpio_interrupt_control_t icx
 *              void*                   callback    function pointer to callback function
 *              void*                   param       the param of the callback function
 * @return      none
 */
void hal_gpio_interrupt_set_callback(gpio_interrupt_channel_t icx, void *callback, void *param)
{
    gpio_interrput_cb[icx].callback = (gpio_callback)callback;
    gpio_interrput_cb[icx].param = param;
}

void hal_gpio_clear_interrupt_control(gpio_interrupt_channel_t icx)
{
    DEV_GPIO->ICn[icx] &= ~DEV_GPIO_ICn_EVENT_MASK;
    DEV_GPIO->ICn[icx] &= ~DEV_GPIO_ICn_SOURCE_MASK;
    hal_gpio_interrupt_set_callback(icx, (void *)0, (void *)0);
}

void hal_gpio_print_pinmux(void)
{
    gpio_muxsel_t muxsel = GPIO_MUXSEL_GPIO;

    for (int i = 0; i < GPIONUM; i++)
    {
        hal_gpio_pinmux_get_function(GPIO0 + i, &muxsel);
        hal_log_info("gpio%d pimux is %d", GPIO0 + i, muxsel);
    }
}


/********************************************   SPIO   **************************************/

/** These  parameter configurations are the same for gpio and spio:
 *      gpio_data_t
 *      gpio_direction_t
 *      gpio_pull_status_t
 *      gpio_interrupt_trigger_event_t
 * */

/**
 * @brief       check the pins is legal or not
 * @param       spio_pin_t  pin
 * @return      ture        correct gpio
 *              false       error   gpio
 */
bool hal_spio_check_valid(spio_pin_t pin)
{
    if (pin >= SPIONUM || pin < 0)
    {
        return false;
    }

    return true;
}

/**
* @brief        读取指定GPIO管脚的输入电平
* @param        uint32_t pin， GPIO管脚号，从枚举定义 #GPIO_ChannelEnum 选择。
* @return       uint32_t 输入电平值： RESET(0), SET(1).
*/
inline uint32_t spio_read_input_pin(spio_pin_t pin)
{
    return ((AON_GPIO->SPIO_IN & (1UL << pin)) ? (SET) : (RESET));
}

/**
 * @brief       get spio level
 * @param       spio_pin_t pin，
 *              gpio_data_t *data
 * @return      none
 */
void hal_spio_get_data(spio_pin_t pin, spio_data_t *data)
{
    uint32_t reg;
    uint32_t pin_offset;
    pin_offset = (uint32_t)pin;
    spio_direction_t direction;
    hal_spio_get_direction(pin, &direction);

    if (direction == SPIO_DIRECTION_INPUT)
    {
        reg = hal_readl(&AON_GPIO->SPIO_IN);
        *data = (reg & BIT(pin_offset)) >> pin_offset;
    }
    else if (direction == SPIO_DIRECTION_OUTPUT)
    {
        *data = (AON_GPIO->SPIOn_CTL[pin] & AON_GPIO_SPIOn_CTL_O) >> 9;
    }
}

uint8_t hal_spio_read(spio_pin_t pin)
{
	uint32_t reg;
	uint8_t data;
	uint32_t pin_offset;
	pin_offset = (uint32_t)pin;
	reg = hal_readl(&AON_GPIO->SPIO_IN);
	data = (reg & BIT(pin_offset)) >> pin_offset;
	return data;
}


/**
 * @brief       set spio level
 * @param       spio_pin_t pin，
 *              gpio_data_t data
 * @return      none
 */
void hal_spio_set_data(spio_pin_t pin, spio_data_t data)
{
    uint32_t reg;
    uint32_t pin_offset;
    pin_offset = (uint32_t)pin;
    reg = hal_readl(AON_GPIO->SPIOn_CTL + pin_offset);

    if (data)
        reg |= AON_GPIO_SPIOn_CTL_O;
    else
        reg &= ~AON_GPIO_SPIOn_CTL_O;

    hal_writel(reg, AON_GPIO->SPIOn_CTL + pin_offset);
}

/**
 * @brief       set SPIO output/input
 * @param       spio_pin_t pin，
 *              gpio_direction_t direction
 * @return      none
 */
void hal_spio_set_direction(spio_pin_t pin, spio_direction_t direction)
{
    uint32_t reg;
    uint32_t pin_offset;
    pin_offset = (uint32_t)pin;
    reg = hal_readl(AON_GPIO->SPIOn_CTL + pin_offset);
    reg &= ~(AON_GPIO_SPIOn_CTL_OE_MASK | AON_GPIO_SPIOn_CTL_IE_MASK);

    if (direction == SPIO_DIRECTION_INPUT)
        reg |= (AON_GPIO_SPIOn_CTL_IE_ENABLE | AON_GPIO_SPIOn_CTL_OE_DISABLE);
    else if (direction == SPIO_DIRECTION_OUTPUT)
        reg |= (AON_GPIO_SPIOn_CTL_IE_DISABLE | AON_GPIO_SPIOn_CTL_OE_ENABLE);
    else
        reg |= (AON_GPIO_SPIOn_CTL_IE_DISABLE | AON_GPIO_SPIOn_CTL_OE_DISABLE);

    hal_writel(reg, AON_GPIO->SPIOn_CTL + pin_offset);
}
/**
 * @brief       get the output/input direction of the GPIO pin
 * @param       spio_pin_t pin，
 *              gpio_direction_t *direction
 * @return      none
 */
void hal_spio_get_direction(spio_pin_t pin, spio_direction_t *direction)
{
    uint32_t reg;
    uint32_t pin_offset;
    pin_offset = (uint32_t)pin;
    reg = hal_readl(AON_GPIO->SPIOn_CTL + pin_offset);

    if (reg & AON_GPIO_SPIOn_CTL_OE_MASK)
        *direction = SPIO_DIRECTION_OUTPUT;

    if (reg & AON_GPIO_SPIOn_CTL_IE_MASK)
        *direction = SPIO_DIRECTION_INPUT;
}
/**
 * @brief       set SPIO pin pull up or pull down
 * @param       spio_pin_t pin
 *              gpio_pull_status_t pull
 * @return      none
 */
void hal_spio_set_pull(spio_pin_t pin, spio_pull_status_t pull)
{
    uint32_t reg;
    uint32_t pin_offset;
    pin_offset = (uint32_t)pin;
    reg = hal_readl(AON_GPIO->SPIOn_CTL + pin_offset);
    reg &= ~(AON_GPIO_SPIOn_CTL_PS_MASK | AON_GPIO_SPIOn_CTL_PE_MASK);

    if (pull == SPIO_PULL_UP)
        reg |= (AON_GPIO_SPIOn_CTL_PS_PULLUP | AON_GPIO_SPIOn_CTL_PE_ENABLE);
    else if (pull == SPIO_PULL_DOWN)
        reg |= (AON_GPIO_SPIOn_CTL_PS_PULLDOWN | AON_GPIO_SPIOn_CTL_PE_ENABLE);
    else
        reg |= AON_GPIO_SPIOn_CTL_PE_DISABLE;

    hal_writel(reg, AON_GPIO->SPIOn_CTL + pin_offset);
}



/**
 * @brief       get SPIO pin pull up or pull down state
 * @param       gpio_pin_t pin，
 *              gpio_pull_status_t *pull
 * @return      none
 */
void hal_spio_get_pull(spio_pin_t pin, spio_pull_status_t *pull)
{
    uint32_t reg;
    uint32_t pin_offset;
    pin_offset = (uint32_t)pin;
    reg = hal_readl(AON_GPIO->SPIOn_CTL + pin_offset);

    if (!(reg & AON_GPIO_SPIOn_CTL_PE_MASK))
        *pull =  SPIO_PULL_DOWN_DISABLED;
    else if (reg & AON_GPIO_SPIOn_CTL_PS_MASK)
        *pull =  SPIO_PULL_UP;
    else
        *pull =  SPIO_PULL_DOWN;
}

/**
 * @brief       set SPIO pin multiplexing function
 * @param       spio_pin_t pin，
 *              gpio_muxsel_t function_index
 * @return      none
 */
void hal_spio_pinmux_set_function(spio_pin_t pin, spio_muxsel_t function_index)
{
    uint32_t reg;
    uint32_t pin_offset;
    pin_offset = (uint32_t)pin;
    reg = hal_readl(AON_GPIO->SPIOn_CTL + pin_offset);
    reg &= ~(AON_GPIO_SPIOn_CTL_MFP_MASK);
    hal_writel(reg | function_index, AON_GPIO->SPIOn_CTL + pin_offset);
}
/**
 * @brief       get SPIO pin multiplexing function
 * @param       spio_pin_t pin，               GPIO管脚号
 *              gpio_muxsel_t *function_index   读取引脚功能
 * @return      none
 */
void hal_spio_pinmux_get_function(spio_pin_t pin, spio_muxsel_t *function_index)
{
    uint32_t reg;
    uint32_t pin_offset;
    pin_offset = (uint32_t)pin;
    reg = hal_readl(AON_GPIO->SPIOn_CTL + pin_offset);
    *function_index = reg & AON_GPIO_SPIOn_CTL_MFP_MASK;
}

void hal_spio_print_pinmux(void)
{
    spio_muxsel_t muxsel = SPIO_MUXSEL_SPIO;

    for (int i = 0; i < SPIONUM; i++)
    {
        hal_spio_pinmux_get_function(SPIO0 + i, &muxsel);
        hal_log_info("spio%d pimux is %d", SPIO0 + i, muxsel);
    }
}


/**
 * @brief       the clock of SPIO in SPV40 always on, so the function is empty
 * @param       none
 * @return      none
 */
int hal_spio_init(void)
{
    //hal_log_info("gpio init success!\n");
    return 0;
}


/**
 * @brief       none
 * @param       none
 * @return      none
 */
inline void hal_spio_interrupt_init(void)
{
}

/**
 * @brief       set a callback function for spio interrupt
 * @detail      the callback function will be called in interrupt function when spio interrupt event is triggered
 *              and if cancel callback function, call this function and set callback = NULL, param = NULL
 * @param
 *              void*                   callback    function pointer to callback function
 *              void*                   param       the param of the callback function
 * @return      none
 */
void hal_spio_interrupt_set_callback(void *callback, void *param)
{
    spio_interrput_cb.callback = (spio_callback)callback;
    spio_interrput_cb.param = param;
}

/**
 * @brief       initialize all settings of the gpio interrupt
 *              include pin、trigger event
 * @param       rt_spio_interrupt_config_type_t* spio_interrupt_struct
 * @return      none
 */
void hal_spio_interrput_config(spio_interrupt_config_type_t *spio_interrupt_struct,  void *callback, void *param)
{
    AON_GPIO->SPIO_IC &= ~(AON_GPIO_SPIO_IC_EVENT_MASK | AON_GPIO_SPIO_IC_SOURCE_MASK);
    AON_GPIO->SPIO_IC |= (spio_interrupt_struct->pin | (spio_interrupt_struct->event << 4));
    hal_spio_interrupt_set_callback(callback, param);
    hal_spio_interrupt_clear_interrupt_pending();
}

/**
 * @brief       Configure the triggering event for the interrupt channel
 * @param       gpio_interrupt_trigger_event_t  event
 * @return      none
 */
inline void hal_spio_interrupt_set_event(gpio_interrupt_trigger_event_t event)
{
    AON_GPIO->SPIO_IC &= ~AON_GPIO_SPIO_IC_EVENT_MASK;
    AON_GPIO->SPIO_IC |= (event << 4);
}

/**
 * @brief       Configures pin for spio interrupt channel
 * @param       spio_pin_t                  pin
 * @return      none
 */
inline void hal_spio_interrupt_set_pin(spio_pin_t pin)
{
    AON_GPIO->SPIO_IC &= ~AON_GPIO_SPIO_IC_SOURCE_MASK;
    AON_GPIO->SPIO_IC |= pin;
}

/**
 * @brief       enable spio interrupt
 * @param       none
 * @return      none
 *
 */
inline void hal_set_spio_interrupt_enable(void)
{
    AON_PMU->IE |= AON_PMU_PD_SPIO_INT;
}

/**
 * @brief       disable spio interrupt
 * @param
 * @return      none
 */
inline void hal_set_spio_interrupt_disable(void)
{
    AON_PMU->IE &= ~AON_PMU_PD_SPIO_INT;
}

/**
 * @brief       get spio interrput status bit
 * @param
 * @return      1   interrupt event was triggered
 *              0   interrupt event was not triggered
 */
inline uint32_t hal_spio_interrupt_get_interrupt_status(void)
{
    return (AON_PMU->PD & AON_PMU_PD_SPIO_INT) ? (SET) : (RESET);
}

/**
 * @brief       clear spio interrupt status bit
 * @param       none
 * @return      none
 */
inline void hal_spio_interrupt_clear_interrupt_pending(void)
{
    AON_PMU->PD = AON_PMU_PD_SPIO_INT;
}

void hal_spio_detach_irq(void)
{
    AON_GPIO->SPIO_IC &= ~(AON_GPIO_SPIO_IC_SOURCE_MASK | AON_GPIO_SPIO_IC_EVENT_MASK);
    hal_spio_interrupt_set_callback((void *)0, (void *)0);
}


HAL_StatusTypeDef hal_gpio_detach_irq(gpio_pin_t pin)
{
    if (pin < 0 || pin >= GPIONUM)
        return HAL_ERROR;

    uint32_t gpio_ic = -1;
    gpio_ic = hal_gpio_find_interrupt_channel_by_pin(pin);

    if (gpio_ic == -1)
        return HAL_ERROR;

    hal_gpio_clear_interrupt_control(gpio_ic);

    //a pin can attach multiple interrupt channel
    for (int i = 0 ; i < NUM_GPIO_IC - 1; i++)
    {
        gpio_ic = hal_gpio_find_interrupt_channel_by_pin(pin);

        if (gpio_ic == -1)
            return HAL_OK;

        hal_gpio_clear_interrupt_control(gpio_ic);
    }

    return HAL_OK;
}

HAL_StatusTypeDef hal_gpio_irq_enable(gpio_pin_t pin, gpio_enable_t enable)
{
    if (pin < 0 || pin >= GPIONUM)
        return HAL_ERROR;

    uint32_t gpio_ic = -1;
    gpio_ic = hal_gpio_find_interrupt_channel_by_pin(pin);

    if (gpio_ic == -1)
        return HAL_ERROR;   //The pin is not attached

    //hal_log_info("in hal_gpio_irq_enable gpio_ic = %d\n",gpio_ic);
    if (enable == GPIO_ENABLE)
        hal_set_gpio_interrupt_enable(gpio_ic);
    else
        hal_set_gpio_interrupt_disable(gpio_ic);

    return HAL_OK;
}


//gpio interrupt function
void gpio0_irq_handler(void)
{
    //
    //hal_log_info("into gpio0_irq_handler \r\n");
    for (int i = GPIO_IC0; i < NUM_GPIO_IC; i++)
    {
        if (hal_gpio_interrupt_get_interrupt_status(i) && (DEV_GPIO->IE0 & (1 << i)))
        {
            hal_gpio_interrupt_clear_interrupt_pending(i);

            if (gpio_interrput_cb[i].callback != 0)
            {
                void *param = gpio_interrput_cb[i].param;
                gpio_interrput_cb[i].callback(param);
            }

            //hal_log_info("Interrupt channel %d \r\n",i);
        }
    }
}
__WEAK void onoff_irq_handler(void)
{
    ;
}
//spio interrupt function
void pmu_irq_handler(void)
{
    if ((AON_PMU->PD & AON_PMU_PD_SPIO_INT) && (AON_PMU->IE & AON_PMU_IE_SPIO_INT))
    {
        if (spio_interrput_cb.callback != 0)
        {
            void *param = spio_interrput_cb.param;
            spio_interrput_cb.callback(param);
            //hal_log_info("get into pmu_spio interrupt \n");
        }

        hal_spio_interrupt_clear_interrupt_pending();
    }
    else
    {
        onoff_irq_handler();
    }
}

inline void gpio_init_output(uint32_t pin,uint8_t val)
{
	if(val == 0)
	{
		DEV_GPIO->CTL[pin] = 0x010;
	}
	else
	{
		DEV_GPIO->CTL[pin] = 0x210;
	}
}

inline void gpio_toggle_pin(uint32_t pin)
{
    DEV_GPIO->OUT0 ^= (1UL << pin);
}


void gpio_eclk_init(uint32_t pin)
{
	DEV_GPIO->CTL[pin] = DEV_GPIO_CTL_IE_ENABLE;
	DEV_GPIO->ECLK_f.SEL = pin;
	DEV_GPIO->ECLK_f.EN = ENABLE;
}
