/*******************************************************************************
* DISCLAIMER
* This software is supplied by Renesas Electronics Corporation and is only
* intended for use with Renesas products. No other uses are authorized. This
* software is owned by Renesas Electronics Corporation and is protected under
* all applicable laws, including copyright laws.
* THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING
* THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT
* LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED.
* TO THE MAXIMUM EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS
* ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES SHALL BE LIABLE
* FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR
* ANY REASON RELATED TO THIS SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
* Renesas reserves the right, without notice, to make changes to this software
* and to discontinue the availability of this software. By using this software,
* you agree to the additional terms and conditions found by accessing the
* following link:
* http://www.renesas.com/disclaimer
* Copyright 2018-2023 Renesas Electronics Corporation All rights reserved.
*******************************************************************************/



/*******************************************************************************
 * DESCRIPTION   : GIC Control Function
 ******************************************************************************/
/******************************************************************************
 * @file          gic.h
 * - Version      : 0.04
 * @brief         Controls GIC-600 registers and interrupts.
 * .
 *****************************************************************************/
/******************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 02.08.2022 0.01     First Release
 *         : 20.09.2022 0.02     Set ChildrenAsleep of GICR_WAKER to 1 and end processing
 *         : 31.10.2022 0.03     License notation change.
 *         : 04.04.2023 0.04     Removed stdio.h.
 *****************************************************************************/

#ifndef GIC_H
#define GIC_H

/*******************************************************************************
**                      Include Section                                       **
*******************************************************************************/
#include <stdint.h>

/*******************************************************************************
**                      Version Information                                   **
*******************************************************************************/

/*******************************************************************************
**                      Global Symbols                                        **
*******************************************************************************/
/* GIC base */
#define GICD_BASE    (0xF1000000UL)
#define GICR_BASE    (GICD_BASE + 0x60000U)

/* Generic Interrupt Controller Distributor (GICD) */
#define GICD_CTLR              *((volatile uint32_t *)(GICD_BASE + 0x000U))
#define GICD_IGROUPR(n)        *((volatile uint32_t *)(GICD_BASE + 0x080U + 4U*(n)))
#define GICD_ISENABLER(n)      *((volatile uint32_t *)(GICD_BASE + 0x100U + 4U*(n)))
#define GICD_ICENABLER(n)      *((volatile uint32_t *)(GICD_BASE + 0x180U + 4U*(n)))
#define GICD_ISPENDR(n)        *((volatile uint32_t *)(GICD_BASE + 0x200U + 4U*(n)))
#define GICD_ICPENDR(n)        *((volatile uint32_t *)(GICD_BASE + 0x280U + 4U*(n)))
#define GICD_IPRIORITYR(n)     *((volatile uint32_t *)(GICD_BASE + 0x400U + 4U*(n)))
#define GICD_ICFGR(n)          *((volatile uint32_t *)(GICD_BASE + 0xC00U + 4U*(n)))
#define GICD_IGRPMODR(n)       *((volatile uint32_t *)(GICD_BASE + 0xD00U + 4U*(n)))
#define GICD_IROUTER(n)        *((volatile uint64_t *)(GICD_BASE + 0x6000U + 8U*(n)))

/* Generic Interrupt Controller Redistributor (GICR) */
#define GICR_CTLR              *((volatile uint32_t *)(GICR_BASE + 0x0000U))
#define GICR_WAKER             *((volatile uint32_t *)(GICR_BASE + 0x0014U))
#define GICR_PWRR              *((volatile uint32_t *)(GICR_BASE + 0x0024U))

#define CHILDREN_ASLEEP        (1U << 2U)
#define PROCESSOR_SLEEP        (1U << 1U)
#define RDPOWER_DOWN           (1U << 0U)

/*******************************************************************************
**                      Global Data Types                                     **
*******************************************************************************/

/*******************************************************************************
**                      Function Prototypes                                   **
*******************************************************************************/

/*******************************************************************************
**                      Macro                                                 **
*******************************************************************************/

/*******************************************************************************
 * Definitions for CPU system register interface to GICv3
 ******************************************************************************/
/* ICC_SRE_EL3 */
#define ICC_SRE_EN_BIT     (8U)
#define ICC_SRE_DIB_BIT    (4U)
#define ICC_SRE_DFB_BIT    (2U)
#define ICC_SRE_SRE_BIT    (1U)

/* SCR_EL3 */
#define SCR_NS_BIT         (1U)

/* Affinity Leve mask value */
#define AFFINITY0_MASK     (0xFFU)
#define AFFINITY1_MASK     (0xFF00U)
#define AFFINITY2_MASK     (0xFF0000U)
#define AFFINITY3_MASK     (0xFF00000000U)
#define IRM_OFF            (0x80000000U)

/* Get ICC_IAR0 */
static inline uint64_t get_ICC_IAR0(void)
{
    uint64_t value = 0U;
    __asm__ volatile("mrs %0, S3_0_c12_c8_0" : "=r" (value));
    return value;
}

/* Set ICC_PMR */
static inline void set_ICC_PMR(uint64_t value)
{
    __asm__ volatile ("msr S3_0_C4_C6_0, %0" :: "r" (value));
}

/* Set ICC_IGRPEN0 */
static inline void set_ICC_IGRPEN0(uint64_t value)
{
    __asm__ volatile ("msr S3_0_c12_c12_6, %0" :: "r" (value));
}

/* Set ICC_SRE_EL1 */
static inline void set_ICC_SRE_EL1(uint64_t value)
{
    __asm__ volatile ("msr S3_0_C12_C12_5, %0" :: "r" (value));
}

/* Get ICC_SRE_EL3 */
static inline uint64_t get_ICC_SRE_EL3(void)
{
    uint64_t value = 0U;
    __asm__ volatile("mrs %0, S3_6_C12_C12_5" : "=r" (value));
    return value;
}

/* Set ICC_SRE_EL3 */
static inline void set_ICC_SRE_EL3(uint64_t value)
{
    __asm__ volatile ("msr S3_6_C12_C12_5, %0" :: "r" (value));
}

/* Get MPIDR_EL1 */
static inline uint64_t get_MPIDR_EL1(void)
{
    uint64_t value = 0U;
    __asm__ volatile("mrs %0, mpidr_el1" : "=r" (value));
    return value;
}

/* ISB */
static inline void GIC_isb(void)
{
    __asm__ volatile ("isb");
}

/*  Enable the interrupt distributor using the GIC's CTLR register */
static inline void GIC_EnableDistributor(void)
{
    GICD_CTLR |= 0x31U;
}

/* Disable the interrupt distributor using the GIC's CTLR register */
static inline void GIC_DisableDistributor(void)
{
    GICD_CTLR &= 0xFFFFFFFEU;
}

/* Set the interrupt enable from the GIC's ISENABLER register */
static inline void GIC_SetEnable(uint32_t intid, uint32_t value)
{
    uint32_t reg     = GICD_ISENABLER(intid / 32U);
    uint32_t shift   = (intid % 32U);

    reg &= (~(1U          << shift));
    reg |= ( (value & 1U) << shift);

    GICD_ISENABLER(intid / 32U) = reg;
}

/* Set the interrupt disable from the GIC's ICENABLER register */
static inline void GIC_SetClearEnable(uint32_t intid, uint32_t value)
{
    uint32_t reg     = GICD_ICENABLER(intid / 32U);
    uint32_t shift   = (intid % 32U);

    reg &= (~(1U          << shift));
    reg |= ( (value & 1U) << shift);

    GICD_ICENABLER(intid / 32U) = reg;
}

/* Sets the interrupt configuration using GIC's ICFGR register */
static inline void GIC_SetConfiguration(uint32_t intid, uint32_t int_config)
{
    uint32_t icfgr = GICD_ICFGR(intid / 16U);
    uint32_t shift = (intid % 16U) << 1U;

    icfgr &= (~(3U         << shift));
    icfgr |= (  int_config << shift);

    GICD_ICFGR(intid / 16U) = icfgr;
}

/* Set the priority for the given interrupt in the GIC's IPRIORITYR register */
static inline void GIC_SetPriority(uint32_t intid, uint32_t priority)
{
    uint32_t mask  = GICD_IPRIORITYR(intid / 4U);
	uint32_t shift = ((intid % 4U) * 8U);

    mask &= (~(0xFFU          << shift));
    mask |= ( (priority & 0xFFU) << shift);

    GICD_IPRIORITYR(intid / 4U) = mask;
}

/* Set the interrupt group from the GIC's IGROUPR register */
static inline void GIC_SetGroup(uint32_t intid, uint32_t group)
{
    uint32_t igroupr = GICD_IGROUPR(intid / 32U);
    uint32_t shift   = (intid % 32U);

    igroupr &= (~(1U          << shift));
    igroupr |= ( (group & 1U) << shift);

    GICD_IGROUPR(intid / 32U) = igroupr;
}

/* Set the interrupt group from the GIC's IGRPMODR register */
static inline void GIC_SetGrpMode(uint32_t intid, uint32_t mode)
{
    uint32_t imode   = GICD_IGRPMODR(intid / 32U);
    uint32_t shift   = (intid % 32U);

    imode &= (~(1U          << shift));
    imode |= ( (mode & 1U) << shift);

    GICD_IGRPMODR(intid / 32U) = imode;
}

/* Set the interrupt routing from the GIC's IROUTER register */
static inline void GIC_SetRouter(uint32_t intid)
{
    uint64_t affinity = 0U;

    /* Get Affinity level */
    affinity = get_MPIDR_EL1();
    affinity &= (AFFINITY0_MASK | AFFINITY1_MASK | AFFINITY2_MASK | AFFINITY3_MASK);
    /* Interrupt routing mode bit OFF */
	affinity &= (~(IRM_OFF));

    GICD_IROUTER(intid) = affinity;
}

/* Get power register value from the GIC's GICR_PWRR register */
static inline uint32_t GIC_Getpwwr(void)
{
    return (GICR_PWRR);
}

/* Set power register value from the GIC's GICR_PWRR register */
static inline void GIC_Setpwwr(uint32_t set_value)
{
    GICR_PWRR = set_value;
}

/* Get power management cotrol register from the GIC's GICR_WAKER register */
static inline uint32_t GIC_Getwaker(void)
{
    return (GICR_WAKER);
}

/* Set power management cotrol register from the GIC's GICR_WAKER register */
static inline void GIC_Setwaker(uint32_t set_value)
{
    GICR_WAKER = set_value;
}

/* Enables the given interrupt using GIC's ISENABLER register */
static inline void GIC_EnableFIQ(uint32_t intid)
{

    /* Disable interrupt forwarding */
    GIC_DisableDistributor();

    /* Set level-sensitive */
    GIC_SetConfiguration(intid, 0U);

    /* Set priority */
    GIC_SetPriority(intid, 0U);

    /* Set group 0 (secure) */
    GIC_SetGroup(intid, 0U);

    /* Set group 0 (secure) */
    GIC_SetGrpMode(intid, 0U);

    /* Enable distributor */
    GIC_EnableDistributor();

    /* Enable the SPI interrupt */
    GIC_SetEnable(intid, 1U);

    /* Set the interrupt routing */
    GIC_SetRouter(intid);
}

/*  Enable the interrupt redistributor wakeup */
static inline void GIC_WakeupRedistributor(void)
{
    uint32_t    get_value = 0U;
    uint32_t    set_value = 0U;

    get_value = GIC_Getpwwr();
	set_value = get_value & ~(RDPOWER_DOWN);
    GIC_Setpwwr(set_value);

    get_value = GIC_Getwaker();
    set_value = get_value & ~(PROCESSOR_SLEEP);
    GIC_Setwaker(set_value);

    do 
    {
        get_value = GIC_Getwaker();
    }while((get_value & CHILDREN_ASLEEP) == CHILDREN_ASLEEP);
}

/* Enable the CPU's interrupt interface */
static inline void GIC_EnableInterface(void)
{
    uint64_t reg = 0U;
    uint64_t icc_sre_el3 = 0U;

    /* Disable the legacy interrupt bypass */
    icc_sre_el3 = ICC_SRE_DIB_BIT | ICC_SRE_DFB_BIT | ICC_SRE_EN_BIT | ICC_SRE_SRE_BIT;

    reg = get_ICC_SRE_EL3();
    set_ICC_SRE_EL3(reg | icc_sre_el3);

    set_ICC_SRE_EL1(ICC_SRE_SRE_BIT);
    GIC_isb();

    set_ICC_IGRPEN0(1U); /* enable interface grp0 */
    GIC_isb();
}

/* Disable the CPU's interrupt interface */
static inline void GIC_DisableInterface(uint32_t intid)
{
    uint32_t    get_value = 0U;
    uint32_t    set_value = 0U;

    /* Clear Enable the SPI interrupt */
    GIC_SetClearEnable(intid, 1U);

    /* Set ChildrenAsleep of GICR_WAKER to 1 and end processing */
    get_value = GIC_Getwaker();
    set_value = get_value | PROCESSOR_SLEEP;
    GIC_Setwaker(set_value);

    do
    {
        get_value = GIC_Getwaker();
    }while((get_value & CHILDREN_ASLEEP) != CHILDREN_ASLEEP);

}

/* Read the CPU's IAR register */
static inline uint32_t GIC_AcknowledgePending(void)
{
    return (uint32_t)(get_ICC_IAR0());
}

/* Set the interrupt priority mask using CPU's PMR register */
static inline void GIC_SetInterfacePriorityMask(uint64_t priority)
{
    /* Specify F8. 32 priority levels are bit0-2 invalid */
    set_ICC_PMR(priority << 3U);
}

/* Initialize and enable the GIC */
static inline void GIC_Enable(void)
{
    GIC_WakeupRedistributor();
    /* Enable interface */
    GIC_EnableInterface();
    /* Set priority mask */
    GIC_SetInterfacePriorityMask(0xFFUL);
}

/*******************************************************************************
**                      Function                                              **
*******************************************************************************/
/* Interrupt configuration */
#define Interrupt_Config(void)              GIC_Enable(void)

/* Enable */
#define Interrupt_Enable(intid)             GIC_EnableFIQ((uint32_t)intid)

/* Disable */
#define Interrupt_Disable(intid)            GIC_DisableInterface((uint32_t)intid)

#endif /* GIC_H */
/*******************************************************************************
**                      End of File                                           **
*******************************************************************************/
