/*
 * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved.
 * Copyright (c) 2015-2021, Renesas Electronics Corporation. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

/******************************************************************************
 * @file          gic_v2.c
 * - Version      : 0.01
 * @brief         Read / Write GIC register.
 * .
 *****************************************************************************/
/******************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 29.05.2020 0.01     First Release
 *****************************************************************************/

#include <assert.h>
#include <gic_v2.h>
#include <mmio.h>

/*******************************************************************************
 * GIC Distributor interface accessors for reading entire registers
 ******************************************************************************/

unsigned int gicd_read_igroupr(uintptr_t base, unsigned int id)
{
    unsigned n = id >> IGROUPR_SHIFT;
    return mmio_read_32(base + GICD_IGROUPR + (n << 2));
}
/* End of function gicd_read_igroupr */

unsigned int gicd_read_isenabler(uintptr_t base, unsigned int id)
{
    unsigned n = id >> ISENABLER_SHIFT;
    return mmio_read_32(base + GICD_ISENABLER + (n << 2));
}
/* End of function gicd_read_isenabler */

unsigned int gicd_read_icenabler(uintptr_t base, unsigned int id)
{
    unsigned n = id >> ICENABLER_SHIFT;
    return mmio_read_32(base + GICD_ICENABLER + (n << 2));
}
/* End of function gicd_read_icenabler */

unsigned int gicd_read_ispendr(uintptr_t base, unsigned int id)
{
    unsigned n = id >> ISPENDR_SHIFT;
    return mmio_read_32(base + GICD_ISPENDR + (n << 2));
}
/* End of function gicd_read_ispendr */

unsigned int gicd_read_icpendr(uintptr_t base, unsigned int id)
{
    unsigned n = id >> ICPENDR_SHIFT;
    return mmio_read_32(base + GICD_ICPENDR + (n << 2));
}
/* End of function gicd_read_icpendr */

unsigned int gicd_read_isactiver(uintptr_t base, unsigned int id)
{
    unsigned n = id >> ISACTIVER_SHIFT;
    return mmio_read_32(base + GICD_ISACTIVER + (n << 2));
}
/* End of function gicd_read_isactiver */

unsigned int gicd_read_icactiver(uintptr_t base, unsigned int id)
{
    unsigned n = id >> ICACTIVER_SHIFT;
    return mmio_read_32(base + GICD_ICACTIVER + (n << 2));
}
/* End of function gicd_read_icactiver */

unsigned int gicd_read_ipriorityr(uintptr_t base, unsigned int id)
{
    unsigned n = id >> IPRIORITYR_SHIFT;
    return mmio_read_32(base + GICD_IPRIORITYR + (n << 2));
}
/* End of function gicd_read_ipriorityr */

unsigned int gicd_read_itargetsr(uintptr_t base, unsigned int id)
{
    unsigned n = id >> ITARGETSR_SHIFT;
    return mmio_read_32(base + GICD_ITARGETSR + (n << 2));
}
/* End of function gicd_read_itargetsr */

unsigned int gicd_read_icfgr(uintptr_t base, unsigned int id)
{
    unsigned n = id >> ICFGR_SHIFT;
    return mmio_read_32(base + GICD_ICFGR + (n << 2));
}
/* End of function gicd_read_icfgr */

unsigned int gicd_read_cpendsgir(uintptr_t base, unsigned int id)
{
    unsigned n = id >> CPENDSGIR_SHIFT;
    return mmio_read_32(base + GICD_CPENDSGIR + (n << 2));
}
/* End of function gicd_read_cpendsgir */

unsigned int gicd_read_spendsgir(uintptr_t base, unsigned int id)
{
    unsigned n = id >> SPENDSGIR_SHIFT;
    return mmio_read_32(base + GICD_SPENDSGIR + (n << 2));
}
/* End of function gicd_read_spendsgir */

/*******************************************************************************
 * GIC Distributor interface accessors for writing entire registers
 ******************************************************************************/

void gicd_write_igroupr(uintptr_t base, unsigned int id, unsigned int val)
{
    unsigned n = id >> IGROUPR_SHIFT;
    mmio_write_32(base + GICD_IGROUPR + (n << 2), val);
}
/* End of function gicd_write_igroupr */

void gicd_write_isenabler(uintptr_t base, unsigned int id, unsigned int val)
{
    unsigned n = id >> ISENABLER_SHIFT;
    mmio_write_32(base + GICD_ISENABLER + (n << 2), val);
}
/* End of function gicd_write_isenabler */

void gicd_write_icenabler(uintptr_t base, unsigned int id, unsigned int val)
{
    unsigned n = id >> ICENABLER_SHIFT;
    mmio_write_32(base + GICD_ICENABLER + (n << 2), val);
}
/* End of function gicd_write_icenabler */

void gicd_write_ispendr(uintptr_t base, unsigned int id, unsigned int val)
{
    unsigned n = id >> ISPENDR_SHIFT;
    mmio_write_32(base + GICD_ISPENDR + (n << 2), val);
}
/* End of function gicd_write_ispendr */

void gicd_write_icpendr(uintptr_t base, unsigned int id, unsigned int val)
{
    unsigned n = id >> ICPENDR_SHIFT;
    mmio_write_32(base + GICD_ICPENDR + (n << 2), val);
}
/* End of function gicd_write_icpendr */

void gicd_write_isactiver(uintptr_t base, unsigned int id, unsigned int val)
{
    unsigned n = id >> ISACTIVER_SHIFT;
    mmio_write_32(base + GICD_ISACTIVER + (n << 2), val);
}
/* End of function gicd_write_isactiver */

void gicd_write_icactiver(uintptr_t base, unsigned int id, unsigned int val)
{
    unsigned n = id >> ICACTIVER_SHIFT;
    mmio_write_32(base + GICD_ICACTIVER + (n << 2), val);
}
/* End of function gicd_write_icactiver */

void gicd_write_ipriorityr(uintptr_t base, unsigned int id, unsigned int val)
{
    unsigned n = id >> IPRIORITYR_SHIFT;
    mmio_write_32(base + GICD_IPRIORITYR + (n << 2), val);
}
/* End of function gicd_write_ipriorityr */

void gicd_write_itargetsr(uintptr_t base, unsigned int id, unsigned int val)
{
    unsigned n = id >> ITARGETSR_SHIFT;
    mmio_write_32(base + GICD_ITARGETSR + (n << 2), val);
}
/* End of function gicd_write_itargetsr */

void gicd_write_icfgr(uintptr_t base, unsigned int id, unsigned int val)
{
    unsigned n = id >> ICFGR_SHIFT;
    mmio_write_32(base + GICD_ICFGR + (n << 2), val);
}
/* End of function gicd_write_icfgr */

void gicd_write_cpendsgir(uintptr_t base, unsigned int id, unsigned int val)
{
    unsigned n = id >> CPENDSGIR_SHIFT;
    mmio_write_32(base + GICD_CPENDSGIR + (n << 2), val);
}
/* End of function gicd_write_cpendsgir */

void gicd_write_spendsgir(uintptr_t base, unsigned int id, unsigned int val)
{
    unsigned n = id >> SPENDSGIR_SHIFT;
    mmio_write_32(base + GICD_SPENDSGIR + (n << 2), val);
}
/* End of function gicd_write_spendsgir */

/*******************************************************************************
 * GIC Distributor interface accessors for individual interrupt manipulation
 ******************************************************************************/
unsigned int gicd_get_igroupr(uintptr_t base, unsigned int id)
{
    unsigned bit_num = id & ((1 << IGROUPR_SHIFT) - 1);
    unsigned int reg_val = gicd_read_igroupr(base, id);

    return (reg_val >> bit_num) & 0x1;
}
/* End of function gicd_get_igroupr */

void gicd_set_igroupr(uintptr_t base, unsigned int id)
{
    unsigned bit_num = id & ((1 << IGROUPR_SHIFT) - 1);
    unsigned int reg_val = gicd_read_igroupr(base, id);

    gicd_write_igroupr(base, id, reg_val | (1 << bit_num));
}
/* End of function gicd_set_igroupr */

void gicd_clr_igroupr(uintptr_t base, unsigned int id)
{
    unsigned bit_num = id & ((1 << IGROUPR_SHIFT) - 1);
    unsigned int reg_val = gicd_read_igroupr(base, id);

    gicd_write_igroupr(base, id, reg_val & ~(1 << bit_num));
}
/* End of function gicd_clr_igroupr */

void gicd_set_isenabler(uintptr_t base, unsigned int id)
{
    unsigned bit_num = id & ((1 << ISENABLER_SHIFT) - 1);

    gicd_write_isenabler(base, id, (1 << bit_num));
}
/* End of function gicd_set_isenabler */

void gicd_set_icenabler(uintptr_t base, unsigned int id)
{
    unsigned bit_num = id & ((1 << ICENABLER_SHIFT) - 1);

    gicd_write_icenabler(base, id, (1 << bit_num));
}
/* End of function gicd_set_icenabler */

void gicd_set_ispendr(uintptr_t base, unsigned int id)
{
    unsigned bit_num = id & ((1 << ISPENDR_SHIFT) - 1);

    gicd_write_ispendr(base, id, (1 << bit_num));
}
/* End of function gicd_set_ispendr */

void gicd_set_icpendr(uintptr_t base, unsigned int id)
{
    unsigned bit_num = id & ((1 << ICPENDR_SHIFT) - 1);

    gicd_write_icpendr(base, id, (1 << bit_num));
}
/* End of function gicd_set_icpendr */

void gicd_set_isactiver(uintptr_t base, unsigned int id)
{
    unsigned bit_num = id & ((1 << ISACTIVER_SHIFT) - 1);

    gicd_write_isactiver(base, id, (1 << bit_num));
}
/* End of function gicd_set_isactiver */

void gicd_set_icactiver(uintptr_t base, unsigned int id)
{
    unsigned bit_num = id & ((1 << ICACTIVER_SHIFT) - 1);

    gicd_write_icactiver(base, id, (1 << bit_num));
}
/* End of function gicd_set_icactiver */

/*
 * Make sure that the interrupt's group is set before expecting
 * this function to do its job correctly.
 */
void gicd_set_ipriorityr(uintptr_t base, unsigned int id, unsigned int pri)
{
    unsigned int reg = base + GICD_IPRIORITYR + (id & ~3);
    unsigned int shift = (id & 3) << 3;
    unsigned int reg_val = mmio_read_32(reg);

    /*
     * Enforce ARM recommendation to manage priority values such
     * that group1 interrupts always have a lower priority than
     * group0 interrupts.
     * Note, lower numerical values are higher priorities so the comparison
     * checks below are reversed from what might be expected.
     */
    assert(gicd_get_igroupr(base, id) == GRP1 ?
        pri >= GIC_HIGHEST_NS_PRIORITY &&
            pri <= GIC_LOWEST_NS_PRIORITY :
        pri >= GIC_HIGHEST_SEC_PRIORITY &&
            pri <= GIC_LOWEST_SEC_PRIORITY);

    reg_val &= ~(GIC_PRI_MASK << shift);
    reg_val |= (pri & GIC_PRI_MASK) << shift;
    mmio_write_32(reg, reg_val);
}
/* End of function gicd_set_ipriorityr */

void gicd_set_itargetsr(uintptr_t base, unsigned int id, unsigned int target)
{
    unsigned byte_off = id & ((1 << ITARGETSR_SHIFT) - 1);
    unsigned int reg_val = gicd_read_itargetsr(base, id);

    gicd_write_itargetsr(base, id, reg_val | (target << (byte_off << 3)));
}
/* End of function gicd_set_itargetsr */

