/**
 * @file        scp_general.h
 * @brief       General SCP operations.
 *
 * @note        Client applications should include scp.h instead of this file.
 *
 * Copyright (c) 2021-2022, NVIDIA CORPORATION. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * * Neither the name of NVIDIA CORPORATION nor the names of its
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef SCP__SCP_GENERAL_H
#define SCP__SCP_GENERAL_H
#define FSP__SCP__SCP_GENERAL_H 1

/* Compiler headers */
#include <stdbool.h>                    // for bool
#include <stdint.h>                     // for uint8_t, uintptr_t, etc.

/* Early FSP headers */
#include <misc/ct-assert.h>             // for HEADER_CHECK

/* Hardware headers */

/* Late FSP headers */
#include <error/common-errors.h>        // for error_t
// Clients must provide their own DRF headers

/* Module-specific FSP headers */
#include <scp/scp_common.h>             // for scp_register_index_t, etc.

/*
 * Compile-time check for FSP header files
 *   Each FSP header file contains a signature unique to that file, and the
 *   FSP project.  The HEADER_CHECK macro (contained in misc/ct-assert.h) can
 *   check for this signature.  If it does not exist, then the build will
 *   abort.
 *
 *   This is a trap for projects which have their own include files of the
 *   same names, but different contents.  This trap ensures that only the
 *   files from the FSP project, are built into the FSP source code.
 */
HEADER_CHECK(FSP__ERROR__COMMON_ERRORS_H)
HEADER_CHECK(FSP__SCP__SCP_COMMON_H)


/**
 * @brief Allocates a buffer whose base is aligned to SCP_BUFFER_ALIGNMENT.
 *
 * Allocates a buffer of size at least size whose base address is aligned to
 * SCP_BUFFER_ALIGNMENT. The buffer has automatic storage duration.
 *
 * Note that the pointer returned by this macro may require virtual-to-physical
 * conversion before it can be used in an SCP interface call, depending on the
 * application's current operating mode. Furthermore, it is up to the
 * application to ensure that the alignment properties of the returned pointer
 * are preserved through the virtual-to-physical conversion process, if
 * applicable.
 *
 * This macro is safe to call from direct mode.
 *
 * @param[in] size The desired size for the new buffer in bytes. This should
 *                 generally be a nonzero multiple of one of the provided
 *                 SCP_*_SIZE constants.
 *
 * @return A pointer to the newly-allocated buffer.
 */
#define SCP_ALIGNED_BUFFER(size) (                                    \
    (uint8_t*)NV_ALIGN_UP64(                                          \
        (uintptr_t)(uint8_t[(size) + SCP_BUFFER_ALIGNMENT - 1U]){0U}, \
        SCP_BUFFER_ALIGNMENT                                          \
    )                                                                 \
)

/**
 * @brief Configures the shortcut-DMA path for an external destination buffer.
 *
 * Configures the shortcut path for transfers to external memory as if by
 * calling scp_Configure_Shortcut(dma_index) and then returns dest_pa. Returns
 * zero/NULL if configuration fails.
 *
 * This helper is meant to simplify the process of passing external buffers to
 * applicable SCP interfaces by allowing for the shortcut-DMA path to be
 * configured in-line. It also aims to lessen the risk of misconfigured
 * shortcut transfers by encouraging applications to explicitly re-configure
 * for each operation.
 *
 * Note that shortcut-DMA support must be enabled during build in order for
 * this macro to succeed.
 *
 * @pre The selected DMA aperture must have been properly configured.
 *
 * @param[in] dest_pa   The physical address of the destination buffer.
 * @param[in] dma_index The index of the DMA aperture to be used when
 *                      transferring to the destination buffer.
 *
 * @retval dest_pa  if the shortcut-DMA path was configured successfully.
 *
 * @retval 0        if dma_index does not refer to a valid DMA aperture, or the
 *                  application does not have shortcut-DMA support enabled, or
 *                  the SCP driver has not been initialized.
 */
#define SCP_EXTERNAL_DEST(dest_pa, dma_index) (        \
    (scp_Configure_Shortcut(dma_index) == E_SUCCESS) ? \
    (dest_pa) : (uintptr_t)0U                          \
)


/**
 * @brief A collection of flags that can be used to customize driver behaviour.
 */
typedef enum scp_init_flag_t
{
    /**
     * @brief Use the recommended default configuration settings.
     *
     * @note Ignored if combined with other flags.
     */
    SCP_INIT_FLAG_DEFAULT = 0,

} scp_init_flag_t;


/**
 * @brief Initializes the SCP driver and underlying hardware for use.
 *
 * Initializes the SCP driver for use. Applications must not call into any
 * other SCP interfaces before executing scp_Init(), unless otherwise noted.
 *
 * After scp_Init() completes successfully, the SCP unit is guaranteed to be in
 * a clean, safe state for the application to use.
 *
 * @param[in] flags A bitmask specifying the desired configuration settings for
 *                  the SCP driver.
 *
 * @retval E_SUCCESS                    if initialization was successful.
 *
 * @retval E_SCP_INVALID_FLAGS          if flags is not a valid combination of
 *                                      scp_init_flag_t values.
 *
 * @retval E_NOTSUPPORTED               if the host engine does not contain an
 *                                      SCP unit.
 *
 * @retval E_PERM                       if the SCP unit or IO-PMP registers are
 *                                      blocked in the host engine's device map.
 *
 * @retval E_SCP_INSUFFICIENT_RESOURCES if the application lacks access to
 *                                      secret-index zero (required in secure
 *                                      contexts).
 *
 * @retval E_BUSY                       if the SCP unit is in use and cannot be
 *                                      safely reset.
 *
 * @retval E_INVALID_STATE              if IO-PMP error-capture is disabled for
 *                                      the CPDMA master.
 *
 * @retval E_SCP_INVALID_LOCK_STATE     if access to certain necessary registers
 *                                      is being blocked by SCP lockdown, or the
 *                                      host engine is secure-capable but SCP
 *                                      lockdown is disabled.
 *
 * @retval E_SCP_GENERIC                if an unexpected error occurred.
 */
error_t scp_Init(scp_init_flag_t flags);

/**
 * @brief Clears all data from the SCP unit and disables further processing.
 *
 * Clears all data from the SCP unit and disables further processing of
 * commands. Applications should invoke scp_Shutdown() as soon as SCP support is
 * no longer needed (or at least before exiting).
 *
 * Should an application require SCP functionality again after issuing
 * scp_Shutdown(), it must first invoke scp_Init() to initialize the driver
 * again, unless otherwise noted.
 *
 * @retval E_SUCCESS                    if shutdown was successful.
 *
 * @retval E_SCP_NOT_READY              if the SCP driver has not been
 *                                      initialized.
 *
 * @retval E_SCP_INSUFFICIENT_RESOURCES if the application lacks access to
 *                                      secret-index zero (required in secure
 *                                      contexts).
 *
 * @retval E_BUSY                       if the SCP unit is in use and cannot be
 *                                      safely reset.
 *
 * @retval E_SCP_INVALID_LOCK_STATE     if access to certain necessary registers
 *                                      is being blocked by SCP lockdown.
 *
 * @retval E_SCP_GENERIC                if an unexpected error occurred.
 *
 * @retval E_SCP_NOTHING_TO_DO          if the SCP driver has already been shut
 *                                      down (no-op).
 */
error_t scp_Shutdown(void);

/**
 * @brief Returns the debug status of the SCP unit.
 *
 * This function can be called regardless of whether the SCP driver has been
 * initialized or shut down and is safe to call from direct mode.
 *
 * @retval  true  if the SCP unit is in debug mode.
 * @retval  false if the SCP unit is not in debug mode.
 */
bool scp_Is_Debug(void);

/**
 * @brief Clears any data from the specified SCP GPR.
 *
 * This function is safe to call from direct mode.
 *
 * @pre The target register must be writable and fetchable if called from an
 *      insecure context.
 *
 * @param[in] reg_index The index of the SCP register to clear.
 *
 * @retval E_SUCCESS                    if the register was cleared
 *                                      successfully.
 *
 * @retval E_SCP_NOT_READY              if the SCP driver has not been
 *                                      initialized.
 *
 * @retval E_SCP_INVALID_INDEX          if reg_index is not a valid
 *                                      scp_register_index_t value.
 *
 * @retval E_SCP_INSUFFICIENT_RESOURCES if the application lacks access to
 *                                      secret-index zero (required in secure
 *                                      contexts).
 *
 * @retval E_SCP_GENERIC                if an unexpected error occurred.
 */
error_t scp_Clear_Reg(scp_register_index_t reg_index);

/**
 * @brief Clears any data from all SCP GPRs.
 *
 * This function is safe to call from direct mode.
 *
 * @pre All GPRs must be writable and fetchable if called from an insecure
 *      context.
 *
 * @retval E_SUCCESS                    if the registers were cleared
 *                                      successfully.
 *
 * @retval E_SCP_NOT_READY              if the SCP driver has not been
 *                                      initialized.
 *
 * @retval E_SCP_INSUFFICIENT_RESOURCES if the application lacks access to
 *                                      secret-index zero (required in secure
 *                                      contexts).
 *
 * @retval E_SCP_GENERIC                if an unexpected error occurred.
 */
error_t scp_Clear_All(void);

/**
 * @brief Configures the shortcut-DMA path for transfers to external memory.
 *
 * Configures the shortcut path to be used by applicable interfaces when
 * transferring data to external memory. Applications must call
 * scp_Configure_Shortcut() (or the SCP_EXTERNAL_DEST() helper macro) before
 * passing external destination buffers to any SCP interfaces.
 *
 * Once configured, the selected DMA aperture will be used for all future
 * shortcut transfers until reconfigured by a subsequent call to
 * scp_Configure_Shortcut().
 *
 * Note that the application is responsible for ensuring that the provided DMA
 * aperture has itself been properly configured before use. Additionally,
 * shortcut-DMA support must be enabled during build in order to leverage this
 * feature.
 *
 * @param[in] dma_index The index of the DMA aperture to be used for shortcut
 *                      transfers to external memory.
 *
 * @retval E_SUCCESS                    if the shortcut-DMA path was configured
 *                                      successfully.
 *
 * @retval E_SCP_NOT_READY              if the SCP driver has not been
 *                                      initialized.
 *
 * @retval E_SCP_INVALID_INDEX          if dma_index does not refer to a valid
 *                                      DMA aperture.
 *
 * @retval E_SCP_FEATURE_NOT_ENABLED    if the application does not have
 *                                      shortcut-DMA support enabled.
 */
error_t scp_Configure_Shortcut(uint8_t dma_index);

#endif // SCP__SCP_GENERAL_H
