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

#ifndef _PUF_V3_H_
#define _PUF_V3_H_

#include <stddef.h>
#include <stdint.h>

#include "fsl_common.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/
/*!
 * @addtogroup puf_v3_driver
 * @{
 */
/*! @name Driver version */
/*@{*/
/*! @brief PUFv3 driver version. Version 2.0.0.
 *
 * Current version: 2.0.0
 *
 * Change log:
 * - 2.0.0
 *   - Initial version.
 */
#define FSL_PUF_V3_DRIVER_VERSION (MAKE_VERSION(2, 0, 0))
/*@}*/

#define kPUF_EndianLittle (0x0u)
#define kPUF_EndianBig    (0x1u)
typedef uint32_t puf_endianness_t;

#define kPUF_KeyDestRegister (0x1u)
#define kPUF_KeyDestKeyBus   (0x2u)
#define kPUF_KeyDestInvalid  (0x3u)
typedef uint32_t puf_key_dest_t;

#define kPUF_KeyAllowRegister (0x1u)
#define kPUF_KeyAllowKeyBus   (0x2u)
#define kPUF_KeyAllowAll      (0x3u)
typedef uint32_t puf_key_scope_t;

#define kPUF_ResultOK                  (0x0u)
#define kPUF_AcNotForThisProductPhase1 (0xf0u)
#define kPUF_AcNotForThisProductPhase2 (0xf1u)
#define kPUF_AcCorruptedPhase1         (0xf2u)
#define kPUF_AcCorruptedPhase2         (0xf3u)
#define kPUF_AcAuthFailedPhase1        (0xf4u)
#define kPUF_AcAuthFailedPhase2        (0xf5u)
#define kPUF_QualityVerificationFail   (0xf6u)
#define kPUF_ContextIncorrect          (0xf7u)
#define kPUF_DestinationNotAllowed     (0xf8u)
#define kPUF_Failure                   (0xFFu)
typedef uint32_t puf_result_code_t;

typedef struct
{
    puf_endianness_t dataEndianness;
    uint8_t CKGATING;
} puf_config_t;

typedef struct
{
    puf_key_scope_t keyScopeStarted;
    puf_key_scope_t keyScopeEnrolled;
    uint32_t userCtx0;
    uint32_t userCtx1;
} puf_key_ctx_t;

#define PUF_ACTIVATION_CODE_SIZE              996
#define PUF_GET_KEY_CODE_SIZE_FOR_KEY_SIZE(x) ((0x34u + x) + 0x10u * (x / 0x32u))

enum _puf_status
{
    kStatus_PUF_OperationNotAllowed       = MAKE_STATUS(kStatusGroup_PUF, 0xA5),
    kStatus_PUF_AcNotForThisProductPhase1 = MAKE_STATUS(kStatusGroup_PUF, kPUF_AcNotForThisProductPhase1),
    kStatus_PUF_AcNotForThisProductPhase2 = MAKE_STATUS(kStatusGroup_PUF, kPUF_AcNotForThisProductPhase2),
    kStatus_PUF_AcCorruptedPhase1         = MAKE_STATUS(kStatusGroup_PUF, kPUF_AcCorruptedPhase1),
    kStatus_PUF_AcCorruptedPhase2         = MAKE_STATUS(kStatusGroup_PUF, kPUF_AcCorruptedPhase2),
    kStatus_PUF_AcAuthFailedPhase1        = MAKE_STATUS(kStatusGroup_PUF, kPUF_AcAuthFailedPhase1),
    kStatus_PUF_NBOOT_AcAuthFailedPhase2  = MAKE_STATUS(kStatusGroup_PUF, kPUF_AcAuthFailedPhase2),
    kStatus_PUF_QualityVerificationFail   = MAKE_STATUS(kStatusGroup_PUF, kPUF_QualityVerificationFail),
    kStatus_PUF_ContextIncorrect          = MAKE_STATUS(kStatusGroup_PUF, kPUF_ContextIncorrect),
    kStatus_PUF_DestinationNotAllowed     = MAKE_STATUS(kStatusGroup_PUF, kPUF_DestinationNotAllowed),
    kStatus_PUF_Failure                   = MAKE_STATUS(kStatusGroup_PUF, kPUF_Failure),
};

/*******************************************************************************
 * API
 *******************************************************************************/
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */

/*!
 * brief Sets the default configuration of PUF
 *
 * This function initialize PUF config structure to default values.
 *
 * @param conf PUF configuration structure
 */
void PUF_GetDefaultConfig(puf_config_t *conf);

/*!
 * brief Initialize PUF
 *
 * This function enables power to PUF block and waits until the block initializes.
 *
 * @param conf PUF configuration structure
 * @return Status of the init operation
 */
status_t PUF_Init(PUF_Type *base, puf_config_t *conf);

/*!
 * brief Denitialize PUF
 *
 * This function disables power to PUF SRAM and peripheral clock.
 *
 * @param base PUF peripheral base address
 * @param conf PUF configuration structure
 */
void PUF_Deinit(PUF_Type *base, puf_config_t *conf);

/*!
 * brief Enroll PUF
 *
 * This function derives a digital fingerprint, generates the corresponding Activation Code (AC)
 * and returns it to be stored in an NVM or a file. This step needs to be
 * performed only once for each device. This function may be permanently disallowed by a fuse.
 *
 * @param base PUF peripheral base address
 * @param[out] activationCode Word aligned address of the resulting activation code.
 * @param activationCodeSize Size of the activationCode buffer in bytes. Shall be 996 bytes.
 * @param score Value of the PUF Score that was obtained during the enroll operation.
 * @return Status of enroll operation.
 */
status_t PUF_Enroll(PUF_Type *base, uint8_t *activationCode, size_t activationCodeSize, uint8_t *score);

/*!
 * brief Start PUF
 *
 * The Activation Code generated during the Enroll operation is used to
 * reconstruct the digital fingerprint. This needs to be done after every power-up
 * and reset.
 *
 * @param base PUF peripheral base address
 * @param[in] activationCode Word aligned address of the input activation code.
 * @param activationCodeSize Size of the activationCode buffer in bytes. Shall be 996 bytes.
 * @param score Value of the PUF Score that was obtained during the start operation.
 * return Status of start operation.
 */
status_t PUF_Start(PUF_Type *base, const uint8_t *activationCode, size_t activationCodeSize, uint8_t *score);

/*!
 * brief Stop PUF
 *
 * The Stop operation removes all key material from PUF flipflops and PUF SRAM, and sets
 * PUF to the Stopped state.
 *
 * @param base PUF peripheral base address
 * @return Status of stop operation.
 */
status_t PUF_Stop(PUF_Type *base);

/*!
 * brief PUF Get Key
 *
 * The Get Key operation derives a key from the intrinsic PUF key and externally provided context.
 *
 * @param base PUF peripheral base address
 * @param keyCtx PUF key context struct
 * @param keyDest output destination of the derived PUF key
 * @param[out] key Word aligned address of output key (only used when kPUF_KeyDestRegister).
 * @param keySize Size of the derived key in bytes.
 * @return Status of get key operation.
 */
status_t PUF_GetKey(PUF_Type *base, puf_key_ctx_t *keyCtx, puf_key_dest_t keyDest, uint8_t *key, size_t keySize);

/*!
 * brief PUF Wrap generated random
 *
 * The Wrap Generated Random operation wraps a random key into a Key Code (KC).
 *
 * @param base PUF peripheral base address
 * @param keyCtx PUF key context struct
 * @param keySize Size of the key to be generated in bytes.
 * @param[out] keyCode Word aligned address of the resulting key code.
 * @param keyCodeSize Size of the output keycode in bytes.
 * @return Status of wrap generated random operation.
 */
status_t PUF_WrapGeneratedRandom(
    PUF_Type *base, puf_key_ctx_t *keyCtx, size_t keySize, uint8_t *keyCode, size_t keyCodeSize);

/*!
 * brief PUF Wrap user key
 *
 * The Wrap operation wraps a user defined key into a Key Code (KC).
 *
 * @param base PUF peripheral base address
 * @param keyCtx PUF key context struct.
 * @param userKey Word aligned address of input user key.
 * @param userKeySize Size of the key to be wrapped in bytes.
 * @param[out] keyCode Word aligned address of the resulting key code.
 * @param keyCodeSize Size of the output keycode in bytes.
 * @return Status of wrap operation.
 */
status_t PUF_Wrap(
    PUF_Type *base, puf_key_ctx_t *keyCtx, uint8_t *userKey, size_t userKeySize, uint8_t *keyCode, size_t keyCodeSize);

/*!
 * brief PUF Unwrap user key
 *
 * The unwrap operation unwraps the key from a previously created Key Code (KC)
 *
 * @param base PUF peripheral base address
 * @param keyDest output destination of the unwraped PUF key
 * @param[in] keyCode Word aligned address of the input key code.
 * @param keyCodeSize Size of the input keycode in bytes.
 * @param key Word aligned address of output key (only used when kPUF_KeyDestRegister).
 * @param keySize Size of the key to be generated in bytes.
 * @return Status of unwrap operation.
 */
status_t PUF_Unwrap(
    PUF_Type *base, puf_key_dest_t keyDest, uint8_t *keyCode, size_t keyCodeSize, uint8_t *key, size_t keySize);

/*!
 * brief Generate Random
 *
 * The Generate Random operation outputs the requested amount of random data as specified in a
 * provided context.
 *
 * @param base PUF peripheral base address
 * @param size Size of random data to be genarated in bytes.
 * @return Status of generate random operation.
 */
status_t PUF_GenerateRandom(PUF_Type *base, uint8_t *data, size_t size);

/*!
 * brief Zeroize PUF
 *
 * This function clears all PUF internal logic and puts the PUF to zeroized state.
 *
 * @param base PUF peripheral base address
 * @return Status of the zeroize operation.
 */
status_t PUF_Zeroize(PUF_Type *base);

/*!
 * brief Test PUF
 *
 * With the Test PUF operation, diagnostics about the PUF quality is collected and presented in a PUF
 * score.
 *
 * @param base PUF peripheral base address
 * @param score Value of the PUF Score that was obtained during the enroll operation.
 * @return Status of the test operation.
 */
status_t PUF_Test(PUF_Type *base, uint8_t *score);

/*!
 * @brief Blocks specified PUF commands
 *
 * This function blocks PUF commands specified by mask parameter.
 *
 * @param base PUF peripheral base address
 * @param score Value of the PUF Score that was obtained during the enroll operation.
 * @return Status of the test operation.
 */
static inline void PUF_BlockCommand(PUF_Type *base, uint32_t mask)
{
    base->CONFIG |= mask;
}

#if defined(__cplusplus)
}
#endif /* __cplusplus */

#endif /* _PUF_H_ */
