/*
 * Copyright (c) 2025, Arm Limited and Contributors. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#ifndef GICV5_H
#define GICV5_H

#ifndef __ASSEMBLER__
#include <stdbool.h>
#include <stdint.h>

#include <lib/mmio.h>
#endif

#include <lib/utils_def.h>

/* Interrupt Domain definitions */
#define INTDMN_S				0
#define INTDMN_NS				1
#define INTDMN_EL3				2
#define INTDMN_RL				3

/* Trigger modes */
#define TM_EDGE					0
#define TM_LEVEL				1

/* Architected PPI numbers */
#define PPI_TRBIRQ				31
#define PPI_CNTP				30
#define PPI_CNTPS				29
#define PPI_CNTHV				28
#define PPI_CNTV				27
#define PPI_CNTHP				26
#define PPI_GICMNT				25
#define PPI_CTIIRQ				24
#define PPI_PMUIRQ				23
#define PPI_COMMIRQ				22
#define PPI_PMBIRQ				21
#define PPI_CNTHPS				20
#define PPI_CNTHVS				19
#define PPI_DB_NS				2
#define PPI_DB_RL				1
#define PPI_DB_S				0

/* Register fields common to all IRI components.
 * They have the same name and offset in every config frame */
#define IRI_AIDR_COMPONENT_SHIFT		8
#define IRI_AIDR_COMPONENT_WIDTH		4
#define IRI_AIDR_COMPONENT_IRS			0
#define IRI_AIDR_COMPONENT_ITS			1
#define IRI_AIDR_COMPONENT_IWB			2
#define IRI_AIDR_ARCH_MAJOR_SHIFT		4
#define IRI_AIDR_ARCH_MAJOR_WIDTH		4
#define IRI_AIDR_ARCH_MAJOR_V5			0
#define IRI_AIDR_ARCH_MINOR_SHIFT		0
#define IRI_AIDR_ARCH_MINOR_WIDTH		4
#define IRI_AIDR_ARCH_MINOR_P0			0

/* IRS register fields */
#define IRS_IDR6_SPI_IRS_RANGE_SHIFT		0
#define IRS_IDR6_SPI_IRS_RANGE_WIDTH		24
#define IRS_IDR7_SPI_BASE_SHIFT			0
#define IRS_IDR7_SPI_BASE_WIDTH			24

#define IRS_SPI_STATUSR_IDLE_BIT		BIT(0)
#define IRS_SPI_STATUSR_V_BIT			BIT(1)

/* IWB register fields */
#define IWB_IDR0_DOMAINS_SHIFT			11
#define IWB_IDR0_DOMAINS_WIDTH			4
#define IWB_IDR0_IWRANGE_SHIFT			0
#define IWB_IDR0_IWRANGE_WIDTH			10

#define IWB_CR0_IWBEN_BIT			BIT(0)
#define IWB_CR0_IDLE_BIT			BIT(1)

#define IWB_WENABLE_STATUSR_IDLE_BIT		BIT(0)
#define IWB_WDOMAIN_STATUSR_IDLE_BIT		BIT(0)

#define IWB_WDOMAINR_DOMAINX_MASK		0x3

#ifndef __ASSEMBLER__

#define _PPI_FIELD_SHIFT(_REG, _ppi_id)						\
	((_ppi_id % (ICC_PPI_##_REG##_COUNT)) * (64 / ICC_PPI_##_REG##_COUNT))

#define write_icc_ppi_domainr(_var, _ppi_id, _value)				\
	do {									\
		_var |=  (uint64_t)_value << _PPI_FIELD_SHIFT(DOMAINR, _ppi_id);\
	} while (false)


#define DEFINE_GICV5_MMIO_WRITE_FUNC(_name, _offset)				\
static inline void write_##_name(uintptr_t base, uint32_t val)			\
{										\
	mmio_write_32(base + _offset, val);					\
}

#define DEFINE_GICV5_MMIO_READ_FUNC(_name, _offset)				\
static inline uint32_t read_##_name(uintptr_t base)				\
{										\
	return mmio_read_32(base + _offset);					\
}

#define DEFINE_GICV5_MMIO_WRITE_INDEXED_FUNC(_name, _offset)			\
static inline void write_##_name(uintptr_t base, uint16_t index, uint32_t val)	\
{										\
	mmio_write_32(base + _offset + (index * sizeof(uint32_t)), val);	\
}

#define DEFINE_GICV5_MMIO_READ_INDEXED_FUNC(_name, _offset)			\
static inline uint32_t read_##_name(uintptr_t base, uint16_t index)		\
{										\
	return mmio_read_32(base + _offset + (index * sizeof(uint32_t)));	\
}

#define DEFINE_GICV5_MMIO_RW_FUNCS(_name, _offset)				\
	DEFINE_GICV5_MMIO_READ_FUNC(_name, _offset)				\
	DEFINE_GICV5_MMIO_WRITE_FUNC(_name, _offset)

#define DEFINE_GICV5_MMIO_RW_INDEXED_FUNCS(_name, _offset)			\
	DEFINE_GICV5_MMIO_READ_INDEXED_FUNC(_name, _offset)			\
	DEFINE_GICV5_MMIO_WRITE_INDEXED_FUNC(_name, _offset)

DEFINE_GICV5_MMIO_READ_FUNC(iri_aidr,			0x44)

DEFINE_GICV5_MMIO_READ_FUNC(iwb_idr0,			0x00)
DEFINE_GICV5_MMIO_RW_FUNCS( iwb_cr0,			0x80)
DEFINE_GICV5_MMIO_READ_FUNC(iwb_wenable_statusr,	0xc0)
DEFINE_GICV5_MMIO_READ_FUNC(iwb_wdomain_statusr,	0xc4)
DEFINE_GICV5_MMIO_RW_INDEXED_FUNCS(iwb_wenabler,	0x2000)
DEFINE_GICV5_MMIO_RW_INDEXED_FUNCS(iwb_wtmr,		0x4000)
DEFINE_GICV5_MMIO_RW_INDEXED_FUNCS(iwb_wdomainr,	0x6000)

DEFINE_GICV5_MMIO_READ_FUNC(irs_idr6,			0x0018)
DEFINE_GICV5_MMIO_READ_FUNC(irs_idr7,			0x001c)
DEFINE_GICV5_MMIO_RW_FUNCS( irs_spi_selr,		0x0108)
DEFINE_GICV5_MMIO_RW_FUNCS( irs_spi_domainr,		0x010c)
DEFINE_GICV5_MMIO_RW_FUNCS( irs_spi_cfgr,		0x0114)
DEFINE_GICV5_MMIO_READ_FUNC(irs_spi_statusr,		0x0118)

#define WAIT_FOR_IDLE(base, reg, reg_up)					\
	do {									\
		while ((read_##reg(base) & reg_up##_IDLE_BIT) == 0U) {}		\
	} while (0)

/* wait for IDLE but also check the V bit was set */
#define WAIT_FOR_VIDLE(base, reg, reg_up)					\
	do {									\
		uint32_t val;							\
		while (((val = read_##reg(base)) & reg_up##_IDLE_BIT) == 0U) {}	\
		assert((val & reg##_V_BIT) != 0U);				\
	} while (0)

#define WAIT_FOR_VIDLE_IRS_SPI_STATUSR(base)					\
	WAIT_FOR_IDLE(base, irs_spi_statusr, IRS_SPI_STATUSR)

#define WAIT_FOR_IDLE_IWB_WENABLE_STATUSR(base)					\
	WAIT_FOR_IDLE(base, iwb_wenable_statusr, IWB_WENABLE_STATUSR)
#define WAIT_FOR_IDLE_IWB_WDOMAIN_STATUSR(base)					\
	WAIT_FOR_IDLE(base, iwb_wdomain_statusr, IWB_WDOMAIN_STATUSR)
#define WAIT_FOR_IDLE_IWB_CR0(base)						\
	WAIT_FOR_IDLE(base, iwb_cr0, IWB_CR0)

#define WIRE_PROP_DESC(_id, _domain, _tm) \
	{ \
		.id = (_id), \
		.domain = (_domain), \
		.tm = (_tm), \
	}

struct gicv5_wire_props {
	/* continuous wire ID as seen by the attached component */
	uint32_t id;
	/* use the INTDMN_XYZ macros */
	uint8_t domain:2;
	/* use the TM_XYZ (eg. TM_EDGE) macros */
	uint8_t tm:1;
};

/* to describe every IRS in the system */
struct gicv5_irs {
	/* mapped device nGnRnE by the platform*/
	uintptr_t el3_config_frame;
	struct gicv5_wire_props *spis;
	uint32_t num_spis;
};

/*
 * to describe every IWB in the system where EL3 is the MPPAS. IWBs that have
 * another world as an MPPAS need not be included
 */
struct gicv5_iwb {
	/* mapped device nGnRnE by the platform*/
	uintptr_t config_frame;
	struct gicv5_wire_props *wires;
	uint32_t num_wires;
};

struct gicv5_driver_data {
	struct gicv5_irs *irss;
	struct gicv5_iwb *iwbs;
	uint32_t num_irss;
	uint32_t num_iwbs;
};

extern const struct gicv5_driver_data plat_gicv5_driver_data;

void gicv5_driver_init();
uint8_t gicv5_get_pending_interrupt_type(void);
bool gicv5_has_interrupt_type(unsigned int type);
void gicv5_enable_ppis();
#endif /* __ASSEMBLER__ */
#endif /* GICV5_H */
