/*
 * Copyright 2019-2020 NXP
 * All rights reserved.
 *
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_otfad.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/

/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.otfad"
#endif

/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/*******************************************************************************
 * Variables
 ******************************************************************************/

/*******************************************************************************
 * Code
 ******************************************************************************/
/*!
 * brief OTFAD module get default configuration.
 *
 * param config configuration.
 */
void OTFAD_GetDefaultConfig(otfad_config_t *config)
{
    assert(config != NULL);

    /* Including OTFAD operate mode, IRQ eable, key blob and restricted register access */
#if defined(FSL_FEATURE_OTFAD_HAS_HAS_IRQ_ENABLE) && (FSL_FEATURE_OTFAD_HAS_HAS_IRQ_ENABLE > 0)
    config->enableIntRequest = false;
#endif /* FSL_FEATURE_OTFAD_HAS_HAS_IRQ_ENABLE */
#if defined(FSL_FEATURE_OTFAD_HAS_FORCE_ERR) && (FSL_FEATURE_OTFAD_HAS_FORCE_ERR > 0)
    config->forceError = false;
#endif /* FSL_FEATURE_OTFAD_HAS_FORCE_ERR */
    config->forceSVM = false;
    config->forceLDM = false;

    config->restrictedRegAccess = false;

#if defined(FSL_FEATURE_OTFAD_HAS_KEYBLOB_PROCESSING) && (FSL_FEATURE_OTFAD_HAS_KEYBLOB_PROCESSING > 0)
    /* Start key blob processing */
    config->startKeyBlobProcessing = false;
    config->keyBlobProcess         = false;
    config->keyBlobScramble        = false;
#endif /* FSL_FEATURE_OTFAD_HAS_KEYBLOB_PROCESSING */

    /* Global OTFAD enable */
    config->enableOTFAD = false;
}

/*!
 * brief OTFAD module initialization function, this function need to put into ram for keyblob process.
 *
 * param base OTFAD base address.
 */
AT_QUICKACCESS_SECTION_CODE(void OTFAD_Init(OTFAD_Type *base, const otfad_config_t *config))
{
    assert(config != NULL);

    /* No independent clock and it is bind with flexspi clock,
       so it need to enable clock for flexspi firstly with initialization. */

    uint32_t temp = 0U;

    /* Set OTFAD operate mode, IRQ eable, key blob and restricted register access */
    temp = OTFAD_CR_FLDM(config->forceLDM) |
#if defined(FSL_FEATURE_OTFAD_HAS_HAS_IRQ_ENABLE) && (FSL_FEATURE_OTFAD_HAS_HAS_IRQ_ENABLE > 0)
           OTFAD_CR_IRQE(config->enableIntRequest) |
#endif /* FSL_FEATURE_OTFAD_HAS_HAS_IRQ_ENABLE */
#if defined(FSL_FEATURE_OTFAD_HAS_FORCE_ERR) && (FSL_FEATURE_OTFAD_HAS_FORCE_ERR > 0)
           OTFAD_CR_FERR(config->forceError) |
#endif /* FSL_FEATURE_OTFAD_HAS_FORCE_ERR */
#if defined(FSL_FEATURE_OTFAD_HAS_SVM_MODE) && FSL_FEATURE_OTFAD_HAS_SVM_MODE
           OTFAD_CR_FSVM(config->forceSVM) |
#endif /* FSL_FEATURE_OTFAD_HAS_SVM_MODE */
#if defined(FSL_FEATURE_OTFAD_HAS_KEYBLOB_PROCESSING) && (FSL_FEATURE_OTFAD_HAS_KEYBLOB_PROCESSING > 0)
           OTFAD_CR_KBSE(config->keyBlobScramble) | OTFAD_CR_KBPE(config->keyBlobProcess) |
#endif /* FSL_FEATURE_OTFAD_HAS_KEYBLOB_PROCESSING */
           OTFAD_CR_RRAE(config->restrictedRegAccess);

    /* Enable global OTFAD operation */
    temp |= OTFAD_CR_GE(config->enableOTFAD);

    /* Wait for AHB bus idle, otherwise the keyblob process will fail */
    while (
        !((((FLEXSPI_Type *)config->flexspiBaseAddr)->STS0 & FLEXSPI_STS0_ARBIDLE_MASK) > FLEXSPI_STS0_ARBIDLE_SHIFT))
    {
    }

#if defined(FSL_FEATURE_OTFAD_HAS_KEYBLOB_PROCESSING) && (FSL_FEATURE_OTFAD_HAS_KEYBLOB_PROCESSING > 0)
    /* Start key blob processing, and software set SKBP only once after a hard reset */
    if (config->startKeyBlobProcessing == true)
    {
        temp |= OTFAD_CR_SKBP(config->startKeyBlobProcessing);
    }
#endif /* FSL_FEATURE_OTFAD_HAS_KEYBLOB_PROCESSING */

    base->CR |= temp;

#if defined(FSL_FEATURE_OTFAD_HAS_KEYBLOB_PROCESSING) && (FSL_FEATURE_OTFAD_HAS_KEYBLOB_PROCESSING > 0)
    /* Check key blob precessing done if enabled */
    if (config->keyBlobProcess == true)
    {
        while (0U == ((base->SR & OTFAD_SR_KBD_MASK) >> OTFAD_SR_KBD_SHIFT))
        {
            ; /* Intentional empty */
        }
    }
#endif /* FSL_FEATURE_OTFAD_HAS_KEYBLOB_PROCESSING */
}

/*!
 * brief Deinitializes the OTFAD.
 *
 */
AT_QUICKACCESS_SECTION_CODE(void OTFAD_Deinit(OTFAD_Type *base))
{
    /* Disable global OTFAD operation */
    base->CR &= ~OTFAD_CR_GE(1U);
}

status_t OTFAD_SetEncryptionConfig(OTFAD_Type *base, const otfad_encryption_config_t *config)
{
    assert(config != NULL);

    status_t status             = kStatus_Success;
    uint32_t temp               = 0U;
    uint32_t startAddr          = config->startAddr;
    uint32_t endAddr            = config->endAddr;
    const uint32_t *tempKey     = config->key;
    const uint32_t *tempCounter = config->counter;
    uint8_t contextIndex        = config->contextIndex;
    uint8_t regAccessMode       = 0U;

    /* Get restricted register access mode */
    regAccessMode = (uint8_t)((base->SR & OTFAD_SR_RRAM_MASK) >> OTFAD_SR_RRAM_SHIFT);

    if (regAccessMode == 0U)
    {
        /* Set context region */
        if (startAddr <= endAddr)
        {
            /* Address is aligned with 1KBytes */
            startAddr = (startAddr & OTFAD_RGD_W0_SRTADDR_MASK) >> OTFAD_RGD_W0_SRTADDR_SHIFT;
            endAddr   = (endAddr & OTFAD_RGD_W1_ENDADDR_MASK) >> OTFAD_RGD_W1_ENDADDR_SHIFT;

            temp |= OTFAD_RGD_W1_ENDADDR(endAddr) | OTFAD_RGD_W1_RO(config->readOnly) |
                    OTFAD_RGD_W1_ADE(config->AESdecryption) | OTFAD_RGD_W1_VLD(config->valid);

            /* Set context n start address  */
            base->CTX[contextIndex].RGD_W0 = OTFAD_RGD_W0_SRTADDR(startAddr);
            /* Set context n end address and enable context n AES decryption */
            base->CTX[contextIndex].RGD_W1 = temp;
        }
        else
        {
            status = kStatus_OTFAD_AddressError;
        }

        /* Set encryption key */
        base->CTX[contextIndex].KEY[0] = tempKey[0];
        base->CTX[contextIndex].KEY[1] = tempKey[1];
        base->CTX[contextIndex].KEY[2] = tempKey[2];
        base->CTX[contextIndex].KEY[3] = tempKey[3];

        /* Set encryption counter */
        base->CTX[contextIndex].CTR[0] = tempCounter[0];
        base->CTX[contextIndex].CTR[1] = tempCounter[1];
    }
    else
    {
        /* Restricted register mode, access to these registers is treated as read-as-zero, write-ignored */
        status = kStatus_OTFAD_ResRegAccessMode;
    }

    return status;
}

status_t OTFAD_GetEncryptionConfig(OTFAD_Type *base, otfad_encryption_config_t *config)
{
    assert(config != NULL);

    status_t status       = kStatus_Success;
    uint8_t regAccessMode = 0U;

    /* Get restricted register access mode */
    regAccessMode = (uint8_t)((base->SR & OTFAD_SR_RRAM_MASK) >> OTFAD_SR_RRAM_SHIFT);

    if (regAccessMode == 0U)
    {
        config->key[0] = base->CTX[config->contextIndex].KEY[0];
        config->key[1] = base->CTX[config->contextIndex].KEY[1];
        config->key[2] = base->CTX[config->contextIndex].KEY[2];
        config->key[3] = base->CTX[config->contextIndex].KEY[3];

        config->counter[0] = base->CTX[config->contextIndex].CTR[0];
        config->counter[1] = base->CTX[config->contextIndex].CTR[1];

        config->startAddr =
            (base->CTX[config->contextIndex].RGD_W0 & OTFAD_RGD_W0_SRTADDR_MASK) >> OTFAD_RGD_W0_SRTADDR_SHIFT;
        config->endAddr =
            (base->CTX[config->contextIndex].RGD_W1 & OTFAD_RGD_W1_ENDADDR_MASK) >> OTFAD_RGD_W1_ENDADDR_SHIFT;
    }
    else
    {
        /* Restricted register mode, access to these registers is treated as read-as-zero, write-ignored */
        status = kStatus_OTFAD_ResRegAccessMode;
    }

    return status;
}

status_t OTFAD_HitDetermination(OTFAD_Type *base, uint32_t address, uint8_t *contextIndex)
{
    status_t status   = kStatus_Success;
    bool contextValid = false;
    uint8_t hitNumber = 0U;
    uint8_t totalHits = 0U;
    uint8_t index     = 0U;
    uint8_t i;
    uint32_t startAddr;
    uint32_t endAddr;
    uint32_t tempAddr;

    /* Address is aligned with 1KBytes */
    tempAddr = (address & 0xFFFFFC00U) >> 10U;

    for (i = 0U; i < OTFAD_RGD_W1_COUNT; i++)
    {
        startAddr = (base->CTX[index].RGD_W0 & OTFAD_RGD_W0_SRTADDR_MASK) >> OTFAD_RGD_W0_SRTADDR_SHIFT;
        endAddr   = (base->CTX[index].RGD_W1 & OTFAD_RGD_W1_ENDADDR_MASK) >> OTFAD_RGD_W1_ENDADDR_SHIFT;
        contextValid =
            ((base->CTX[index].RGD_W1 & OTFAD_RGD_W1_VLD_MASK) >> OTFAD_RGD_W1_VLD_SHIFT) == 1U ? true : false;

        /* Check address hits context n or not */
        if ((tempAddr >= startAddr) && (tempAddr <= endAddr) && contextValid)
        {
            totalHits++;
            hitNumber = i;
        }

        index++;
    }

    /* Hit in multiple contexts or no contexts */
    if (totalHits != 1U)
    {
        status = kStatus_OTFAD_RegionOverlap;
    }
    /* Hit in single context */
    else
    {
        *contextIndex = hitNumber;
    }

    return status;
}
