/*
 * Copyright (c) 2022, NVIDIA CORPORATION.  All rights reserved.
 *
 * NVIDIA CORPORATION and its licensors retain all intellectual property
 * and proprietary rights in and to this software, related documentation
 * and any modifications thereto.  Any use, reproduction, disclosure or
 * distribution of this software and related documentation without an express
 * license agreement from NVIDIA CORPORATION is strictly prohibited.
 */

/* Compiler headers */
#include <stdbool.h>                      // for true
#include <stdint.h>                       // for uint16_t, uint32_t, uint8_t
#include <string.h>                       // for NULL, memcpy, size_t

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

/* Hardware headers */

/* Late FSP headers */
#include <error/common-errors.h>          // for E_INVALID_PARAM, E_SUCCESS...

/* Module-specific FSP headers */
#include <i2c/i2c-errors.h>               // for E_I2C_INVALID_OPS, FSP__I2C...
#include <i2c/i2c-config.h>               // for i2c_handle, ...
#include <i2c/i2c-hw-ops.h>               // for i2c_hw_ops, FSP__I2C__I2C_H...
#include <i2c/i2c-tegra.h>                // for i2c_xfer_msg, tegra_i2c_init
#include <i2c/i2c-tegra-async.h>          // for tegra_i2c_get_xfer_state
#include <i2c/i2c.h>                      // for i2c_handle, FSP__I2C__I2C_H
#include <i2c/i2c-async.h>                // for i2c_start_async_xfer, i2c_f...
#include <soc-common/i2c-dependencies.h>  // for appfw_mutex_acquire, appfw_...

/*
 * Compile-time check for FSP header files
 *   Each FSP header file contains a signature unique to that file, and the
 *   FSP project.  The CT_ASSERT 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.
 */
START_RFD_BLOCK(MISRA, DEVIATE, Rule_8_7, "Approval: Bug 200531999, DR: SWE-FSP-015-SWSADR.docx",
                MISRA, DEVIATE, Rule_15_1, "Approval: Bug 3176459, DR: SWE-FSP-028-SWSADR.docx",
                MISRA, DEVIATE, Rule_1_2, "Approval: Bug 200531996, DR: SWE-FSP-009-SWSADR.docx",
                MISRA, DEVIATE, Rule_2_3, "Approval:  On-file, DR: SWE-FSP-010-SWSADR.docx",
                MISRA, DEVIATE, Directive_4_9, "Approval: Bug 200531995, DR: SWE-FSP-012-SWSADR.docx")
CT_ASSERT(FSP__ERROR__COMMON_ERRORS_H, "Header file missing or invalid.")
CT_ASSERT(FSP__I2C__I2C_ERRORS_H, "Header file missing or invalid.")
CT_ASSERT(FSP__I2C__I2C_CONFIG_H, "Header file missing or invalid.")
CT_ASSERT(FSP__I2C__I2C_HW_OPS_H, "Header file missing or invalid.")
CT_ASSERT(FSP__I2C__I2C_TEGRA_H, "Header file missing or invalid.")
CT_ASSERT(FSP__I2C__I2C_TEGRA_ASYNC_H, "Header file missing or invalid.")
CT_ASSERT(FSP__I2C__I2C_H, "Header file missing or invalid.")
CT_ASSERT(FSP__I2C__I2C_ASYNC_H, "Header file missing or invalid.")
CT_ASSERT(FSP__SOC_COMMON__I2C_DEPENDENCIES_H, "Header file missing or invalid.")
END_RFD_BLOCK(MISRA, DEVIATE, Rule_2_3, "Approval:  On-file, DR: SWE-FSP-010-SWSADR.docx")

/* Custom Defines */
#define I2C_MUTEX_TIMEOUT_US           1000U

/* Stuct Definitions and Declarations */

/* API Definitions */

error_t i2c_start_async_xfer(const struct i2c_handle *hi2c,
                            struct i2c_xfer_msg *msgs, uint32_t num_msgs,
                            void *client_data, i2c_async_callback async_cb)
{
    error_t ret;
    error_t tmp;
    uint32_t i;

    if ((hi2c == NULL) ||
        (msgs == NULL) ||
        (async_cb == NULL) ||
        ((num_msgs == 0UL))) {
        INLINE_RFD(MISRA, FP, Rule_10_8, "Approval: JIRA TID-412, DR: SWE-FSP-021-SWSADR.docx");
        ret = E_INVALID_PARAM;
        i2c_pr_error("%s: invalid (%s) params provided, ret - 0x%x\n", __func__,
                    ((hi2c == NULL) ? "hi2c" :
                     ((pdata == NULL) ? "pdata" :
                      ((async_cb == NULL) ? "async_cb" : "num_data"))),
                     (int32_t)ret);
        goto out;
    }

    for (i = 0U; i < num_msgs; i++) {
        if (msgs[i].buf_len == 0U) {
            INLINE_RFD(MISRA, FP, Rule_10_8, "Approval: JIRA TID-412, DR: SWE-FSP-021-SWSADR.docx");
            ret = E_INVALID_PARAM;
            i2c_pr_error("%s: invalid msg[%lu].buf_len, ret - 0x%x\n", __func__,
                            i, (int32_t)ret);
            goto out;
        }
    }

    ret = appfw_mutex_acquire(hi2c->ctrl_id, I2C_MUTEX_TIMEOUT_US);
    if (ret != E_SUCCESS) {
        i2c_pr_error("%s: failed to acquire mutex, ret - 0x%x\n", __func__,
                        (int32_t)ret);
        goto release;
    }

    if (hi2c->hw_ops->start_async_xfer != NULL) {
        ret = hi2c->hw_ops->start_async_xfer(hi2c->hi2c_tegra, msgs, num_msgs,
                                            client_data, async_cb);
    } else {
        ret = E_I2C_INVALID_OPS;
        i2c_pr_error("%s: invalid hi2c->hw_ops->start_async_xfer, ret - 0x%x\n",
                        __func__, (int32_t)ret);
    }

release:
    /* Release mutex if start transfer failed. */
    if (ret != E_SUCCESS) {
        tmp = appfw_mutex_release(hi2c->ctrl_id);
        if (tmp != E_SUCCESS) {
            i2c_pr_error("%s: failed to release mutex, ret - 0x%x\n", __func__,
                            (int32_t) ret);
        }
    }

out:
    return ret;
}

error_t i2c_finish_async_xfer(const struct i2c_handle *hi2c, error_t xfer_ret)
{
    error_t ret;
    error_t tmp;

    if (hi2c == NULL) {
        INLINE_RFD(MISRA, FP, Rule_10_8, "Approval: JIRA TID-412, DR: SWE-FSP-021-SWSADR.docx");
        ret = E_INVALID_PARAM;
        i2c_pr_error("%s: invalid hi2c, ret - 0x%x\n", __func__,
                        (int32_t)ret);
        goto out;
    }

    if (hi2c->hw_ops->finish_async_xfer != NULL) {
        ret = hi2c->hw_ops->finish_async_xfer(hi2c->hi2c_tegra, xfer_ret);
    } else {
        ret = E_I2C_INVALID_OPS;
        i2c_pr_error("%s: invalid hi2c->hw_ops->finish_async_xfer, ret - 0x%x\n",
                        __func__, (int32_t)ret);
    }

    tmp = appfw_mutex_release(hi2c->ctrl_id);
    if (ret == E_SUCCESS) {
        ret = tmp;
        i2c_pr_error("%s: failed to release mutex, ret - 0x%x\n", __func__,
                (int32_t) ret);
    }

out:
    return ret;
}

error_t i2c_get_xfer_state(const struct i2c_handle *hi2c, uint32_t *state,
                            uint32_t *msgs_xfered)
{
    error_t ret = E_SUCCESS;

    if ((hi2c == NULL) || (state == NULL) || (msgs_xfered == NULL)) {
        INLINE_RFD(MISRA, FP, Rule_10_8, "Approval: JIRA TID-412, DR: SWE-FSP-021-SWSADR.docx");
        ret = E_INVALID_PARAM;
        i2c_pr_error("%s: invalid %s, ret - 0x%x\n", __func__,
                        ((hi2c == NULL) ? "hi2c" :
                         (state == NULL) ? "state" : "msgs_xfered"),
                        (int32_t)ret);
        goto out;
    }

    if (hi2c->hi2c_tegra != NULL) {
        *state = tegra_i2c_get_xfer_state(hi2c->hi2c_tegra, msgs_xfered);
    } else {
        ret = E_INVALID_PARAM;
        i2c_pr_error("%s: invalid hi2c->hi2c_tegra, ret - 0x%x\n", __func__,
                        (int32_t)ret);
    }

out:
    return ret;
}
END_RFD_BLOCK(MISRA, DEVIATE, Rule_8_7, "Approval: Bug 200531999, DR: SWE-FSP-015-SWSADR.docx",
              MISRA, DEVIATE, Rule_15_1, "Approval: Bug 3176459, DR: SWE-FSP-028-SWSADR.docx",
              MISRA, DEVIATE, Rule_1_2, "Approval: Bug 200531996, DR: SWE-FSP-009-SWSADR.docx",
              MISRA, DEVIATE, Directive_4_9, "Approval: Bug 200531995, DR: SWE-FSP-012-SWSADR.docx")
