/*
 * SPDX-FileCopyrightText: Copyright (c) 2018-2023 NVIDIA CORPORATION &
 * AFFILIATES. All rights reserved.
 * SPDX-License-Identifier: LicenseRef-NvidiaProprietary
 *
 * NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
 * property and proprietary rights in and to this material, related
 * documentation and any modifications thereto. Any use, reproduction,
 * disclosure or distribution of this material and related documentation
 * without an express license agreement from NVIDIA CORPORATION or
 * its affiliates is strictly prohibited.
 */

#ifndef VIC__VIC_ASM_H
#define VIC__VIC_ASM_H
#define FSP__VIC__VIC_ASM_H                             1

/* Compiler headers */

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

/* Hardware headers */

/* Late FSP headers */
#include <ospl/rtos-port-asm.h>
#include <hw-config/vic-irqs.h>

/* Module-specific FSP headers */
#include <vic/tegra-vic.h>

/*
 * 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.
 */
CT_ASSERT(FSP__VIC__TEGRA_VIC_H, "Header file missing or invalid.")
CT_ASSERT(FSP__OSPL__RTOS_PORT_ASM_H, "Header file missing or invalid.")

#ifdef __ASSEMBLER__
/*
 * Macros used for generating code for interrupt handlers.
 */

/*
 * External references used by these macros
 */
.extern bad_irq
.type bad_irq, STT_FUNC

.extern vic0_isr_end
.type vic0_isr_end, STT_FUNC

.extern vic1_isr_end
.type vic1_isr_end, STT_FUNC

#if defined(MAX_VIC_CONTROLLER) && (MAX_VIC_CONTROLLER == 4)
.extern vic2_isr_end
.type vic2_isr_end, STT_FUNC

.extern vic3_isr_end
.type vic3_isr_end, STT_FUNC
#endif

.extern vic_base_addr
.type vic_base_addr, STT_OBJECT

/**
 * @brief bad_irq_handler macro
 *
 * Defines the handler for an interrupt vector for which there is
 * no other handler.  Calls the function:
 *      void bad_irq(uint32_t vector)
 *
 * @param[in] label  Label to be applied to the code
 * @param[in] vector Tegra interrupt vector. It is a number that encodes both
 *                   the VIC instance ID and local IRQ number
 *                    [0 - MAX_VIC_INTERRUPT_VECTOR]
 *
 */
.macro  bad_irq_handler label, vector
        .global         \label
        .balign         4
\label :
        rtosIRQ_ENTRY
        MOV     R0, #\vector
        BLX     bad_irq
        B       .               // not reached
.endm

/**
 * @brief Interrupt handler with address as input
 *
 * Interrupt handlers that take an address as a parameter to be passed
 * to a C function with the prototype:
 *     void func(void *addr)
 *
 * @param[in] label  Label to be applied to the code
 * @param[in] addr   Address to be passed to the function
 * @param[in] func   Function to be called
 * @param[in] vector Tegra interrupt vector. It is a number that encodes both
 *                   the VIC instance ID and local IRQ number
 *                    [0 - MAX_VIC_INTERRUPT_VECTOR]
 */
.macro  isr_addr         label, addr, func, vector
        .global         \label
        .balign         4
\label :
        rtosIRQ_ENTRY
        LDR     R0, =\addr
        BLX     \func
#if defined(MAX_VIC_CONTROLLER) && (MAX_VIC_CONTROLLER == 4)
.if TEGRA_INTERRUPT_TO_VIC(\vector) == 3
        B       vic3_isr_end
.elseif TEGRA_INTERRUPT_TO_VIC(\vector) == 2
        B       vic2_isr_end
.elseif TEGRA_INTERRUPT_TO_VIC(\vector) == 1
        B       vic1_isr_end
.else
        B       vic0_isr_end
.endif
#else
.if TEGRA_INTERRUPT_TO_VIC(\vector) == 1
        B       vic1_isr_end
.else
        B       vic0_isr_end
.endif
#endif
.endm

/**
 * @brief Interrupt handler with value as an input to the function
 * Interrupt handlers that take a value as a parameter to be passed
 * to a C function with the prototype:
 *     void func(uint32_t value)
 *
 * @param[in] label  Label to be applied to the code
 * @param[in] value  16-bit constant value to be passed to the function
 * @param[in] func   Function to be called
 * @param[in] vector Tegra interrupt vector. It is a number that encodes both
 *                   the VIC instance ID and local IRQ number
 *                    [0 - MAX_VIC_INTERRUPT_VECTOR]
 */
.macro  isr_value       label, value, func, vector
        .global         \label
        .balign         4
\label :
        rtosIRQ_ENTRY
        MOVW    R0, #\value
        MOVT    R0, #0
        BLX     \func
#if defined(MAX_VIC_CONTROLLER) && (MAX_VIC_CONTROLLER == 4)
.if TEGRA_INTERRUPT_TO_VIC(\vector) == 3
        B       vic3_isr_end
.elseif TEGRA_INTERRUPT_TO_VIC(\vector) == 2
        B       vic2_isr_end
.elseif TEGRA_INTERRUPT_TO_VIC(\vector) == 1
        B       vic1_isr_end
.else
        B       vic0_isr_end
.endif
#else
.if TEGRA_INTERRUPT_TO_VIC(\vector) == 1
        B       vic1_isr_end
.else
        B       vic0_isr_end
.endif
#endif
.endm

/**
 * @brief ACK_VIC0_IRQ macro
 *
 * Acknowledges an interrupt originating from VIC0.
 *
 * Usage:
 *      ACK_VIC0_IRQ label
 *
 * Registers Modified:
 *      R0
 *
 * @param[in] label Label to be applied to the code
 */
.macro  ACK_VIC0_IRQ    label
        .global         \label
        .balign         4
\label :
        LDR     R0, =vic_base_addr
        LDR     R0, [R0]
        STR     R0, [R0, #APS_VIC_VICADDRESS_0]
.endm

/**
 * @brief ACK_VIC1_IRQ macro
 *
 * Acknowledges an interrupt originating from VIC1.
 *
 * Usage:
 *      ACK_VIC1_IRQ label
 *
 * Registers Modified:
 *      R0
 *      R1
 *
 * @param[in] label Label to be applied to the code
 */
.macro  ACK_VIC1_IRQ    label
        .global         \label
        .balign         4
\label :
        LDR     R0, =vic_base_addr
        LDR     R1, [R0, #4]
        STR     R0, [R1, #APS_VIC_VICADDRESS_0]
        LDR     R1, [R0]
        STR     R0, [R1, #APS_VIC_VICADDRESS_0]
.endm


#if defined(MAX_VIC_CONTROLLER) && (MAX_VIC_CONTROLLER == 4)
/**
 * @brief ACK_VIC2_IRQ macro
 *
 * Acknowledges an interrupt originating from VIC1.
 *
 * Usage:
 *      ACK_VIC2_IRQ label
 *
 * Registers Modified:
 *      R0
 *      R1
 *
 * @param[in] label Label to be applied to the code
 */
.macro  ACK_VIC2_IRQ    label
        .global         \label
        .balign         4
\label :
        LDR     R0, =vic_base_addr
        LDR     R1, [R0, #8]
        STR     R0, [R1, #APS_VIC_VICADDRESS_0]
        LDR     R1, [R0, #4]
        STR     R0, [R1, #APS_VIC_VICADDRESS_0]
        LDR     R1, [R0]
        STR     R0, [R1, #APS_VIC_VICADDRESS_0]
.endm

/**
 * @brief ACK_VIC3_IRQ macro
 *
 * Acknowledges an interrupt originating from VIC1.
 *
 * Usage:
 *      ACK_VIC3_IRQ label
 *
 * Registers Modified:
 *      R0
 *      R1
 *
 * @param[in] label Label to be applied to the code
 */
.macro  ACK_VIC3_IRQ    label
        .global         \label
        .balign         4
\label :
        LDR     R0, =vic_base_addr
        LDR     R1, [R0, #12]
        STR     R0, [R1, #APS_VIC_VICADDRESS_0]
        LDR     R1, [R0, #8]
        STR     R0, [R1, #APS_VIC_VICADDRESS_0]
        LDR     R1, [R0, #4]
        STR     R0, [R1, #APS_VIC_VICADDRESS_0]
        LDR     R1, [R0]
        STR     R0, [R1, #APS_VIC_VICADDRESS_0]
.endm

#endif

/**
 * @brief isr_map_entry macro
 *
 * Creates an ISR map entry
 *
 * Usage:
 *      isr_map_entry vector, isr_fn
 *
 * @param[in] vector Tegra interrupt vector. It is a number that encodes both
 *                   the VIC instance ID and local IRQ number
 *                    [0 - MAX_VIC_INTERRUPT_VECTOR]
 * @param[in] isr_fn Assembler ISR function that will be dispatched when an
 *                   interrupt occurs
 *
 * @pre
 * - Interrupts set using static configuration
 *
 */
.macro  isr_map_entry   vector, isr_fn
        .balign         8
        .long   \vector
        .long   \isr_fn
.endm

#endif // __ASSEMBLER__
#endif
