/* =========================================================================
 *  This code (or certain portions of it) is released for use under license
 *  from Imagination Technologies Limited.
 *  Copyright (c) 2019 Imagination Technologies Limited
 *  Copyright 2021-2022 NXP
 *
 *  NXP Confidential. This software is owned or controlled by NXP and may only
 *  be used strictly in accordance with the applicable license terms. By
 *  expressly accepting such terms or by downloading, installing, activating
 *  and/or otherwise using the software, you are agreeing that you have read,
 *  and that you agree to comply with and are bound by, such license terms. If
 *  you do not agree to be bound by the applicable license terms, then you may
 *  not retain, install, activate or otherwise use the software.
 *  <<< PFE Restricted Software >>>
 *
 * ========================================================================= */

/*==================================================================================================
*                              SOURCE FILE VERSION INFORMATION
==================================================================================================*/
#define PFE_SRC_VERSION_CHECK 43100440

#include "pfe_cfg.h"
#include "oal.h"
#include "hal.h"
#include "pfe_platform_cfg.h"
#include "pfe_class.h"
#include "pfe_util.h"
#include "pfe_tmu.h"
#include "pfe_hw_feature.h"
#include "pfe_feature_mgr.h"

/*
--------------         -----------------         -----------
|PFE Platform|--uses-->|pfe_feature_mgr|--uses-->|pfe_class|--uses---------\
--------------         -----------------         -----------               |
                          ^    |      |          ----------                |
-----                     |    |      \---uses-->|pfe_util|---uses------\  |
|FCI|----uses-------------/    |                 ----------             |  |
-----                          |                                        V  V
                               |                                     ----------------
                               |------------------------------uses-->|pfe_fw_feature|
                               |                                     ----------------
                               |
                               |                                     ----------------
                               \------------------------------uses-->|pfe_hw_feature|
                                                                     ----------------
*/

typedef struct
{
    uint32_t *         cbus_base;
    uint32_t           current_hw_feature; /* Index of the hw feature to return by pfe_hw_get_feature_next() */
    pfe_hw_feature_t   hw_features[2];        /* List of all hw features*/
    uint32_t           hw_features_count;  /* Number of items in hw_features */

    bool_t rewind_flg; /* Internal flag supporting transition walk from hw_feature set to fw_feature set */
    pfe_class_t *class;
    pfe_util_t *util;
    pfe_tmu_t * tmu; /* Included because of err051211_workaround */
} pfe_feature_mgr_t;

typedef struct
{
    bool_t   class_avail;
    bool_t   util_avail;
} pfe_feature_mgr_state_info_t;

#ifdef PFE_CFG_TARGET_OS_AUTOSAR
#define ETH_43_PFE_START_SEC_VAR_CLEARED_UNSPECIFIED
#include "Eth_43_PFE_MemMap.h"
#endif /* PFE_CFG_TARGET_OS_AUTOSAR */

/**
 * @brief Feature manager instance
 * @details The feature manager is a single instance only, the instance handle is stored here
 */

static pfe_feature_mgr_t feature_mgr = { 0 };

#ifdef PFE_CFG_TARGET_OS_AUTOSAR
#define ETH_43_PFE_STOP_SEC_VAR_CLEARED_UNSPECIFIED
#include "Eth_43_PFE_MemMap.h"
#endif /* PFE_CFG_TARGET_OS_AUTOSAR */

#ifdef PFE_CFG_TARGET_OS_AUTOSAR
#define ETH_43_PFE_START_SEC_CODE
#include "Eth_43_PFE_MemMap.h"
#endif /* PFE_CFG_TARGET_OS_AUTOSAR */

static errno_t pfe_hw_get_feature(pfe_feature_mgr_t *fmgr, pfe_hw_feature_t **feature, const char *name);
static errno_t pfe_hw_get_feature_first(pfe_feature_mgr_t *fmgr, pfe_hw_feature_t **feature);
static errno_t pfe_hw_get_feature_next(pfe_feature_mgr_t *fmgr, pfe_hw_feature_t **feature);
static errno_t pfe_feature_mgr_configure_driver(const char *feature_name, const uint8_t val);
static errno_t pfe_feature_mgr_disable_hw_feature(const char *feature_name);
static errno_t pfe_feature_mgr_disable_class_feature(const char *feature_name);
static errno_t pfe_feature_mgr_enable_class_feature(const char *feature_name);
static errno_t pfe_feature_mgr_enable_hw_feature(const char *feature_name);
static errno_t pfe_feature_mgr_get_val_class_util(const char *feature_name, uint8_t **val);
static errno_t pfe_feature_mgr_set_val_class_util(pfe_fw_feature_t *fw_feature_class, pfe_fw_feature_t *fw_feature_util, const uint8_t val);
static errno_t pfe_feature_mgr_handle_feature(const pfe_feature_mgr_t *fmgr, const char *feature_name, const uint8_t val);
static void pfe_feature_mgr_is_feature_present(pfe_fw_feature_t *fw_feature_class, pfe_feature_mgr_state_info_t *mgr_state);
static errno_t pfe_feature_mgr_is_class_available(const char *feature_name, pfe_fw_feature_t **fw_feature_class, pfe_feature_mgr_state_info_t *mgr_state);
static errno_t pfe_feature_mgr_is_util_available(const char *feature_name, pfe_fw_feature_t **fw_feature_util, pfe_feature_mgr_state_info_t *mgr_state);

/**
 * @brief Initializes (the only) feature manager instance
 * @param[in] cbus_base Reference to the Platform config
 * @return EOK or error code in case of failure.
 */
errno_t pfe_feature_mgr_init(uint32_t *cbus_base)
{
    errno_t ret;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (NULL == cbus_base)
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif
    {
        (void)memset(&feature_mgr, 0, sizeof(pfe_feature_mgr_t));
        feature_mgr.cbus_base = cbus_base;
        ret = pfe_hw_feature_init_all(cbus_base, feature_mgr.hw_features, &feature_mgr.hw_features_count);
    }

    return ret;
}

/**
 * @brief Link FW modules class and util
 * @param[in] class Reference to the class module (cannot be NULL - class must be always present)
 * @param[in] util Reference to the util module (value NULL means util is not present)
 * @param[in] tmu Reference to the tmu module  (cannot be NULL - tmu must be always present)
 * @return EOK or error code in case of failure.
 */
errno_t pfe_feature_mgr_add_modules(pfe_class_t *class, pfe_util_t *util, pfe_tmu_t *tmu)
{
    errno_t ret;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if ((NULL == class) || (NULL == tmu))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
/* Note it is OK for "util" to be NULL */
#endif
    {
        feature_mgr.class = class;
        feature_mgr.util = util;
        feature_mgr.tmu = tmu;
        ret = EOK;
    }

    return ret;
}

/**
 * @brief Deinitializes the feature manager instance
 * @return EOK or error code in case of failure.
 */
errno_t pfe_feature_mgr_fini(void)
{
    (void)memset(&feature_mgr, 0, sizeof(pfe_feature_mgr_t));

    return EOK;
}

/**
 * @brief       Checks whether the firmware feature with given name is available to be used
 * @param[in]   feature_name Name of the feature to check
 * @param[in]   fw_feature_class Feature descriptor in class.
 * @param[in]   mgr_state Class/Util feature state
 * @return      EOK or failure code.
 * @details     It is checked whether the feature is applicable for class and then
 *              it is checked whether it is enabled at all places it is applicable for.
 */

static errno_t pfe_feature_mgr_is_class_available(const char *feature_name, pfe_fw_feature_t **fw_feature_class, pfe_feature_mgr_state_info_t *mgr_state)
{
    errno_t ret;

    ret = pfe_class_get_feature(feature_mgr.class, fw_feature_class, feature_name);

    /* Analyze Class */
    if (EOK == ret)
    {
        /* Descriptor in class is available */

        if (TRUE == pfe_fw_feature_is_in_class(*fw_feature_class))
        {
            /* This feature is applicable for class */

            if (pfe_fw_feature_enabled(*fw_feature_class))
            {
                /* Feature is enabled thus it is available */
                mgr_state->class_avail = TRUE;
            }
            else
            {
                /* Feature is disabled thus it is not available */
                mgr_state->class_avail = FALSE;
            }
        }
        else
        {
            /* Not applicable for class */
            /* Do not block availability of the feature which is not applicable */
            mgr_state->class_avail = TRUE;
        }
    }

    return ret;
}

/**
 * @brief       Checks whether the firmware feature with given name is available to be used
 * @param[in]   name Name of the feature to check
 * @retval      TRUE Feature is available
 * @retval      FALSE Feature is not available
 * @details     It is checked whether the feature is applicable for hw, class, util or both and then
 *              it is checked whether it is enabled at all places it is applicable for.
 */
static errno_t pfe_feature_mgr_is_util_available(const char *feature_name, pfe_fw_feature_t **fw_feature_util, pfe_feature_mgr_state_info_t *mgr_state)
{
    errno_t ret;

    /* Util is present */
    ret = pfe_util_get_feature(feature_mgr.util, fw_feature_util, feature_name);

    /* Analyze Util */
    if (EOK == ret)
    {
        /* Descriptor in util is available */
        if (TRUE == pfe_fw_feature_is_in_util(*fw_feature_util))
        {
            /* This feature is applicable for util */

            if (pfe_fw_feature_enabled(*fw_feature_util))
            {
                /* Feature is enabled thus it is available */
                mgr_state->util_avail = TRUE;
            }
            else
            {
                /* Feature is disabled thus it is not available */
                mgr_state->util_avail = FALSE;
            }
        }
        else
        {
            /* Not applicable for util */
            /* Do not block availability of the feature which is not applicable */
            mgr_state->util_avail = TRUE;
        }
    }

    return ret;
}

/**
 * @brief       Checks whether the feature requires util to be present
 * @param[in]   fw_feature_class class information
 * @param[in]   mgr_state Class/Util feature state
 */
static void pfe_feature_mgr_is_feature_present(pfe_fw_feature_t *fw_feature_class, pfe_feature_mgr_state_info_t *mgr_state)
{
    /* Use class information to check whether the feature requires util to be present */
    if (pfe_fw_feature_is_in_util(fw_feature_class))
    {
        /* No firmware = no feature */
        mgr_state->util_avail = FALSE;
    }
    else
    {
        /* Feature does not need util to be present */
        mgr_state->util_avail = TRUE;
    }
}

/**
 * @brief       Checks whether the firmware feature with given name is available to be used
 * @param[in]   name Name of the feature to check
 * @retval      TRUE Feature is available
 * @retval      FALSE Feature is not available
 * @details     It is checked whether the feature is applicable for hw, class, util or both and then
 *              it is checked whether it is enabled at all places it is applicable for.
 */
bool_t pfe_feature_mgr_is_available(const char *feature_name)
{
    pfe_hw_feature_t *hw_feature = NULL;
    pfe_fw_feature_t *fw_feature_class = NULL;
    pfe_fw_feature_t *fw_feature_util = NULL;
    pfe_feature_mgr_state_info_t mgr_state;
    errno_t           ret_class, ret_util;
    bool_t            is_avail = FALSE;
    errno_t           ret_hw;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (NULL == feature_name)
    {
        NXP_LOG_ERROR("NULL argument received\n");
        is_avail = FALSE;
    }
    else
#endif
    {
        if (0U == feature_mgr.hw_features_count)
        {
            NXP_LOG_ERROR("Feature Mgr not initialized\n");
            is_avail = FALSE;
        }
        else
        {
            (void)memset(&mgr_state, 0, sizeof(pfe_feature_mgr_state_info_t));
            ret_hw = pfe_hw_get_feature(&feature_mgr, &hw_feature, feature_name);
            if (EOK == ret_hw)
            {
                /* Descriptor in platform is available */
                if (pfe_hw_feature_enabled(hw_feature))
                {
                    /* Feature is enabled thus it is available */
                    is_avail = TRUE;
                }
                else
                {
                    /* Feature is disabled thus it is not available */
                    is_avail = FALSE;
                }
            }
            else
            {
                if (NULL == feature_mgr.class)
                {
                    /* Class block is not initialized */
                    is_avail = FALSE;
                }
                else
                {
                    ret_class = pfe_feature_mgr_is_class_available(feature_name, &fw_feature_class, &mgr_state);
                    if(EOK != ret_class)
                    {
                        /* Feature does not exist i.e. it is not available */
                        mgr_state.util_avail = FALSE;
                    }

                    if (NULL != feature_mgr.util)
                    {
                        ret_util = pfe_feature_mgr_is_util_available(feature_name, &fw_feature_util, &mgr_state);
                        if(EOK != ret_util)
                        {
                            /* Feature does not exist i.e. it is not available */
                            mgr_state.util_avail = FALSE;
                        }
                    }
                    else
                    {
                        /* Util not present */

                        if (EOK == ret_class)
                        {
                            pfe_feature_mgr_is_feature_present(fw_feature_class, &mgr_state);
                        }
                    }
                    /* Return TRUE if the feature availability is not blocked by any of class/util pair */
                    is_avail = ((FALSE != mgr_state.class_avail) && (FALSE != mgr_state.util_avail)) ? TRUE : FALSE;
                }
            }
        }
    }

    return is_avail;
}

/**
 * @brief  Set value for class and util feature
 * @param[in] fw_feature_class Name of the class feature to be set
 * @param[in] fw_feature_util Name of the util feature to be set
 * @param[in] val Value to be set
 * @return EOK or failure code.
 */
static errno_t pfe_feature_mgr_set_val_class_util(pfe_fw_feature_t *fw_feature_class, pfe_fw_feature_t *fw_feature_util, const uint8_t val)
{
    errno_t ret = EOK;
    uint8_t old_val;

    /* Handle the Class */
    if (TRUE == pfe_fw_feature_is_in_class(fw_feature_class))
    {
        /* Backup the original value for the failure case */
        (void)pfe_fw_feature_get_val(fw_feature_class, &old_val);
        /* Set the new value */
        ret = pfe_fw_feature_set_val(fw_feature_class, val);
    }

    /* Handle the Util */
    if (NULL != feature_mgr.util)
    { /* Util is present */
        /* Continue only if the previous code succeeded - we need to
        keep the class and util coherent */
        if (EOK == ret)
        {
            if (TRUE == pfe_fw_feature_is_in_util(fw_feature_util))
            {
                ret = pfe_fw_feature_set_val(fw_feature_util, val);
                if (EOK != ret)
                { /* Failure */
                    /* Revert the changes already made */
                    (void)pfe_fw_feature_set_val(fw_feature_util, old_val);
                }
            }
        }
    }

    return ret;
}

/**
 * @brief  Handle class and util feature
 * @param[in] fmgr The feature_mgr instance
 * @param[in] feature_name Name of the feature to be set
 * @param[in] val Value to be set
 * @return EOK or failure code.
 */
static errno_t pfe_feature_mgr_handle_feature(const pfe_feature_mgr_t *fmgr, const char *feature_name, const uint8_t val)
{
    errno_t          ret = EOK;
    pfe_fw_feature_t *fw_feature_class = NULL;
    pfe_fw_feature_t *fw_feature_util = NULL;
    errno_t           ret_class, ret_util;

    ret_class = pfe_class_get_feature(fmgr->class, &fw_feature_class, feature_name);
    if (EOK != ret_class)
    { /* Feature does not exist or data is inconsistent */
        ret = EINVAL;
    }
    else
    {
        if (NULL != fmgr->util)
        {
            ret_util = pfe_util_get_feature(fmgr->util, &fw_feature_util, feature_name);
            if (EOK != ret_util)
            { /* Feature does not exist or data is inconsistent */
                ret = EINVAL;
            }
        }

        if (EOK == ret)
        {
            ret = pfe_feature_mgr_set_val_class_util(fw_feature_class, fw_feature_util, val);
            /* Check/configure driver (if needed) */
            if (EOK == ret)
            {
                ret = pfe_feature_mgr_configure_driver(feature_name, val);
            }
        }
    }

    return ret;
}

/**
 * @brief Sets the value of the feature enable variable
 * @param[in] feature_name Name of the feature to be set
 * @param[in] val Value to be set
 * @return EOK or failure code.
 */
errno_t pfe_feature_mgr_set_val(const char *feature_name, const uint8_t val)
{
    pfe_hw_feature_t *hw_feature;
    pfe_ct_feature_flags_t flags = F_NONE;
    errno_t           ret_feature;
    errno_t           ret = EOK;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (NULL == feature_name)
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif
    {
        if (0U == feature_mgr.hw_features_count)
        {
            NXP_LOG_ERROR("Feature Mgr not initialized\n");
            ret = EINVAL;
        }
        else
        {
            ret_feature = pfe_hw_get_feature(&feature_mgr, &hw_feature, feature_name);
            if (EOK == ret_feature)
            { /* Feature exists */
                ret = pfe_hw_feature_get_flags(hw_feature, &flags);
                if (0U != ((uint8_t)flags & (uint8_t)F_RUNTIME))
                {
                    ret = pfe_hw_feature_set_val(hw_feature, val);
                }
                else
                {
                    ret = EFAULT;
                }
            }
            else
            {
                if (NULL == feature_mgr.class)
                { /* Class block is not initialized */
                    ret = EINVAL;
                }
                else
                {
                    ret = pfe_feature_mgr_handle_feature(&feature_mgr, feature_name, val);
                }
            }
        }
    }

    return ret;
}

/**
 * @brief       Enables the given hw feature
 * @param[in]   feature_name Feature to enable
 * @retval      EOK the feature is enabled
 * @return      Failure code means the feature could not be enabled
 */
static errno_t pfe_feature_mgr_enable_hw_feature(const char *feature_name)
{
    errno_t             ret = EINVAL;
    errno_t             ret_val;
    pfe_hw_feature_t *  hw_feature;
    pfe_ct_feature_flags_t tmp;

    /* HW feature first */
    ret_val = pfe_hw_get_feature(&feature_mgr, &hw_feature, feature_name);
    if (EOK == ret_val)
    {
        ret_val = pfe_hw_feature_get_flags(hw_feature, &tmp);
        if (EOK == ret_val)
        {
            if (0U == ((uint8_t)tmp & (uint8_t)F_PRESENT))
            { /* Feature cannot be enabled */
                NXP_LOG_WARNING("Cannot enable feature %s - not present in Platform\n", feature_name);
                ret = EINVAL;
            }
            else if (0U == ((uint8_t)tmp & (uint8_t)F_RUNTIME))
            { /* Feature cannot be disabled */
                NXP_LOG_INFO("Feature %s is always enabled in Platform\n", feature_name);
                ret = EOK;
            }
            else
            { /* Feature needs to be enabled */
                ret = pfe_feature_mgr_set_val(feature_name, 1);
            }
        }
        /* Don't continue with FW features */
    }

    return ret;
}

/**
 * @brief       Enables the given class feature
 * @param[in]   feature_name Feature to enable
 * @retval      EOK the feature is enabled
 * @return      Failure code means the feature could not be enabled
 */
static errno_t pfe_feature_mgr_enable_class_feature(const char *feature_name)
{
    errno_t              ret = EINVAL;
    errno_t              ret_val;
    pfe_fw_feature_t *   fw_feature_class;
    pfe_ct_feature_flags_t tmp;

    /* Class and util share the same information thus it is enough to use just the class to get it */
    ret_val = pfe_class_get_feature(feature_mgr.class, &fw_feature_class, feature_name);
    if (EOK == ret_val)
    {
        ret_val = pfe_fw_feature_get_flags(fw_feature_class, &tmp);
        if (EOK == ret_val)
        {
            if (0U == ((uint8_t)tmp & (uint8_t)F_PRESENT))
            { /* Feature cannot be enabled */
                NXP_LOG_INFO("Cannot enable feature %s - not present in FW\n", feature_name);
                ret = EINVAL;
            }
            else if (0U == ((uint8_t)tmp & (uint8_t)F_RUNTIME))
            { /* Feature cannot be disabled */
                NXP_LOG_ERROR("Feature %s is always enabled in FW\n", feature_name);
                ret = EOK;
            }
            else
            { /* Feature needs to be enabled */
                ret = pfe_feature_mgr_set_val(feature_name, 1);
            }
        }
    }

    return ret;
}

/**
 * @brief       Enables the given feature
 * @param[in]   feature_name Feature to enabled
 * @retval      EOK the feature is enabled
 * @return      Failure code means the feature could not be enabled
 */
errno_t pfe_feature_mgr_enable(const char *feature_name)
{
    errno_t                ret = EINVAL;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (NULL == feature_name)
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif
    {
        if (0U == feature_mgr.hw_features_count)
        {
            NXP_LOG_ERROR("Feature Mgr not initialized\n");
            ret = EINVAL;
        }
        else
        {

            ret = pfe_feature_mgr_enable_hw_feature(feature_name);
            if(EOK != ret)
            {
                if (NULL == feature_mgr.class)
                { /* Class block is not initialized */
                    ret = EINVAL;
                }
                else
                {
                    ret = pfe_feature_mgr_enable_class_feature(feature_name);
                }
            }
        }
    }

    return ret;
}

/**
 * @brief       Disables the given hw feature
 * @param[in]   feature_name Feature to disable
 * @retval      EOK the feature is disabled
 * @return      Failure code means the feature could not be disabled
 */
static errno_t pfe_feature_mgr_disable_hw_feature(const char *feature_name)
{
    errno_t             ret = EINVAL;
    errno_t             ret_val;
    pfe_hw_feature_t *  hw_feature;
    pfe_ct_feature_flags_t tmp;

    /* HW feature first */
    ret_val = pfe_hw_get_feature(&feature_mgr, &hw_feature, feature_name);
    if (EOK == ret_val)
    {
        ret_val = pfe_hw_feature_get_flags(hw_feature, &tmp);
        if (EOK == ret_val)
        {
            if (0U == ((uint8_t)tmp & (uint8_t)F_PRESENT))
            { /* Feature cannot be enabled */
                NXP_LOG_INFO("Feature %s is always disabled in Platform\n", feature_name);
                ret = EOK;
            }
            else if (0U == ((uint8_t)tmp & (uint8_t)F_RUNTIME))
            { /* Feature cannot be disabled */
                NXP_LOG_ERROR("Cannot disabled feature %s - always enabled in Platform\n", feature_name);
                ret = EINVAL;
            }
            else
            { /* Feature needs to be disabled */
                ret = pfe_feature_mgr_set_val(feature_name, 0);
            }
        }

        /* Don't continue with FW features */
    }

    return ret;
}

/**
 * @brief       Disables the given class feature
 * @param[in]   feature_name Feature to disable
 * @retval      EOK the feature is disabled
 * @return      Failure code means the feature could not be disabled
 */
static errno_t pfe_feature_mgr_disable_class_feature(const char *feature_name)
{
    errno_t              ret = EINVAL;
    errno_t              ret_val;
    pfe_fw_feature_t    *fw_feature_class;
    pfe_ct_feature_flags_t tmp;

    /* Class and util share the same information thus it is enough to use just the class to get it */
    ret_val = pfe_class_get_feature(feature_mgr.class, &fw_feature_class, feature_name);
    if (EOK == ret_val)
    {
        ret_val = pfe_fw_feature_get_flags(fw_feature_class, &tmp);
        if (EOK == ret_val)
        {
            if (0U == ((uint8_t)tmp & (uint8_t)F_PRESENT))
            { /* Feature cannot be enabled */
                NXP_LOG_INFO("Feature %s is always disabled in FW\n", feature_name);
                ret = EOK;
            }
            else if (0U == ((uint8_t)tmp & (uint8_t)F_RUNTIME))
            { /* Feature cannot be disabled */
                NXP_LOG_ERROR("Cannot disabled feature %s - always enabled in FW\n", feature_name);
                ret = EINVAL;
            }
            else
            { /* Feature needs to be disabled */
                ret = pfe_feature_mgr_set_val(feature_name, 0);
            }
        }
    }

    return ret;
}

/**
 * @brief       Disables the given feature
 * @param[in]   feature_name Feature to disable
 * @retval      EOK the feature is disabled
 * @return      Failure code means the feature could not be disabled
 */
errno_t pfe_feature_mgr_disable(const char *feature_name)
{
    errno_t                ret = EINVAL;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (NULL == feature_name)
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif
    {
        if (0U == feature_mgr.hw_features_count)
        {
            NXP_LOG_ERROR("Feature Mgr not initialized\n");
            ret = EINVAL;
        }
        else
        {

            ret = pfe_feature_mgr_disable_hw_feature(feature_name);
            if(EOK != ret)
            {
                if (NULL == feature_mgr.class)
                { /* Class block is not initialized */
                    ret = EINVAL;
                }
                else
                {

                    ret = pfe_feature_mgr_disable_class_feature(feature_name);
                }
            }
        }
    }

    return ret;
}

/**
 * @brief       Reads the class/util feature value
 * @param[in]   feature_name Name of the feature to be read
 * @param[out]  val The read value of the feature enable variable
 * @return      EOK or failure code.
 */
static errno_t pfe_feature_mgr_get_val_class_util(const char *feature_name, uint8_t **val)
{
    errno_t             ret = EOK;
    pfe_fw_feature_t    *fw_feature_util = NULL;
    pfe_fw_feature_t    *fw_feature_class;
    errno_t             ret_val;

    ret_val = pfe_class_get_feature(feature_mgr.class, &fw_feature_class, feature_name);
    if (EOK != ret_val)
    { /* Feature does not exist or data is inconsistent */
        ret = EINVAL;
    }
    else
    {

        if (NULL != feature_mgr.util)
        {
            ret_val = pfe_util_get_feature(feature_mgr.util, &fw_feature_util, feature_name);
            if (EOK != ret_val)
            { /* Data is inconsistent - feature found in class but not in util */
                    NXP_LOG_WARNING("Inconsistent feature data for %s\n", feature_name);
                    ret = EINVAL;
                }
        }

        if (EOK == ret)
        {
            /* Check the value in class if relates to class */
            if (TRUE == pfe_fw_feature_is_in_class(fw_feature_class))
            {
                ret = pfe_fw_feature_get_val(fw_feature_class, *val);
                /* We can stop here because data shall be consistent between class and util
                thus it does not matter which value is read */
            }
            else
            {
                /* This is for features related to util only (code above will not read the value)*/
                if (NULL != feature_mgr.util)
                { /* Util is available */
                    /* Check the value in util if relates to util */
                    if (TRUE == pfe_fw_feature_is_in_util(fw_feature_util))
                    {
                        ret = pfe_fw_feature_get_val(fw_feature_util, *val);
                    }
                    else
                    {
                        /* We can get here only if feature is not present in class nor util */
                        NXP_LOG_WARNING("Wrong feature %s (not relevant to any FW)\n", feature_name);
                    }
                }
                else
                {
                    /* We can get here only if feature is not present in class nor util */
                    NXP_LOG_WARNING("Wrong feature %s (not relevant to any FW)\n", feature_name);
                }
            }
        }
    }

    return ret;
}

/**
 * @brief       Reads the feature value
 * @param[in]   feature_name Name of the feature to be read
 * @param[out]  val The read value of the feature enable variable
 * @return      EOK or failure code.
 */
errno_t pfe_feature_mgr_get_val(const char *feature_name, uint8_t *val)
{
    pfe_hw_feature_t *hw_feature;
    errno_t           ret_feature;
    errno_t           ret = EOK;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if ((NULL == feature_name) || (NULL == val))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif
    {
        if (0U == feature_mgr.hw_features_count)
        {
            NXP_LOG_ERROR("Feature Mgr not initialized\n");
            ret = EINVAL;
        }
        else
        {

            ret_feature = pfe_hw_get_feature(&feature_mgr, &hw_feature, feature_name);
            if (EOK == ret_feature)
            { /* Feature exist */
                ret = pfe_hw_feature_get_val(hw_feature, val);
            }
            else
            {

                if (NULL == feature_mgr.class)
                { /* Class block is not initialized */
                    ret = EINVAL;
                }
                else
                {
                    ret = pfe_feature_mgr_get_val_class_util(feature_name, &val);

                }
            }
        }
    }

    return ret;
}

/**
 * @brief       Returns the 1st feature (resets the features query)
 * @param[out]  feature_name Name of the 1st feature
 * @return      EOK or failure code.
 */
errno_t pfe_feature_mgr_get_first(const char **feature_name)
{
    pfe_hw_feature_t *hw_feature;
    pfe_fw_feature_t *fw_feature;
    errno_t           ret;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (NULL == feature_name)
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif
    {
        if (0U == feature_mgr.hw_features_count)
        {
            NXP_LOG_ERROR("Feature Mgr not initialized\n");
            ret = EINVAL;
        }
        else
        {

            /* HW feature first */
            ret = pfe_hw_get_feature_first(&feature_mgr, &hw_feature);
            if (EOK == ret)
            {
                ret = pfe_hw_feature_get_name(hw_feature, feature_name);
                /* Signal rewind_flg for class/util fw feature walk */
                feature_mgr.rewind_flg = TRUE;
            }
            else
            {
                /* We use the fact that class and util share same list of features and read
           from only one of them */

                if (NULL == feature_mgr.class)
                { /* Class block is not initialized */
                    ret = EINVAL;
                }
                else
                {
                    ret = pfe_class_get_feature_first(feature_mgr.class, &fw_feature);
                    if (EOK == ret)
                    {
                        ret = pfe_fw_feature_get_name(fw_feature, feature_name);
                    }
                    feature_mgr.rewind_flg = FALSE;
                }
            }
        }
    }

    return ret;
}

/**
 * @brief       Returns the next feature (continues the features query)
 * @param[out]  feature_name Name of the next feature
 * @return      EOK or failure code.
 */
errno_t pfe_feature_mgr_get_next(const char **feature_name)
{
    pfe_hw_feature_t *hw_feature;
    pfe_fw_feature_t *fw_feature;
    errno_t           ret;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (NULL == feature_name)
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif
    {
        if (0U == feature_mgr.hw_features_count)
        {
            NXP_LOG_ERROR("Feature Mgr not initialized\n");
            ret = EINVAL;
        }
        else
        {

            /* HW feature first */
            ret = pfe_hw_get_feature_next(&feature_mgr, &hw_feature);
            if (EOK == ret)
            {
                ret = pfe_hw_feature_get_name(hw_feature, feature_name);
            }
            else if (ENOENT == ret)
            {
                /* We use the fact that class and util share same list of features and read
           from only one of them */

                if (NULL == feature_mgr.class)
                { /* Class block is not initialized */
                    ret = EINVAL;
                }
                else
                {

                    if (TRUE == feature_mgr.rewind_flg)
                    {
                        ret = pfe_class_get_feature_first(feature_mgr.class, &fw_feature);
                        /* Unset 'rewind_flg' to use real pfe_class_get_feature_next next time */
                        feature_mgr.rewind_flg = FALSE;
                    }
                    else
                    {
                        ret = pfe_class_get_feature_next(feature_mgr.class, &fw_feature);
                    }

                    if (EOK == ret)
                    {
                        ret = pfe_fw_feature_get_name(fw_feature, feature_name);
                    }
                }
            }
            else
            {
                ; /* No action required */
            }
        }
    }

    return ret;
}

/**
 * @brief       Returns the feature default value
 * @param[in]   feature_name Name of the feature
 * @param[out]  val Default (initial) value of feature enable variable
 * @return      EOK or failure code.
 */
errno_t pfe_feature_mgr_get_def_val(const char *feature_name, uint8_t *val)
{
    pfe_hw_feature_t *hw_feature;
    pfe_fw_feature_t *fw_feature_class;
    errno_t           ret;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (NULL == feature_name)
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif
    {
        if (0U == feature_mgr.hw_features_count)
        {
            NXP_LOG_ERROR("Feature Mgr not initialized\n");
            ret = EINVAL;
        }
        else
        {
            /* HW feature first */
            if (EOK == pfe_hw_get_feature(&feature_mgr, &hw_feature, feature_name))
            {
                ret = pfe_hw_feature_get_def_val(hw_feature, val);
            }
            else
            {

                if (NULL == feature_mgr.class)
                { /* Class block is not initialized */
                    ret = EINVAL;
                }
                else
                {
                    /* The data shall be consistent between util and class thus it is enough to read them from class */

                    ret = pfe_class_get_feature(feature_mgr.class, &fw_feature_class, feature_name);
                    if (EOK == ret)
                    {
                        ret = pfe_fw_feature_get_def_val(fw_feature_class, val);
                    }
                }
            }
        }
    }

    return ret;
}

/**
 * @brief       Returns the feature default description text
 * @param[in]   feature_name Name of the feature
 * @param[out]  desc Feature description text (string)
 * @return      EOK or failure code.
 */
errno_t pfe_feature_mgr_get_desc(const char *feature_name, const char **desc)
{
    pfe_hw_feature_t *hw_feature;
    pfe_fw_feature_t *fw_feature_class;
    errno_t           ret;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if ((NULL == desc) || (NULL == feature_name))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif
    {
        if (0U == feature_mgr.hw_features_count)
        {
            NXP_LOG_ERROR("Feature Mgr not initialized\n");
            ret = EINVAL;
        }
        else
        {
            /* Platfoorm feature first */
            if (EOK == pfe_hw_get_feature(&feature_mgr, &hw_feature, feature_name))
            {
                ret = pfe_hw_feature_get_desc(hw_feature, desc);
            }
            else
            {
                if (NULL == feature_mgr.class)
                { /* Class block is not initialized */
                    ret = EINVAL;
                }
                else
                {
                    /* The data shall be consistent between util and class thus it is enough to read
                    them from class */
                    ret = pfe_class_get_feature(feature_mgr.class, &fw_feature_class, feature_name);
                    if (EOK == ret)
                    {
                        ret = pfe_fw_feature_get_desc(fw_feature_class, desc);
                    }
                }
            }
        }
    }

    return ret;
}

/**
 * @brief       Returns the feature variant
 * @param[in]   feature_name Name of the feature
 * @param[out]  val Value of the flags defining feature variant
 * @return      EOK or failure code.
 */
errno_t pfe_feature_mgr_get_variant(const char *feature_name, uint8_t *val)
{
    pfe_hw_feature_t *     hw_feature;
    pfe_fw_feature_t *     fw_feature_class;
    errno_t                ret;
    pfe_ct_feature_flags_t tmp;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if ((NULL == feature_name) || (NULL == val))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif
    {
        if (0U == feature_mgr.hw_features_count)
        {
            NXP_LOG_ERROR("Feature Mgr not initialized\n");
            ret = EINVAL;
        }
        else
        {
            /* HW feature first */
            if (EOK == pfe_hw_get_feature(&feature_mgr, &hw_feature, feature_name))
            {
                ret = pfe_hw_feature_get_flags(hw_feature, &tmp);
                if (EOK == ret)
                {
                    *val = (uint8_t)tmp & ((uint8_t)F_PRESENT | (uint8_t)F_RUNTIME);
                }
            }
            else
            {
                if (NULL == feature_mgr.class)
                { /* Class block is not initialized */
                    ret = EINVAL;
                }
                else
                {
                    /* The data shall be consistent between util and class thus it is enough to read
                    them from class */
                    ret = pfe_class_get_feature(feature_mgr.class, &fw_feature_class, feature_name);
                    if (EOK == ret)
                    {
                        ret = pfe_fw_feature_get_flags(fw_feature_class, &tmp);
                        if (EOK == ret)
                        {
                            *val = (uint8_t)tmp & ((uint8_t)F_PRESENT | (uint8_t)F_RUNTIME);
                        }
                    }
                }
            }
        }
    }

    return ret;
}

/**
 * @brief Finds and returns HW feature by its name
 * @param[in] fmgr The feature_mgr instance
 * @param[out] feature Feature found (valid only if EOK is returned)
 * @param[in] name Name of the feature to be found
 * @return EOK when given entry is found, ENOENT when it is not found, error code otherwise
 */
static errno_t pfe_hw_get_feature(pfe_feature_mgr_t *fmgr, pfe_hw_feature_t **feature, const char *name)
{
    uint32_t    i;
    const char *fname;
    errno_t     ret = ENOENT;
    errno_t     ret_val = EOK;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely((NULL == fmgr) || (NULL == feature) || (NULL == name)))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        for (i = 0U; i < fmgr->hw_features_count; i++)
        {
            ret_val = pfe_hw_feature_get_name(&fmgr->hw_features[i], &fname);
            if (ret_val == EOK)
            {
                if (0 == strcmp(fname, name))
                {
                    *feature = &fmgr->hw_features[i];
                    ret = EOK;
                    break;
                }
            }
        }
    }
    return ret;
}

/**
 * @brief Finds and returns the 1st HW feature by order of their discovery - used for listing all features
 * @param[in] fmgr The feature mgr instance
 * @param[out] feature Feature found (valid only if EOK is returned)
 * @return EOK when given entry is found, ENOENT when it is not found, error code otherwise
 */
static errno_t pfe_hw_get_feature_first(pfe_feature_mgr_t *fmgr, pfe_hw_feature_t **feature)
{
    errno_t ret;
#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely((NULL == fmgr) || (NULL == feature)))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        if (fmgr->hw_features_count > 0U)
        {
            fmgr->current_hw_feature = 0U;
            *feature = &fmgr->hw_features[fmgr->current_hw_feature];
            ret = EOK;
        }
        else
        {
            ret = ENOENT;
        }
    }

    return ret;
}

/**
 * @brief Finds and returns the next HW feature by order of their discovery - used for listing all features
 * @param[in] fmgr The feature_mgr instance
 * @param[out] feature Feature found (valid only if EOK is returned)
 * @return EOK when given entry is found, ENOENT when it is not found, error code otherwise
 */
static errno_t pfe_hw_get_feature_next(pfe_feature_mgr_t *fmgr, pfe_hw_feature_t **feature)
{
    errno_t ret = ENOENT;
#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely((NULL == fmgr) || (NULL == feature)))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        if (fmgr->hw_features_count > 0U)
        {
            /* Avoid going out of the array boundaries */
            if ((fmgr->current_hw_feature + 1U) < fmgr->hw_features_count)
            {
                fmgr->current_hw_feature += 1U;
                *feature = &fmgr->hw_features[fmgr->current_hw_feature];
                ret = EOK;
            }
        }
    }

    return ret;
}

/**
 * @brief Executes driver-side checks and configurations (if some are needed) in response to FW feature being enabled/disabled.
 * @param[in] feature_name Name of the feature to be set
 * @param[in] val Enable/disable value of the FW feture which was freshly set.
 * @return EOK or failure code.
 */
static errno_t pfe_feature_mgr_configure_driver(const char *feature_name, const uint8_t val)
{
    errno_t ret = EOK;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (NULL == feature_name)
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif
    {
        if (0 == strcmp(feature_name, "err051211_workaround"))
        {
            if (0U != val) /* feature got enabled */
            {
                ret = pfe_tmu_queue_err051211_sync(feature_mgr.tmu);
            }
        }
    }
    return ret;
}

#ifdef PFE_CFG_TARGET_OS_AUTOSAR
#define ETH_43_PFE_STOP_SEC_CODE
#include "Eth_43_PFE_MemMap.h"
#endif /* PFE_CFG_TARGET_OS_AUTOSAR */
