xref: /rk3399_ARM-atf/drivers/arm/gicv5/gicv5_main.c (revision dfb37a2df579ccf0e92c97515452b13ce2c9fba8)
1 /*
2  * Copyright (c) 2025, Arm Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <assert.h>
8 #include <cdefs.h>
9 
10 #include <arch.h>
11 #include <arch_features.h>
12 #include <arch_helpers.h>
13 
14 #include <bl31/interrupt_mgmt.h>
15 #include <common/debug.h>
16 #include <drivers/arm/gicv5.h>
17 
18 static void irs_configure_wire(uintptr_t base_addr, uint32_t wire, uint8_t domain)
19 {
20 	write_irs_spi_selr(base_addr, wire);
21 	WAIT_FOR_VIDLE_IRS_SPI_STATUSR(base_addr);
22 
23 	write_irs_spi_domainr(base_addr, domain);
24 	WAIT_FOR_VIDLE_IRS_SPI_STATUSR(base_addr);
25 }
26 
27 static void irs_enable(const struct gicv5_irs *config)
28 {
29 	uint32_t spi_base, spi_range;
30 	uintptr_t base_addr = config->el3_config_frame;
31 
32 	spi_base  = EXTRACT(IRS_IDR7_SPI_BASE, read_irs_idr7(base_addr));
33 	spi_range = EXTRACT(IRS_IDR6_SPI_IRS_RANGE, read_irs_idr6(base_addr));
34 
35 	if (spi_range == 0U) {
36 		assert(config->num_spis == 0U);
37 	}
38 
39 	/* default all wires to the NS domain */
40 	for (uint32_t i = spi_base; i < spi_base + spi_range; i++) {
41 		irs_configure_wire(base_addr, i, INTDMN_NS);
42 	}
43 
44 	for (uint32_t i = 0U; i < config->num_spis; i++) {
45 		assert((config->spis[i].id >= spi_base) &&
46 		       (config->spis[i].id < spi_base + spi_range));
47 
48 		irs_configure_wire(base_addr, config->spis[i].id, config->spis[i].domain);
49 
50 		/* don't (can't) configure TM of wires for other domains */
51 		if (config->spis[i].domain == INTDMN_EL3) {
52 			write_irs_spi_cfgr(base_addr, config->spis[i].tm);
53 			WAIT_FOR_VIDLE_IRS_SPI_STATUSR(base_addr);
54 		}
55 	}
56 }
57 
58 void __init gicv5_driver_init(void)
59 {
60 	for (size_t i = 0U; i < plat_gicv5_driver_data.num_irss; i++) {
61 		irs_enable(&plat_gicv5_driver_data.irss[i]);
62 	}
63 }
64 
65 /*
66  * There exists a theoretical configuration where FEAT_RME is enabled
67  * without using TrustZone (i.e., no Secure world present). Currently,
68  * there is no reliable mechanism to detect this scenario at runtime.
69  *
70  * TODO: Add support for this configuration in the future if required.
71  */
72 bool gicv5_has_interrupt_type(unsigned int type)
73 {
74 	switch (type) {
75 	case INTR_TYPE_EL3:
76 	case INTR_TYPE_S_EL1:
77 	case INTR_TYPE_NS:
78 		return true;
79 	case INTR_TYPE_RL:
80 		return is_feat_rme_supported();
81 	default:
82 		return false;
83 	}
84 }
85 
86 uint8_t gicv5_get_pending_interrupt_type(void)
87 {
88 	/* there is no pending interrupt expected */
89 	return INTR_TYPE_INVAL;
90 }
91 
92 /* TODO: these will probably end up contexted. Make Linux work for now */
93 void gicv5_enable_ppis(void)
94 {
95 	uint64_t domainr = 0U;
96 
97 	/* the only ones described in the device tree at the moment */
98 	write_icc_ppi_domainr(domainr, PPI_PMUIRQ,	INTDMN_NS);
99 	write_icc_ppi_domainr(domainr, PPI_GICMNT,	INTDMN_NS);
100 	write_icc_ppi_domainr(domainr, PPI_CNTHP,	INTDMN_NS);
101 	write_icc_ppi_domainr(domainr, PPI_CNTV,	INTDMN_NS);
102 	write_icc_ppi_domainr(domainr, PPI_CNTPS,	INTDMN_NS);
103 	write_icc_ppi_domainr(domainr, PPI_CNTP,	INTDMN_NS);
104 
105 	write_icc_ppi_domainr0_el3(domainr);
106 }
107