/*!
 * @file        apm32f10x_gpio.c
 *
 * @brief       This file provides all the GPIO firmware functions
 *
 * @version     V1.0.3
 *
 * @date        2022-07-25
 *
 * @attention
 *
 *  Copyright (C) 2022 Geehy Semiconductor
 *
 *  You may not use this file except in compliance with the
 *  GEEHY COPYRIGHT NOTICE (GEEHY SOFTWARE PACKAGE LICENSE).
 *
 *  The program is only for reference, which is distributed in the hope
 *  that it will be useful and instructional for customers to develop
 *  their software. Unless required by applicable law or agreed to in
 *  writing, the program is distributed on an "AS IS" BASIS, WITHOUT
 *  ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the GEEHY SOFTWARE PACKAGE LICENSE for the governing permissions
 *  and limitations under the License.
 */

#include "apm32f10x_gpio.h"
#include "apm32f10x_rcm.h"

/** @addtogroup APM32F10x_StdPeriphDriver
  @{
*/

/** @defgroup GPIO_Driver
  * @brief GPIO driver modules
  @{
*/

/** @defgroup GPIO_Fuctions
  @{
*/

/*!
 * @brief     Reset GPIO peripheral registers to their default reset values
 *
 * @param     port: Select the GPIO port.
 *                  This parameter can be one of GPIOx( x can be from A to G).
 *
 * @retval    None
 */
void GPIO_Reset(GPIO_T* port)
{
    RCM_APB2_PERIPH_T APB2Periph;

    if (port == GPIOA)
    {
        APB2Periph = RCM_APB2_PERIPH_GPIOA;
    }
    else if (port == GPIOB)
    {
        APB2Periph = RCM_APB2_PERIPH_GPIOB;
    }
    else if (port == GPIOC)
    {
        APB2Periph = RCM_APB2_PERIPH_GPIOC;
    }
    else if (port == GPIOD)
    {
        APB2Periph = RCM_APB2_PERIPH_GPIOD;
    }
    else if (port == GPIOE)
    {
        APB2Periph = RCM_APB2_PERIPH_GPIOE;
    }
    else if (port == GPIOF)
    {
        APB2Periph = RCM_APB2_PERIPH_GPIOF;
    }
    else if (port == GPIOG)
    {
        APB2Periph = RCM_APB2_PERIPH_GPIOG;
    }

    RCM_EnableAPB2PeriphReset(APB2Periph);
    RCM_DisableAPB2PeriphReset(APB2Periph);
}

/*!
 * @brief     Reset Alternate Functions registers to their default reset values
 *
 * @param     None
 *
 * @retval    None
 */
void GPIO_AFIOReset(void)
{
    RCM_EnableAPB2PeriphReset(RCM_APB2_PERIPH_AFIO);
    RCM_DisableAPB2PeriphReset(RCM_APB2_PERIPH_AFIO);
}

/*!
 * @brief     Config the GPIO peripheral according to the specified parameters in the gpioConfig
 *
 * @param     port: Select the GPIO port.
 *                  This parameter can be one of GPIOx( x can be from A to G).
 *
 * @param     gpioConfig: pointer to a GPIO_Config_T structure
 *
 * @retval    None
 */
void GPIO_Config(GPIO_T* port, GPIO_Config_T* gpioConfig)
{
    uint8_t i;
    uint32_t mode;
    uint32_t CR;
    uint32_t temp;
    uint32_t shift;

    mode = gpioConfig->mode & 0x0f;

    if (gpioConfig->mode & 0x80)
    {
        mode |= gpioConfig->speed;
    }

    if (gpioConfig->pin & 0xff)
    {
        CR = port->CFGLOW;

        for (i = 0, shift = 0x01; i < 8; i++, shift <<= 1)
        {
            if (gpioConfig->pin & shift)
            {
                temp = i << 2;
                CR &= (uint32_t)~(0x0f << temp);
                CR |= mode << temp;

                if (gpioConfig->mode == GPIO_MODE_IN_PD)
                {
                    port->BC = shift;
                }
                else if (gpioConfig->mode == GPIO_MODE_IN_PU)
                {
                    port->BSC = shift;
                }
            }
        }

        port->CFGLOW = CR;
    }

    if (gpioConfig->pin & 0xff00)
    {
        CR = port->CFGHIG;

        for (i = 8, shift = 0x100; i < 16; i++, shift <<= 1)
        {
            if (gpioConfig->pin & shift)
            {
                temp = (i - 8) << 2;
                CR &= (uint32_t)~(0x0f << temp);
                CR |= mode << temp;

                if (gpioConfig->mode == GPIO_MODE_IN_PD)
                {
                    port->BC = shift;
                }
                else if (gpioConfig->mode == GPIO_MODE_IN_PU)
                {
                    port->BSC = shift;
                }
            }
        }

        port->CFGHIG = CR;
    }
}

/*!
 * @brief     Fills each gpioConfig member with its default value.
 *
 * @param     gpioConfig : pointer to a GPIO_Config_T structure which will be initialized.
 *
 * @retval    None
 */
void GPIO_ConfigStructInit(GPIO_Config_T* gpioConfig)
{
    gpioConfig->pin  = GPIO_PIN_ALL;
    gpioConfig->speed = GPIO_SPEED_2MHz;
    gpioConfig->mode = GPIO_MODE_IN_FLOATING;
}

/*!
 * @brief     Reads the specified input port pin
 *
 * @param     port: Select the GPIO port.
 *                  This parameter can be one of GPIOx( x can be from A to G).
 *
 * @param     pin : specifies pin to read.
 *                  This parameter can be one of GPIO_PIN_x( x can be from 0 to 15).
 *
 * @retval    The input port pin value
 */
uint8_t GPIO_ReadInputBit(GPIO_T* port, uint16_t pin)
{
    uint8_t ret;

    ret = (port->IDATA & pin) ?  BIT_SET : BIT_RESET;

    return ret;
}

/*!
 * @brief     Reads the specified GPIO input data port
 *
 * @param     port: Select the GPIO port.
 *                  This parameter can be one of GPIOx( x can be from A to G).
 *
 * @retval    GPIO input data port value
 */
uint16_t GPIO_ReadInputPort(GPIO_T* port)
{
    return ((uint16_t)port->IDATA);
}

/*!
 * @brief     Reads the specified output data port bit
 *
 * @param     port: Select the GPIO port.
 *                  This parameter can be one of GPIOx( x can be from A to G).
 *
 * @param     pin : specifies pin to read.
 *                  This parameter can be one of GPIO_PIN_x( x can be from 0 to 15).
 *
 * @retval    The output port pin value
 */
uint8_t GPIO_ReadOutputBit(GPIO_T* port, uint16_t pin)
{

    uint8_t ret;

    ret = (port->ODATA & pin) ? BIT_SET : BIT_RESET;

    return ret;
}

/*!
 * @brief     Reads the specified GPIO output data port
 *
 * @param     port: Select the GPIO port.
 *                  This parameter can be one of GPIOx( x can be from A to G).
 *
 * @retval    output data port value
 */
uint16_t GPIO_ReadOutputPort(GPIO_T* port)
{
    return ((uint16_t)port->ODATA);
}

/*!
 * @brief     Sets the selected data port bits
 *
 * @param     port: Select the GPIO port.
 *                  This parameter can be one of GPIOx( x can be from A to G).
 *
 * @param     pin : specifies pin to be written.
 *                  This parameter can be any combination of GPIO_PIN_x( x can be from 0 to 15).
 *
 * @retval    None
 */
void GPIO_SetBit(GPIO_T* port, uint16_t pin)
{
    port->BSC = (uint32_t)pin;
}

/*!
 * @brief     Clears the selected data port bits
 *
 * @param     port: Select the GPIO port.
 *                  This parameter can be one of GPIOx( x can be from A to G).
 *
 * @param     pin : specifies pin to be cleared.
 *                  This parameter can be any combination of GPIO_PIN_x( x can be from 0 to 15).
 *
 * @retval    None
 */
void GPIO_ResetBit(GPIO_T* port, uint16_t pin)
{
    port->BC = (uint32_t)pin;
}

/*!
 * @brief     Writes data to the specified GPIO data port bit
 *
 * @param     port: Select the GPIO port.
 *                  This parameter can be one of GPIOx( x can be from A to G).
 *
 * @param     pin : Select specifies pin.
 *                  This parameter can be one of GPIO_PIN_x( x can be from 0 to 15).
 *
 *
 * @param     bitVal : specifies the value to be written to the port output data register
 *                     This parameter can be one of the following values:
 *                       @arg BIT_RESET: Reset the port pin
 *                       @arg BIT_SET  : Set the port pin
 *
 * @retval    None
 */
void GPIO_WriteBitValue(GPIO_T* port, uint16_t pin, uint8_t bitVal)
{
    if (bitVal != BIT_RESET)
    {
        port->BSC = pin;
    }
    else
    {
        port->BC = pin ;
    }
}

/*!
 * @brief     Writes data to the specified GPIO data port
 *
 * @param     port: Select the GPIO port.
 *                  This parameter can be one of GPIOx( x can be from A to G).
 *
 * @param     portValue : specifies the value to be written to the port output data register.
 *
 * @retval    None
 */
void GPIO_WriteOutputPort(GPIO_T* port, uint16_t portValue)
{
    port->ODATA = (uint32_t)portValue;
}

/*!
 * @brief     Locks GPIO Pins configuration registers
 *
 * @param     port: Select the GPIO port.
 *                  This parameter can be one of GPIOx( x can be from A to G).
 *
 * @param     pin : Select specifies pin.
 *                  This parameter can be any combination of GPIO_PIN_x( x can be from 0 to 15).
 *
 * @retval    None
 */
void GPIO_ConfigPinLock(GPIO_T* port, uint16_t pin)
{
    uint32_t val = 0x00010000;

    val  |= pin;
    /* Set LCKK bit */
    port->LOCK = val ;
    /* Reset LCKK bit */
    port->LOCK =  pin;
    /* Set LCKK bit */
    port->LOCK = val;
    /* Read LCKK bit*/
    val = port->LOCK;
    /* Read LCKK bit*/
    val = port->LOCK;
}

/*!
 * @brief     Selects the GPIO pin used as Event output
 *
 * @param     portSource : selects the GPIO port to be used as source for Event output.
 *                         This parameter can be one of GPIO_PORT_SOURCE_x( x can be from A to E).
 *
 * @param     pinSource   specifies the pin for the Event output
 *                  This parameter can be GPIO_PIN_SOURCE_x( x can be from 0 to 15).
 *
 * @retval    None
 */
void GPIO_ConfigEventOutput(GPIO_PORT_SOURCE_T portSource, GPIO_PIN_SOURCE_T pinSource)
{
    AFIO->EVCTRL_B.PORTSEL =  portSource;
    AFIO->EVCTRL_B.PINSEL = pinSource;
}

/*!
 * @brief     Enables the Event Output
 *
 * @param     None
 *
 * @retval    None
 */
void GPIO_EnableEventOutput(void)
{
    AFIO->EVCTRL_B.EVOEN = BIT_SET;
}

/*!
 * @brief     Disable the Event Output
 *
 * @param     None
 *
 * @retval    None
 */
void GPIO_DisableEventOutput(void)
{
    AFIO->EVCTRL_B.EVOEN = BIT_RESET;
}

/*!
 * @brief     Changes the mapping of the specified pin
 *
 * @param     remap : selects the pin to remap
 *                    This parameter can be one of the following values:
 *                    @arg GPIO_NO_REMAP_SPI1         : No SPI1 Alternate Function mapping
 *                    @arg GPIO_REMAP_SPI1            : SPI1 Alternate Function mapping
 *                    @arg GPIO_NO_REMAP_I2C1         : No I2C1 Alternate Function mapping
 *                    @arg GPIO_REMAP_I2C1            : I2C1 Alternate Function mapping
 *                    @arg GPIO_NO_REMAP_USART1       : No USART1 Alternate Function mapping
 *                    @arg GPIO_REMAP_USART1          : USART1 Alternate Function mapping
 *                    @arg GPIO_NO_REMAP_USART2       : No USART2 Alternate Function mapping
 *                    @arg GPIO_REMAP_USART2          : USART2 Alternate Function mapping
 *                    @arg GPIO_NO_REMAP_USART3       : No USART3 Partial Alternate Function mapping
 *                    @arg GPIO_PARTIAL_REMAP_USART3  : USART3 Partial Alternate Function mapping
 *                    @arg GPIO_FULL_REMAP_USART3     : USART3 Full Alternate Function mapping
 *                    @arg GPIO_NO_REMAP_TMR1         : No TIM1 Partial Alternate Function mapping
 *                    @arg GPIO_PARTIAL_REMAP_TMR1    : TIM1 Partial Alternate Function mapping
 *                    @arg GPIO_FULL_REMAP_TMR1       : TIM1 Full Alternate Function mapping
 *                    @arg GPIO_NO_REMAP1_TMR2        : No TIM2 Partial1 Alternate Function mapping
 *                    @arg GPIO_PARTIAL_REMAP1_TMR2   : TIM2 Partial1 Alternate Function mapping
 *                    @arg GPIO_PARTIAL_REMAP2_TMR2   : TIM2 Partial2 Alternate Function mapping
 *                    @arg GPIO_FULL_REMAP_TMR2       : TIM2 Full Alternate Function mapping
 *                    @arg GPIO_NO_REMAP_TMR3         : No TIM3 Partial Alternate Function mapping
 *                    @arg GPIO_PARTIAL_REMAP_TMR3    : TIM3 Partial Alternate Function mapping
 *                    @arg GPIO_FULL_REMAP_TMR3       : TIM3 Full Alternate Function mapping
 *                    @arg GPIO_NO_REMAP_TMR4         : No TIM4 Alternate Function mapping
 *                    @arg GPIO_REMAP_TMR4            : TIM4 Alternate Function mapping
 *                    @arg GPIO_NO_REMAP_CAN1         : No CAN1 Alternate Function mapping
 *                    @arg GPIO_REMAP1_CAN1           : CAN1 Alternate Function mapping
 *                    @arg GPIO_REMAP2_CAN1           : CAN1 Alternate Function mapping
 *                    @arg GPIO_NO_REMAP_PD01         : No PD01 Alternate Function mapping
 *                    @arg GPIO_REMAP_PD01            : PD01 Alternate Function mapping
 *                    @arg GPIO_NO_REMAP_TMR5CH4_LSI  : No LSI connected to TIM5 Channel4 input capture for calibration
 *                    @arg GPIO_REMAP_TMR5CH4_LSI     : LSI connected to TIM5 Channel4 input capture for calibration
 *                  Only For APM32F10X_CL devices(APM32F107xx and APM32F105xx):
 *                    @arg GPIO_NO_REMAP_ETH_MAC      : No Ethernet MAC Alternate remapping
 *                    @arg GPIO_REMAP_ETH_MAC         : Ethernet MAC Alternate remapping
 *                    @arg GPIO_NO_REMAP_CAN2         : No CAN2 Alternate Function mapping
 *                    @arg GPIO_REMAP_CAN2            : CAN2 Alternate Function mapping
 *                    @arg GPIO_REMAP_MACEISEL_MII    : Ethernet MAC External Interface Select MII Interface
 *                    @arg GPIO_REMAP_MACEISEL_RMII   : Ethernet MAC External Interface Select RMII Interface
 *                    @arg GPIO_NO_REMAP_SPI3         : No SPI3 Alternate Function mapping
 *                    @arg GPIO_REMAP_SPI3            : SPI3 Alternate Function mapping
 *                    @arg GPIO_NO_REMAP_SWJ          : Full SWJ Enabled (JTAG-DP + SW-DP)
 *                    @arg GPIO_REMAP_SWJ_NOJTRST     : Full SWJ Enabled (JTAG-DP + SW-DP) but without JTRST
 *                    @arg GPIO_REMAP_SWJ_JTAGDISABLE : JTAG-DP Disabled and SW-DP Enabled
 *                    @arg GPIO_REMAP_SWJ_DISABLE     : Full SWJ Disabled (JTAG-DP + SW-DP)
 *                    @arg GPIO_NO_REMAP_TMR2ITR1     : No TMR2 ITR1 Alternate Function mapping
 *                    @arg GPIO_REMAP_TMR2ITR1        : TMR2 ITR1 Alternate Function mapping
 *                    @arg GPIO_NO_REMAP_PTP_PPS      : No Ethernet MAC PTP_PPS Alternate Function mapping
 *                    @arg GPIO_REMAP_PTP_PPS         : Ethernet MAC PTP_PPS Alternate Function mapping
 *                  For Other APM32F10X_HD/MD/LD devices:
 *                    @arg GPIO_NO_REMAP_ADC1_ETRGINJ : No ADC1 External Trigger Injected Conversion remapping
 *                    @arg GPIO_REMAP_ADC1_ETRGINJ    : ADC1 External Trigger Injected Conversion remapping
 *                    @arg GPIO_NO_REMAP_ADC1_ETRGREG : No ADC1 External Trigger Regular Conversion remapping
 *                    @arg GPIO_REMAP_ADC1_ETRGREG    : ADC1 External Trigger Regular Conversion remapping
 *                    @arg GPIO_NO_REMAP_ADC2_ETRGINJ : No ADC2 External Trigger Injected Conversion remapping
 *                    @arg GPIO_REMAP_ADC2_ETRGINJ    : ADC2 External Trigger Injected Conversion remapping
 *                    @arg GPIO_NO_REMAP_ADC2_ETRGREG : No ADC2 External Trigger Regular Conversion remapping
 *                    @arg GPIO_REMAP_ADC2_ETRGREG    : ADC2 External Trigger Regular Conversion remapping
 *                    @arg GPIO_NO_REMAP_CAN2         : No CAN2 Alternate Function mapping
 *                    @arg GPIO_REMAP_CAN2            : CAN2 Alternate Function mapping
 *                    @arg GPIO_NO_REMAP_SWJ          : Full SWJ Enabled (JTAG-DP + SW-DP)
 *                    @arg GPIO_REMAP_SWJ_NOJTRST     : Full SWJ Enabled (JTAG-DP + SW-DP) but without JTRST
 *                    @arg GPIO_REMAP_SWJ_JTAGDISABLE : JTAG-DP Disabled and SW-DP Enabled
 *                    @arg GPIO_REMAP_SWJ_DISABLE     : Full SWJ Disabled (JTAG-DP + SW-DP)
 *
 * @retval    When you use GPIO_REMAP_CAN2, you must put this function last of all other ConfigPinRemap Function.
 */
void GPIO_ConfigPinRemap(GPIO_REMAP_T remap)
{
    uint32_t val, mask, bitOffset, regOffset;
    uint32_t regVal;

    val = remap & 0x0f;
    mask = (remap >> 4) & 0x0f;
    bitOffset = (remap >> 8) & 0xff;
    regOffset = (remap >> 16) & 0x0f;

    if (regOffset)
    {
        regVal = AFIO->REMAP2;
    }
    else
    {
        regVal = AFIO->REMAP1;
    }

    if(remap >> 8 == 0x18)
    {
        regVal &= 0xF0FFFFFF;
        AFIO->REMAP1 &= 0xF0FFFFFF;
    }
    else
    {
        regVal |= 0x0F000000;
    }

    mask <<= bitOffset;
    regVal &= (uint32_t)~mask;
    val <<= bitOffset;
    regVal |= val;

    if (regOffset)
    {
        AFIO->REMAP2 = regVal;
    }
    else
    {
        AFIO->REMAP1 = regVal;
    }
}

/*!
 * @brief     Selects the GPIO pin used as EINT Line
 *
 * @param     portSource : selects the GPIO port to be used as source for EINT line.
 *                         This parameter can be one of GPIO_PORT_SOURCE_x( x can be from A to G).
 *
 * @param     pinSource : Specifies the EINT line to be configured.
 *                        This parameter can be GPIO_PIN_SOURCE_x( x can be from 0 to 15).
 *
 * @retval    None
 */
void GPIO_ConfigEINTLine(GPIO_PORT_SOURCE_T portSource, GPIO_PIN_SOURCE_T pinSource)
{
    uint32_t shift;

    if (pinSource <= GPIO_PIN_SOURCE_3)
    {
        shift = pinSource << 2;
        AFIO->EINTSEL1 &= (uint32_t )~(0x0f << shift);
        AFIO->EINTSEL1 |=  portSource << shift;
    }

    else if (pinSource <= GPIO_PIN_SOURCE_7)
    {
        shift = (pinSource - GPIO_PIN_SOURCE_4) << 2;
        AFIO->EINTSEL2 &= (uint32_t )~(0x0f << shift);
        AFIO->EINTSEL2 |=  portSource << shift;
    }

    else if (pinSource <= GPIO_PIN_SOURCE_11)
    {
        shift = (pinSource - GPIO_PIN_SOURCE_8) << 2;
        AFIO->EINTSEL3 &= (uint32_t )~(0x0f << shift);
        AFIO->EINTSEL3 |=  portSource << shift;
    }

    else if (pinSource <= GPIO_PIN_SOURCE_15)
    {
        shift = (pinSource - GPIO_PIN_SOURCE_12) << 2;
        AFIO->EINTSEL4 &= (uint32_t )~(0x0f << shift);
        AFIO->EINTSEL4 |=  portSource << shift;
    }
}

/**@} end of group GPIO_Fuctions*/
/**@} end of group GPIO_Driver*/
/**@} end of group APM32F10x_StdPeriphDriver*/
