/*
 * Copyright (c) 2019 Stephanos Ioannidis <root@stephanos.io>
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @file
 * @brief Driver for ARM Generic Interrupt Controller
 *
 * The Generic Interrupt Controller (GIC) is the default interrupt controller
 * for the ARM A and R profile cores.  This driver is used by the ARM arch
 * implementation to handle interrupts.
 */

#ifndef ZEPHYR_INCLUDE_DRIVERS_GIC_H_
#define ZEPHYR_INCLUDE_DRIVERS_GIC_H_

/*
 * GIC Register Interface Base Addresses
 */

#define GIC_DIST_BASE	DT_REG_ADDR_BY_IDX(DT_INST(0, arm_gic), 0)
#define GIC_CPU_BASE	DT_REG_ADDR_BY_IDX(DT_INST(0, arm_gic), 1)

/*
 * GIC Distributor Interface
 */

/*
 * 0x000  Distributor Control Register
 * v1		ICDDCR
 * v2/v3	GICD_CTLR
 */
#define	GICD_CTLR		(GIC_DIST_BASE +   0x0)

/*
 * 0x004  Interrupt Controller Type Register
 * v1		ICDICTR
 * v2/v3	GICD_TYPER
 */
#define	GICD_TYPER		(GIC_DIST_BASE +   0x4)

/*
 * 0x008  Distributor Implementer Identification Register
 * v1		ICDIIDR
 * v2/v3	GICD_IIDR
 */
#define	GICD_IIDR		(GIC_DIST_BASE +   0x8)

/*
 * 0x080  Interrupt Group Registers
 * v1		ICDISRn
 * v2/v3	GICD_IGROUPRn
 */
#define	GICD_IGROUPRn		(GIC_DIST_BASE +  0x80)

/*
 * 0x100  Interrupt Set-Enable Registers
 * v1		ICDISERn
 * v2/v3	GICD_ISENABLERn
 */
#define	GICD_ISENABLERn		(GIC_DIST_BASE + 0x100)

/*
 * 0x180  Interrupt Clear-Enable Registers
 * v1		ICDICERn
 * v2/v3	GICD_ICENABLERn
 */
#define	GICD_ICENABLERn		(GIC_DIST_BASE + 0x180)

/*
 * 0x200  Interrupt Set-Pending Registers
 * v1		ICDISPRn
 * v2/v3	GICD_ISPENDRn
 */
#define	GICD_ISPENDRn		(GIC_DIST_BASE + 0x200)

/*
 * 0x280  Interrupt Clear-Pending Registers
 * v1		ICDICPRn
 * v2/v3	GICD_ICPENDRn
 */
#define	GICD_ICPENDRn		(GIC_DIST_BASE + 0x280)

/*
 * 0x300  Interrupt Set-Active Registers
 * v1		ICDABRn
 * v2/v3	GICD_ISACTIVERn
 */
#define	GICD_ISACTIVERn		(GIC_DIST_BASE + 0x300)

#if CONFIG_GIC_VER >= 2
/*
 * 0x380  Interrupt Clear-Active Registers
 * v2/v3	GICD_ICACTIVERn
 */
#define	GICD_ICACTIVERn		(GIC_DIST_BASE + 0x380)
#endif

/*
 * 0x400  Interrupt Priority Registers
 * v1		ICDIPRn
 * v2/v3	GICD_IPRIORITYRn
 */
#define	GICD_IPRIORITYRn	(GIC_DIST_BASE + 0x400)

/*
 * 0x800  Interrupt Processor Targets Registers
 * v1		ICDIPTRn
 * v2/v3	GICD_ITARGETSRn
 */
#define	GICD_ITARGETSRn		(GIC_DIST_BASE + 0x800)

/*
 * 0xC00  Interrupt Configuration Registers
 * v1		ICDICRn
 * v2/v3	GICD_ICFGRn
 */
#define	GICD_ICFGRn		(GIC_DIST_BASE + 0xc00)

/*
 * 0xF00  Software Generated Interrupt Register
 * v1		ICDSGIR
 * v2/v3	GICD_SGIR
 */
#define	GICD_SGIR		(GIC_DIST_BASE + 0xf00)

/*
 * GIC CPU Interface
 */

#if CONFIG_GIC_VER <= 2

/*
 * 0x0000  CPU Interface Control Register
 * v1		ICCICR
 * v2/v3	GICC_CTLR
 */
#define GICC_CTLR		(GIC_CPU_BASE +    0x0)

/*
 * 0x0004  Interrupt Priority Mask Register
 * v1		ICCPMR
 * v2/v3	GICC_PMR
 */
#define GICC_PMR		(GIC_CPU_BASE +    0x4)

/*
 * 0x0008  Binary Point Register
 * v1		ICCBPR
 * v2/v3	GICC_BPR
 */
#define GICC_BPR		(GIC_CPU_BASE +    0x8)

/*
 * 0x000C  Interrupt Acknowledge Register
 * v1		ICCIAR
 * v2/v3	GICC_IAR
 */
#define GICC_IAR		(GIC_CPU_BASE +    0xc)

/*
 * 0x0010  End of Interrupt Register
 * v1		ICCEOIR
 * v2/v3	GICC_EOIR
 */
#define GICC_EOIR		(GIC_CPU_BASE +   0x10)


/*
 * Helper Constants
 */

/* GICC_CTLR */
#define GICC_CTLR_ENABLEGRP0	BIT(0)
#define GICC_CTLR_ENABLEGRP1	BIT(1)

#define GICC_CTLR_ENABLE_MASK	(GICC_CTLR_ENABLEGRP0 | GICC_CTLR_ENABLEGRP1)

#if defined(CONFIG_GIC_V2)

#define GICC_CTLR_FIQBYPDISGRP0	BIT(5)
#define GICC_CTLR_IRQBYPDISGRP0	BIT(6)
#define GICC_CTLR_FIQBYPDISGRP1	BIT(7)
#define GICC_CTLR_IRQBYPDISGRP1	BIT(8)

#define GICC_CTLR_BYPASS_MASK	(GICC_CTLR_FIQBYPDISGRP0 | \
				 GICC_CTLR_IRQBYPDISGRP1 | \
				 GICC_CTLR_FIQBYPDISGRP1 | \
				 GICC_CTLR_IRQBYPDISGRP1)

#endif /* CONFIG_GIC_V2 */

/* GICD_SGIR */
#define GICD_SGIR_TGTFILT(x)		((x) << 24)
#define GICD_SGIR_TGTFILT_CPULIST	GICD_SGIR_TGTFILT(0b00)
#define GICD_SGIR_TGTFILT_ALLBUTREQ	GICD_SGIR_TGTFILT(0b01)
#define GICD_SGIR_TGTFILT_REQONLY	GICD_SGIR_TGTFILT(0b10)

#define GICD_SGIR_CPULIST(x)		((x) << 16)
#define GICD_SGIR_CPULIST_CPU(n)	GICD_SGIR_CPULIST(BIT(n))
#define GICD_SGIR_CPULIST_MASK		0xff

#define GICD_SGIR_NSATT			BIT(15)

#define GICD_SGIR_SGIINTID(x)		(x)

#endif /* CONFIG_GIC_VER <= 2 */


/* GICD_ICFGR */
#define GICD_ICFGR_MASK			BIT_MASK(2)
#define GICD_ICFGR_TYPE			BIT(1)

/* GICD_TYPER.ITLinesNumber 0:4 */
#define GICD_TYPER_ITLINESNUM_MASK	0x1f

/* GICD_TYPER.IDbits */
#define GICD_TYPER_IDBITS(typer)	((((typer) >> 19) & 0x1f) + 1)

/*
 * Common Helper Constants
 */
#define GIC_SGI_INT_BASE		0
#define GIC_PPI_INT_BASE		16

#define GIC_IS_SGI(intid)		(((intid) >= GIC_SGI_INT_BASE) && \
					 ((intid) < GIC_PPI_INT_BASE))


#define GIC_SPI_INT_BASE		32

#define GIC_SPI_MAX_INTID		1019

#define GIC_IS_SPI(intid)		(((intid) >= GIC_SPI_INT_BASE) && \
					((intid) <= GIC_SPI_MAX_INTID))

#define GIC_NUM_INTR_PER_REG		32

#define GIC_NUM_CFG_PER_REG		16

#define GIC_NUM_PRI_PER_REG		4

/* GIC idle priority : value '0xff' will allow all interrupts */
#define GIC_IDLE_PRIO			0xff

/* Priority levels 0:255 */
#define GIC_PRI_MASK			0xff

/*
 * '0xa0'is used to initialize each interrupt default priority.
 * This is an arbitrary value in current context.
 * Any value '0x80' to '0xff' will work for both NS and S state.
 * The values of individual interrupt and default has to be chosen
 * carefully if PMR and BPR based nesting and preemption has to be done.
 */
#define GIC_INT_DEF_PRI_X4		0xa0a0a0a0

/* GIC special interrupt id */
#define GIC_INTID_SPURIOUS		1023

/* Fixme: update from platform specific define or dt */
#define GIC_NUM_CPU_IF			CONFIG_MP_MAX_NUM_CPUS

#ifndef _ASMLANGUAGE

#include <zephyr/types.h>
#include <zephyr/device.h>

/*
 * GIC Driver Interface Functions
 */

/**
 * @brief Enable interrupt
 *
 * @param irq interrupt ID
 */
void arm_gic_irq_enable(unsigned int irq);

/**
 * @brief Disable interrupt
 *
 * @param irq interrupt ID
 */
void arm_gic_irq_disable(unsigned int irq);

/**
 * @brief Check if an interrupt is enabled
 *
 * @param irq interrupt ID
 * @return Returns true if interrupt is enabled, false otherwise
 */
bool arm_gic_irq_is_enabled(unsigned int irq);

/**
 * @brief Check if an interrupt is pending
 *
 * @param irq interrupt ID
 * @return Returns true if interrupt is pending, false otherwise
 */
bool arm_gic_irq_is_pending(unsigned int irq);

/**
 * @brief Clear the pending irq
 *
 * @param irq interrupt ID
 */
void arm_gic_irq_clear_pending(unsigned int irq);

/**
 * @brief Set interrupt priority
 *
 * @param irq interrupt ID
 * @param prio interrupt priority
 * @param flags interrupt flags
 */
void arm_gic_irq_set_priority(
	unsigned int irq, unsigned int prio, unsigned int flags);

/**
 * @brief Get active interrupt ID
 *
 * @return Returns the ID of an active interrupt
 */
unsigned int arm_gic_get_active(void);

/**
 * @brief Signal end-of-interrupt
 *
 * @param irq interrupt ID
 */
void arm_gic_eoi(unsigned int irq);

#ifdef CONFIG_SMP
/**
 * @brief Initialize GIC of secondary cores
 */
void arm_gic_secondary_init(void);
#endif

/**
 * @brief raise SGI to target cores
 *
 * @param sgi_id      SGI ID 0 to 15
 * @param target_aff  target affinity in mpidr form.
 *                    Aff level 1 2 3 will be extracted by api.
 * @param target_list bitmask of target cores
 */
void gic_raise_sgi(unsigned int sgi_id, uint64_t target_aff,
		   uint16_t target_list);

#endif /* !_ASMLANGUAGE */

#endif /* ZEPHYR_INCLUDE_DRIVERS_GIC_H_ */
