#include "gw3323_gpio.h"

/**
  * @brief  Initializes the gpiox peripheral according to the specified
  *         parameters in the gpio_init_struct.
  * @param  gpiox: where x can be (A.B.E.F.G) to select the GPIO peripheral.
  * @param  gpio_init_struct: pointer to a gpio_init_typedef structure that
  *         contains the configuration information for the specified GPIO peripheral.
  * @retval None
  */
void gpio_init(gpio_typedef* gpiox, gpio_init_typedef* gpio_init_struct)
{
    uint32_t pin_offset = 0x00, offset = 0x00;
    uint8_t i = 0x00;

    /*--- Configure the port pins ---*/
    for (pin_offset = 0x00; pin_offset < 0x08; pin_offset++) {
        offset = ((uint32_t)0x01) << pin_offset;
        if (gpio_init_struct->gpio_pin & offset) {
            /*--- Input or Output Configuration ---*/
            gpiox->dir &= ~(((uint32_t)0x01) << pin_offset);
            gpiox->dir |= ((uint32_t)(gpio_init_struct->gpio_dir) << pin_offset);

            /*--- Digital or Analog Configuration ---*/
            gpiox->de &= ~(((uint32_t)0x01) << pin_offset);
            gpiox->de |= ((uint32_t)(gpio_init_struct->gpio_mode) << pin_offset);

            /*--- Function Mapping Enable Configuration ---*/
            gpiox->fen &= ~(((uint32_t)0x01) << pin_offset);
            gpiox->fen |= ((uint32_t)(gpio_init_struct->gpio_fen) << pin_offset);

            /*--- Pull Up or Pull Down Configuration ---*/
            if (gpio_init_struct->gpio_dir == GPIO_DIR_INPUT) {
                for (i = 0x00; i < 0x06; i++) {
                    gpiox->pupd[i] &= ~(((uint32_t)0x01) << pin_offset);
                    if (i == gpio_init_struct->gpio_pupd) {
                        gpiox->pupd[i] |= (((uint32_t)0x01) << pin_offset);
                    }
                }
            /*--- Output driving Configuration ---*/
            } else if (gpio_init_struct->gpio_mode == GPIO_MODE_DIGITAL) {
                gpiox->drv &= ~(((uint32_t)0x01) << pin_offset);
                gpiox->drv |= ((uint32_t)(gpio_init_struct->gpio_drv) << pin_offset);
            }

            /*--- Disable TK channel Configuration ---*/
            if (gpiox == GPIOB_REG && offset == GPIO_PIN_5) {
                RTCCON0 |= (1 << 17) | (1 << 4);
                TKACON0 &= ~((1 << 23) | (1 << 11));
                TKACON1 &= ~((1 << 23) | (1 << 11));
            }
        }
    }
}

/**
  * @brief  Sets the selected data port bits.
  * @param  gpiox: where x can be (A.B.E.F.G) to select the GPIO peripheral.
  * @param  gpio_pin: specifies the port bits to be written. This parameter
  *         can be any combination of GPIO_PIN_x where x can be (0..7).
  * @retval None
  */
AT(.com_text.gpio)
void gpio_set_bits(gpio_typedef* gpiox, uint8_t gpio_pin)
{
    gpiox->io_set = gpio_pin;
}

/**
  * @brief  Clears the selected data port bits.
  * @param  gpiox: where x can be (A.B.E.F.G) to select the GPIO peripheral.
  * @param  gpio_pin: specifies the port bits to be written.
  *         This parameter can be any combination of GPIO_PIN_x where x can be (0..7).
  * @retval None
  */
AT(.com_text.gpio)
void gpio_reset_bits(gpio_typedef* gpiox, uint8_t gpio_pin)
{
    gpiox->io_clr = gpio_pin;
}

/**
  * @brief  Set the GPIO to high or low
  * @param  gpiox: where x can be (A.B.E.F.G) to select the GPIO peripheral.
  * @param  gpio_pin: specifies the port bits to be written.
  *         This parameter can be any combination of GPIO_PIN_x where x can be (0..7).
  * @param  BitVal: 0 or l.
  * @retval None
  */
AT(.com_text.gpio)
void gpio_writebit(gpio_typedef* gpiox, uint8_t gpio_pin, FLAG_STATE BitVal)
{
  if (BitVal != RESET){
    gpiox->io_set = gpio_pin;
  }
  else{
    gpiox->io_clr = gpio_pin;
  }
}

/**
  * @brief  Writes the specified GPIO output data port.
  * @param  gpiox: where x can be (A.B.E.F.G) to select the GPIO peripheral.
  * @param  port_val: specifies the port bits to be written. This parameter
  *         can be any combination of GPIO_PIN_x where x can be (0..7).
  * @retval None
  */
AT(.com_text.gpio)
void gpio_write_data(gpio_typedef *gpiox, uint8_t port_val)
{
    gpiox->io_set = port_val;
    gpiox->io_clr = ~port_val;
}

/**
  * @brief  Toggles the selected output data port bits.
  * @param  gpiox: where x can be (A.B.E.F.G) to select the GPIO peripheral.
  * @param  gpio_pin: specifies the port bits to be written. This parameter
  *         can be any combination of GPIO_PIN_x where x can be (0..7).
  * @retval None
  */
AT(.com_text.gpio)
void gpio_toggle_bits(gpio_typedef *gpiox, uint8_t gpio_pin)
{
    gpiox->dr ^= gpio_pin;
}

/**
  * @brief  Reads the specified GPIO input data port.
  * @param  gpiox: where x can be (A.B.E.F.G) to select the GPIO peripheral.
  * @retval GPIO output data port value.
  */
AT(.com_text.gpio)
uint8_t gpio_read_data(gpio_typedef* gpiox)
{
    return ((uint8_t)gpiox->dr);
}

/**
  * @brief  Reads the specified input port pin.
  * @param  gpiox: where x can be (A.B.E.F.G) to select the GPIO peripheral.
  * @param  gpio_pin:  specifies the port bit to read.
  *   This parameter can be gpio_pin where x can be (0..7).
  * @retval The input port pin value.
  */
AT(.com_text.gpio)
uint8_t gpio_read_bit(gpio_typedef* gpiox, uint8_t gpio_pin)
{
    uint8_t bit_status;

    if ((gpiox->dr & gpio_pin) != (uint8_t)RESET) {
        bit_status = (uint8_t)SET;
    } else {
        bit_status = (uint8_t)RESET;
    }

    return bit_status;
}

/**
  * @brief  Configure the mapping between peripherals and registers.
  * @param  gpio_mapping: the mapping relationship code. see the file gw3323_gpio.h
  * @retval The input port pin value.
  */
void gpio_func_mapping(uint16_t gpio_mapping)
{
    uint8_t funcmcon_offset, register_inner_offset, map_code;

    /*--- Configure FUNCMCONx ---*/
    funcmcon_offset = (uint16_t)((gpio_mapping & FUNCMCONx_MASK_REG) >> 12);
    register_inner_offset = (uint16_t)((gpio_mapping & FUNCMCONx_MASK_INNER) >> 4);
    map_code = (uint16_t)((gpio_mapping & FUNCMCONx_MASK_MAPPING) >> 0);

    if (funcmcon_offset < 3) {          //Configure the FUNMCON(0..2)
        *(&FUNCMCON0 + funcmcon_offset) |= (uint32_t)(0x0f << register_inner_offset);
        *(&FUNCMCON0 + funcmcon_offset) |= (uint32_t)(map_code << register_inner_offset);
    } else if (funcmcon_offset == 3) {  //Configure the FUNMCON3
        FUNCMCON3 |= (uint32_t)(0x0f << register_inner_offset);
        FUNCMCON3 |= (uint32_t)(map_code << register_inner_offset);
    }
}

/**
  * @brief  External interrupt configuration reset,You only need to call it once
  * @param  Reset external interrupts when initialized
  * @retval To initialize multiple external interrupts, simply call them at the very beginning of the interrupt initialization
  */
void gpio_pic_config_reset(void)
{
    WKUPEDG = 0;
    WKUPCON = 0;
}

/**
  * @brief  External interrupt configuration for GPIO
  */
void gpio_pic_config(gpio_interrupt_typedef* gpio_init_struct, isr_t isr, int pr, FUNCTIONAL_STATE state)
{
    if (gpio_init_struct->extitrigger) {
        WKUPEDG |= (1 << gpio_init_struct->gpio_exti_line);
    } else {
        WKUPEDG &= ~(1 << gpio_init_struct->gpio_exti_line);
    }
    WKUPCON |= (1 << gpio_init_struct->gpio_exti_line);
    WKUPCPND = 0xff << 16;                  // clear interrupt flag
    if(gpio_init_struct->gpio_exti_line != gpioa7 &&
       gpio_init_struct->gpio_exti_line != gpiob1 &&
       gpio_init_struct->gpio_exti_line != gpiob2 &&
       gpio_init_struct->gpio_exti_line != gpiob3 &&
       gpio_init_struct->gpio_exti_line != gpiob4 &&
       gpio_init_struct->gpio_exti_line != gpiob5)
    {
        PORTINTEN |= gpio_init_struct->gpio_other_exti_line;
        PORTINTEDG |= gpio_init_struct->gpio_other_exti_line;
    }
    sys_irq_init(PORT_IRQn,pr,isr); // regoster interrupt handle
    WKUPCON |= (1 << 16);
}


/**
  * @brief  Get the external interrupt bit of the GPIO
  */
AT(.com_text.isr)
FLAG_STATE gpio_get_flag(u32 exti_line)
{
    return (WKUPEDG & (1 << (16+exti_line)));
}

/**
  * @brief  Clear the external interrupt bit of the GPIO
  */
AT(.com_text.isr)
void gpio_clear_flag(u32 exti_line)
{
    return (WKUPCPND |= (1 << ((16+exti_line))));
}
