xref: /rk3399_ARM-atf/drivers/arm/gicv5/gicv5_main.c (revision 71799209def08f4afdc49555246268cf1300a2d7)
18cef63d6SBoyan Karatotev /*
28cef63d6SBoyan Karatotev  * Copyright (c) 2025, Arm Limited and Contributors. All rights reserved.
38cef63d6SBoyan Karatotev  *
48cef63d6SBoyan Karatotev  * SPDX-License-Identifier: BSD-3-Clause
58cef63d6SBoyan Karatotev  */
613b62814SBoyan Karatotev 
7dfb37a2dSBoyan Karatotev #include <assert.h>
813b62814SBoyan Karatotev #include <cdefs.h>
913b62814SBoyan Karatotev 
1082b228baSBoyan Karatotev #include <arch.h>
1113b62814SBoyan Karatotev #include <arch_features.h>
1282b228baSBoyan Karatotev #include <arch_helpers.h>
1382b228baSBoyan Karatotev 
1413b62814SBoyan Karatotev #include <bl31/interrupt_mgmt.h>
1513b62814SBoyan Karatotev #include <common/debug.h>
1613b62814SBoyan Karatotev #include <drivers/arm/gicv5.h>
1713b62814SBoyan Karatotev 
18*71799209SBoyan Karatotev static inline bool iwb_domain_supported(uint32_t idr0, uint8_t domain)
19*71799209SBoyan Karatotev {
20*71799209SBoyan Karatotev 	return (EXTRACT(IWB_IDR0_DOMAINS, idr0) & (1U << domain)) != 0U;
21*71799209SBoyan Karatotev }
22*71799209SBoyan Karatotev 
23*71799209SBoyan Karatotev static void iwb_configure_domainr(uintptr_t base_addr, struct gicv5_wire_props wire)
24*71799209SBoyan Karatotev {
25*71799209SBoyan Karatotev 	uint32_t reg_offset = (wire.id % 16U) * 2U;
26*71799209SBoyan Karatotev 	uint32_t reg_index = wire.id / 16U;
27*71799209SBoyan Karatotev 	uint32_t val = read_iwb_wdomainr(base_addr, reg_index) &
28*71799209SBoyan Karatotev 		       ~(IWB_WDOMAINR_DOMAINX_MASK << reg_offset);
29*71799209SBoyan Karatotev 
30*71799209SBoyan Karatotev 	write_iwb_wdomainr(base_addr, reg_index, val | wire.domain << reg_offset);
31*71799209SBoyan Karatotev }
32*71799209SBoyan Karatotev 
33*71799209SBoyan Karatotev static void iwb_configure_wtmr(uintptr_t base_addr, struct gicv5_wire_props wire)
34*71799209SBoyan Karatotev {
35*71799209SBoyan Karatotev 	uint32_t reg_offset = wire.id % 32U;
36*71799209SBoyan Karatotev 	uint32_t reg_index = wire.id / 32U;
37*71799209SBoyan Karatotev 	uint32_t val = read_iwb_wtmr(base_addr, reg_index) & ~(1U << reg_offset);
38*71799209SBoyan Karatotev 
39*71799209SBoyan Karatotev 	write_iwb_wtmr(base_addr, reg_index, val | wire.tm << reg_offset);
40*71799209SBoyan Karatotev }
41*71799209SBoyan Karatotev 
42*71799209SBoyan Karatotev static void iwb_enable(const struct gicv5_iwb *config)
43*71799209SBoyan Karatotev {
44*71799209SBoyan Karatotev 	uintptr_t base_addr = config->config_frame;
45*71799209SBoyan Karatotev 	uint32_t idr0;
46*71799209SBoyan Karatotev 	uint16_t num_regs;
47*71799209SBoyan Karatotev 
48*71799209SBoyan Karatotev 	idr0 = read_iwb_idr0(base_addr);
49*71799209SBoyan Karatotev 	num_regs = EXTRACT(IWB_IDR0_IWRANGE, idr0) + 1U;
50*71799209SBoyan Karatotev 
51*71799209SBoyan Karatotev 	/* initialise all wires as disabled */
52*71799209SBoyan Karatotev 	for (int i = 0U; i < num_regs; i++) {
53*71799209SBoyan Karatotev 		write_iwb_wenabler(base_addr, i, 0U);
54*71799209SBoyan Karatotev 	}
55*71799209SBoyan Karatotev 
56*71799209SBoyan Karatotev 	/* default all wires to the NS domain */
57*71799209SBoyan Karatotev 	for (int i = 0U; i < num_regs * 2; i++) {
58*71799209SBoyan Karatotev 		write_iwb_wdomainr(base_addr, i, 0x55555555);
59*71799209SBoyan Karatotev 	}
60*71799209SBoyan Karatotev 
61*71799209SBoyan Karatotev 	for (uint32_t i = 0U; i < config->num_wires; i++) {
62*71799209SBoyan Karatotev 		assert(iwb_domain_supported(idr0, config->wires[i].domain));
63*71799209SBoyan Karatotev 		assert(config->wires[i].id <= num_regs * 32);
64*71799209SBoyan Karatotev 
65*71799209SBoyan Karatotev 		iwb_configure_domainr(base_addr, config->wires[i]);
66*71799209SBoyan Karatotev 		iwb_configure_wtmr(base_addr, config->wires[i]);
67*71799209SBoyan Karatotev 	}
68*71799209SBoyan Karatotev 
69*71799209SBoyan Karatotev 	write_iwb_cr0(base_addr, IWB_CR0_IWBEN_BIT);
70*71799209SBoyan Karatotev 	WAIT_FOR_IDLE_IWB_WENABLE_STATUSR(base_addr);
71*71799209SBoyan Karatotev 	WAIT_FOR_IDLE_IWB_WDOMAIN_STATUSR(base_addr);
72*71799209SBoyan Karatotev 	WAIT_FOR_IDLE_IWB_CR0(base_addr);
73*71799209SBoyan Karatotev }
74*71799209SBoyan Karatotev 
75dfb37a2dSBoyan Karatotev static void irs_configure_wire(uintptr_t base_addr, uint32_t wire, uint8_t domain)
76dfb37a2dSBoyan Karatotev {
77dfb37a2dSBoyan Karatotev 	write_irs_spi_selr(base_addr, wire);
78dfb37a2dSBoyan Karatotev 	WAIT_FOR_VIDLE_IRS_SPI_STATUSR(base_addr);
79dfb37a2dSBoyan Karatotev 
80dfb37a2dSBoyan Karatotev 	write_irs_spi_domainr(base_addr, domain);
81dfb37a2dSBoyan Karatotev 	WAIT_FOR_VIDLE_IRS_SPI_STATUSR(base_addr);
82dfb37a2dSBoyan Karatotev }
83dfb37a2dSBoyan Karatotev 
84dfb37a2dSBoyan Karatotev static void irs_enable(const struct gicv5_irs *config)
85dfb37a2dSBoyan Karatotev {
86dfb37a2dSBoyan Karatotev 	uint32_t spi_base, spi_range;
87dfb37a2dSBoyan Karatotev 	uintptr_t base_addr = config->el3_config_frame;
88dfb37a2dSBoyan Karatotev 
89dfb37a2dSBoyan Karatotev 	spi_base  = EXTRACT(IRS_IDR7_SPI_BASE, read_irs_idr7(base_addr));
90dfb37a2dSBoyan Karatotev 	spi_range = EXTRACT(IRS_IDR6_SPI_IRS_RANGE, read_irs_idr6(base_addr));
91dfb37a2dSBoyan Karatotev 
92dfb37a2dSBoyan Karatotev 	if (spi_range == 0U) {
93dfb37a2dSBoyan Karatotev 		assert(config->num_spis == 0U);
94dfb37a2dSBoyan Karatotev 	}
95dfb37a2dSBoyan Karatotev 
96dfb37a2dSBoyan Karatotev 	/* default all wires to the NS domain */
97dfb37a2dSBoyan Karatotev 	for (uint32_t i = spi_base; i < spi_base + spi_range; i++) {
98dfb37a2dSBoyan Karatotev 		irs_configure_wire(base_addr, i, INTDMN_NS);
99dfb37a2dSBoyan Karatotev 	}
100dfb37a2dSBoyan Karatotev 
101dfb37a2dSBoyan Karatotev 	for (uint32_t i = 0U; i < config->num_spis; i++) {
102dfb37a2dSBoyan Karatotev 		assert((config->spis[i].id >= spi_base) &&
103dfb37a2dSBoyan Karatotev 		       (config->spis[i].id < spi_base + spi_range));
104dfb37a2dSBoyan Karatotev 
105dfb37a2dSBoyan Karatotev 		irs_configure_wire(base_addr, config->spis[i].id, config->spis[i].domain);
106dfb37a2dSBoyan Karatotev 
107dfb37a2dSBoyan Karatotev 		/* don't (can't) configure TM of wires for other domains */
108dfb37a2dSBoyan Karatotev 		if (config->spis[i].domain == INTDMN_EL3) {
109dfb37a2dSBoyan Karatotev 			write_irs_spi_cfgr(base_addr, config->spis[i].tm);
110dfb37a2dSBoyan Karatotev 			WAIT_FOR_VIDLE_IRS_SPI_STATUSR(base_addr);
111dfb37a2dSBoyan Karatotev 		}
112dfb37a2dSBoyan Karatotev 	}
113dfb37a2dSBoyan Karatotev }
114dfb37a2dSBoyan Karatotev 
11513b62814SBoyan Karatotev void __init gicv5_driver_init(void)
11613b62814SBoyan Karatotev {
117*71799209SBoyan Karatotev 	for (size_t i = 0U; i < plat_gicv5_driver_data.num_iwbs; i++) {
118*71799209SBoyan Karatotev 		iwb_enable(&plat_gicv5_driver_data.iwbs[i]);
119*71799209SBoyan Karatotev 	}
120*71799209SBoyan Karatotev 
121dfb37a2dSBoyan Karatotev 	for (size_t i = 0U; i < plat_gicv5_driver_data.num_irss; i++) {
122dfb37a2dSBoyan Karatotev 		irs_enable(&plat_gicv5_driver_data.irss[i]);
123dfb37a2dSBoyan Karatotev 	}
12413b62814SBoyan Karatotev }
12513b62814SBoyan Karatotev 
12613b62814SBoyan Karatotev /*
12713b62814SBoyan Karatotev  * There exists a theoretical configuration where FEAT_RME is enabled
12813b62814SBoyan Karatotev  * without using TrustZone (i.e., no Secure world present). Currently,
12913b62814SBoyan Karatotev  * there is no reliable mechanism to detect this scenario at runtime.
13013b62814SBoyan Karatotev  *
13113b62814SBoyan Karatotev  * TODO: Add support for this configuration in the future if required.
13213b62814SBoyan Karatotev  */
13313b62814SBoyan Karatotev bool gicv5_has_interrupt_type(unsigned int type)
13413b62814SBoyan Karatotev {
13513b62814SBoyan Karatotev 	switch (type) {
13613b62814SBoyan Karatotev 	case INTR_TYPE_EL3:
13713b62814SBoyan Karatotev 	case INTR_TYPE_S_EL1:
13813b62814SBoyan Karatotev 	case INTR_TYPE_NS:
13913b62814SBoyan Karatotev 		return true;
14013b62814SBoyan Karatotev 	case INTR_TYPE_RL:
14113b62814SBoyan Karatotev 		return is_feat_rme_supported();
14213b62814SBoyan Karatotev 	default:
14313b62814SBoyan Karatotev 		return false;
14413b62814SBoyan Karatotev 	}
14513b62814SBoyan Karatotev }
14613b62814SBoyan Karatotev 
14713b62814SBoyan Karatotev uint8_t gicv5_get_pending_interrupt_type(void)
14813b62814SBoyan Karatotev {
14913b62814SBoyan Karatotev 	/* there is no pending interrupt expected */
15013b62814SBoyan Karatotev 	return INTR_TYPE_INVAL;
15113b62814SBoyan Karatotev }
15282b228baSBoyan Karatotev 
15382b228baSBoyan Karatotev /* TODO: these will probably end up contexted. Make Linux work for now */
15482b228baSBoyan Karatotev void gicv5_enable_ppis(void)
15582b228baSBoyan Karatotev {
15682b228baSBoyan Karatotev 	uint64_t domainr = 0U;
15782b228baSBoyan Karatotev 
15882b228baSBoyan Karatotev 	/* the only ones described in the device tree at the moment */
15982b228baSBoyan Karatotev 	write_icc_ppi_domainr(domainr, PPI_PMUIRQ,	INTDMN_NS);
16082b228baSBoyan Karatotev 	write_icc_ppi_domainr(domainr, PPI_GICMNT,	INTDMN_NS);
16182b228baSBoyan Karatotev 	write_icc_ppi_domainr(domainr, PPI_CNTHP,	INTDMN_NS);
16282b228baSBoyan Karatotev 	write_icc_ppi_domainr(domainr, PPI_CNTV,	INTDMN_NS);
16382b228baSBoyan Karatotev 	write_icc_ppi_domainr(domainr, PPI_CNTPS,	INTDMN_NS);
16482b228baSBoyan Karatotev 	write_icc_ppi_domainr(domainr, PPI_CNTP,	INTDMN_NS);
16582b228baSBoyan Karatotev 
16682b228baSBoyan Karatotev 	write_icc_ppi_domainr0_el3(domainr);
16782b228baSBoyan Karatotev }
168