/*
 * Copyright 2020-2022 Yuntu Microelectronics Co., Ltd.
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

/*!
 * @file pins_port_hw_access.c
 * @version 1.4.1
 *
 * @brief PINS PORT Hardware Access Layer — non-inline PCTRL helpers.
 *
 * This file contains the PCTRL access routines that are implemented in source
 * form rather than as header inlines.
 */

/*!
 * @page misra_violations MISRA-C:2012 violations list
 *
 * PRQA S 2985 Rule 2.2 : This operation is redundant. The value of the result is
 *                        always that of the left-hand operand.
 */

#include "pins_port_hw_access.h"
#include "pins_gpio_hw_access.h"

#if FEATURE_SOC_PORT_COUNT > 0

/*******************************************************************************
 * Pin Initialization
 ******************************************************************************/

/*!
 * @brief Apply one pin configuration record to the hardware registers.
 */
void PINS_Init(const pin_settings_config_t * config)
{
    DEV_ASSERT(config->base != NULL);
    DEV_ASSERT((PCTRL_MUX_AS_GPIO != config->mux) || (config->gpioBase != NULL));
    DEV_ASSERT(config->pinPortIdx < PCTRL_PCR_COUNT);

    uint32_t regValue = config->base->PCR[config->pinPortIdx];
    uint32_t directions;

#if FEATURE_PINS_HAS_PULL_SELECTION
    switch (config->pullConfig)
    {
        case PCTRL_INTERNAL_PULL_NOT_ENABLED:
            {
                regValue &= ~(PCTRL_PCR_PE_MASK);
            }
            break;
        case PCTRL_INTERNAL_PULL_DOWN_ENABLED:
            {
                regValue &= ~(PCTRL_PCR_PS_MASK);
                regValue |= PCTRL_PCR_PE(1U);
            }
            break;
        case PCTRL_INTERNAL_PULL_UP_ENABLED:
            {
                regValue |= PCTRL_PCR_PE(1U);
                regValue |= PCTRL_PCR_PS(1U);
            }
            break;
        default:
            /* invalid command */
            DEV_ASSERT(false);
            break;
    }
#endif /* FEATURE_PINS_HAS_PULL_SELECTION */
#if FEATURE_PINS_HAS_SLEW_RATE
    regValue &= ~(PCTRL_PCR_SRE_MASK);
    regValue |= PCTRL_PCR_SRE(config->rateSelect);
#endif
#if FEATURE_PINS_HAS_PASSIVE_FILTER
    regValue &= ~(PCTRL_PCR_PFE_MASK);
    regValue |= PCTRL_PCR_PFE(config->passiveFilter ? 1U : 0U);  /*PRQA S 2985*/
#endif
#if FEATURE_PINS_HAS_OPEN_DRAIN
    regValue &= ~(PCTRL_PCR_ODE_MASK);
    regValue |= PCTRL_PCR_ODE(config->openDrain);
#endif
#if FEATURE_PINS_HAS_DRIVE_STRENGTH
    regValue &= ~(PCTRL_PCR_DSE_MASK);
    regValue |= PCTRL_PCR_DSE(config->driveSelect);
#endif
    regValue &= ~(PCTRL_PCR_MUX_MASK);
    regValue |= PCTRL_PCR_MUX(config->mux);

    config->base->PCR[config->pinPortIdx] = regValue;

#if FEATURE_PINS_HAS_DIGITAL_FILTER
    if (config->digitalFilter)
    {
        PINS_ConfigDigitalFilter(config->gpioBase, &config->filterConfig, config->pinPortIdx);
        config->gpioBase->PCR[config->pinPortIdx] |= GPIO_PCR_DFE_MASK;
    }
    else
    {
        config->gpioBase->PCR[config->pinPortIdx] &= ~GPIO_PCR_DFE_MASK;
    }
#endif /* FEATURE_PINS_HAS_DIGITAL_FILTER */
#if defined(FEATURE_PCTRL_HAS_DIGITAL_FILTER) && (FEATURE_PCTRL_HAS_DIGITAL_FILTER == 1)
    if (config->digitalFilter)
    {
        PINS_ConfigDigitalFilter(config->base, &config->filterConfig, config->pinPortIdx);
        config->base->PCR[config->pinPortIdx] |= PCTRL_PCR_DFE_MASK;
    }
    else
    {
        config->base->PCR[config->pinPortIdx] &= ~PCTRL_PCR_DFE_MASK;
    }
#endif /* FEATURE_PCTRL_HAS_DIGITAL_FILTER */

    /* If gpioBase address not null setup the direction of pin */
    if (PCTRL_MUX_AS_GPIO == config->mux)
    {
        /* Read current direction */
        directions = (uint32_t)(config->gpioBase->POER);

        switch (config->direction)
        {
            case GPIO_INPUT_DIRECTION:
                directions &= ~(1UL << config->pinPortIdx);
                break;
            case GPIO_OUTPUT_DIRECTION:
                directions |= (1UL << config->pinPortIdx);
                break;
            case GPIO_UNSPECIFIED_DIRECTION:
            /* pass-through */
            default:
                /* nothing to configure */
                DEV_ASSERT(false);
                break;
        }

        /* Configure initial value for output */
        if (config->direction == GPIO_OUTPUT_DIRECTION)
        {
            PINS_GPIO_WritePin(config->gpioBase, config->pinPortIdx, config->initValue);
        }

        /* Configure direction */
        config->gpioBase->POER = GPIO_POER_POE(directions);  /*PRQA S 2985*/
    }

    /* config irq register */
    config->gpioBase->PCR[config->pinPortIdx] &= ~(GPIO_PCR_IRQC_MASK);
    config->gpioBase->PCR[config->pinPortIdx] |= GPIO_PCR_IRQC(config->intConfig);  /*PRQA S 2985*/

    if (config->clearIntFlag)
    {
        config->gpioBase->PIFR = ((uint32_t)0x01) << config->pinPortIdx;
    }

#if FEATURE_PCTRL_HAS_PIN_CONTROL_LOCK
    config->base->PCR[config->pinPortIdx] &= ~(PCTRL_PCR_LOCK_MASK);
    config->base->PCR[config->pinPortIdx] |= PCTRL_PCR_LOCK(config->pinLock ? 1U : 0U);  /*PRQA S 2985*/
#endif
}

/*******************************************************************************
 * Port Control
 ******************************************************************************/

/*!
 * @brief Select the mux mode for a pin.
 */
void PINS_SetMuxModeSel(PCTRL_Type * const base,
                        uint32_t pin,
                        port_mux_t mux)
{
    DEV_ASSERT(pin < PCTRL_PCR_COUNT);

    uint32_t regValue = base->PCR[pin];

    regValue &= ~(PCTRL_PCR_MUX_MASK);
    regValue |= PCTRL_PCR_MUX(mux);
    base->PCR[pin] = regValue;
}

#endif /* FEATURE_SOC_PCTRL_COUNT */
/*******************************************************************************
 * EOF
 ******************************************************************************/
