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 /*
19 * Check for a bad platform configuration.
20 * Not expected to be called on release builds.
21 */
probe_component(uintptr_t base_addr,uint8_t component)22 static inline bool probe_component(uintptr_t base_addr, uint8_t component)
23 {
24 uint32_t aidr = read_iri_aidr(base_addr);
25
26 if (EXTRACT(IRI_AIDR_COMPONENT, aidr) != component) {
27 ERROR("GICv5 frame belongs to wrong component\n");
28 return false;
29 }
30
31 if (EXTRACT(IRI_AIDR_ARCH_MAJOR, aidr) != IRI_AIDR_ARCH_MAJOR_V5) {
32 ERROR("Bad GICv5 major version\n");
33 return false;
34 }
35
36 /* there was a bump in architecture and we've not updated the driver */
37 assert(EXTRACT(IRI_AIDR_ARCH_MINOR, aidr) == IRI_AIDR_ARCH_MINOR_P0);
38
39 return true;
40 }
41
iwb_domain_supported(uint32_t idr0,uint8_t domain)42 static inline bool iwb_domain_supported(uint32_t idr0, uint8_t domain)
43 {
44 return (EXTRACT(IWB_IDR0_DOMAINS, idr0) & (1U << domain)) != 0U;
45 }
46
iwb_configure_domainr(uintptr_t base_addr,struct gicv5_wire_props wire)47 static void iwb_configure_domainr(uintptr_t base_addr, struct gicv5_wire_props wire)
48 {
49 uint32_t reg_offset = (wire.id % 16U) * 2U;
50 uint32_t reg_index = wire.id / 16U;
51 uint32_t val = read_iwb_wdomainr(base_addr, reg_index) &
52 ~(IWB_WDOMAINR_DOMAINX_MASK << reg_offset);
53
54 write_iwb_wdomainr(base_addr, reg_index, val | wire.domain << reg_offset);
55 }
56
iwb_configure_wtmr(uintptr_t base_addr,struct gicv5_wire_props wire)57 static void iwb_configure_wtmr(uintptr_t base_addr, struct gicv5_wire_props wire)
58 {
59 uint32_t reg_offset = wire.id % 32U;
60 uint32_t reg_index = wire.id / 32U;
61 uint32_t val = read_iwb_wtmr(base_addr, reg_index) & ~(1U << reg_offset);
62
63 write_iwb_wtmr(base_addr, reg_index, val | wire.tm << reg_offset);
64 }
65
iwb_enable(const struct gicv5_iwb * config)66 static void iwb_enable(const struct gicv5_iwb *config)
67 {
68 uintptr_t base_addr = config->config_frame;
69 uint32_t idr0;
70 uint16_t num_regs;
71
72 assert(probe_component(base_addr, IRI_AIDR_COMPONENT_IWB));
73
74 idr0 = read_iwb_idr0(base_addr);
75 num_regs = EXTRACT(IWB_IDR0_IWRANGE, idr0) + 1U;
76
77 /* initialise all wires as disabled */
78 for (int i = 0U; i < num_regs; i++) {
79 write_iwb_wenabler(base_addr, i, 0U);
80 }
81
82 /* default all wires to the NS domain */
83 for (int i = 0U; i < num_regs * 2; i++) {
84 write_iwb_wdomainr(base_addr, i, 0x55555555);
85 }
86
87 for (uint32_t i = 0U; i < config->num_wires; i++) {
88 assert(iwb_domain_supported(idr0, config->wires[i].domain));
89 assert(config->wires[i].id <= num_regs * 32);
90
91 iwb_configure_domainr(base_addr, config->wires[i]);
92 iwb_configure_wtmr(base_addr, config->wires[i]);
93 }
94
95 write_iwb_cr0(base_addr, IWB_CR0_IWBEN_BIT);
96 WAIT_FOR_IDLE_IWB_WENABLE_STATUSR(base_addr);
97 WAIT_FOR_IDLE_IWB_WDOMAIN_STATUSR(base_addr);
98 WAIT_FOR_IDLE_IWB_CR0(base_addr);
99 }
100
irs_configure_wire(uintptr_t base_addr,uint32_t wire,uint8_t domain)101 static void irs_configure_wire(uintptr_t base_addr, uint32_t wire, uint8_t domain)
102 {
103 write_irs_spi_selr(base_addr, wire);
104 WAIT_FOR_VIDLE_IRS_SPI_STATUSR(base_addr);
105
106 write_irs_spi_domainr(base_addr, domain);
107 WAIT_FOR_VIDLE_IRS_SPI_STATUSR(base_addr);
108 }
109
irs_enable(const struct gicv5_irs * config)110 static void irs_enable(const struct gicv5_irs *config)
111 {
112 uint32_t spi_base, spi_range;
113 uintptr_t base_addr = config->el3_config_frame;
114
115 spi_base = EXTRACT(IRS_IDR7_SPI_BASE, read_irs_idr7(base_addr));
116 spi_range = EXTRACT(IRS_IDR6_SPI_IRS_RANGE, read_irs_idr6(base_addr));
117
118 assert(probe_component(base_addr, IRI_AIDR_COMPONENT_IRS));
119
120 if (spi_range == 0U) {
121 assert(config->num_spis == 0U);
122 }
123
124 /* default all wires to the NS domain */
125 for (uint32_t i = spi_base; i < spi_base + spi_range; i++) {
126 irs_configure_wire(base_addr, i, INTDMN_NS);
127 }
128
129 for (uint32_t i = 0U; i < config->num_spis; i++) {
130 assert((config->spis[i].id >= spi_base) &&
131 (config->spis[i].id < spi_base + spi_range));
132
133 irs_configure_wire(base_addr, config->spis[i].id, config->spis[i].domain);
134
135 /* don't (can't) configure TM of wires for other domains */
136 if (config->spis[i].domain == INTDMN_EL3) {
137 write_irs_spi_cfgr(base_addr, config->spis[i].tm);
138 WAIT_FOR_VIDLE_IRS_SPI_STATUSR(base_addr);
139 }
140 }
141 }
142
gicv5_driver_init(void)143 void __init gicv5_driver_init(void)
144 {
145 for (size_t i = 0U; i < plat_gicv5_driver_data.num_iwbs; i++) {
146 iwb_enable(&plat_gicv5_driver_data.iwbs[i]);
147 }
148
149 for (size_t i = 0U; i < plat_gicv5_driver_data.num_irss; i++) {
150 irs_enable(&plat_gicv5_driver_data.irss[i]);
151 }
152 }
153
154 /*
155 * There exists a theoretical configuration where FEAT_RME is enabled
156 * without using TrustZone (i.e., no Secure world present). Currently,
157 * there is no reliable mechanism to detect this scenario at runtime.
158 *
159 * TODO: Add support for this configuration in the future if required.
160 */
gicv5_has_interrupt_type(unsigned int type)161 bool gicv5_has_interrupt_type(unsigned int type)
162 {
163 switch (type) {
164 case INTR_TYPE_EL3:
165 case INTR_TYPE_S_EL1:
166 case INTR_TYPE_NS:
167 return true;
168 case INTR_TYPE_RL:
169 return is_feat_rme_supported();
170 default:
171 return false;
172 }
173 }
174
gicv5_get_pending_interrupt_type(void)175 uint8_t gicv5_get_pending_interrupt_type(void)
176 {
177 /* there is no pending interrupt expected */
178 return INTR_TYPE_INVAL;
179 }
180
181 /* TODO: these will probably end up contexted. Make Linux work for now */
gicv5_enable_ppis(void)182 void gicv5_enable_ppis(void)
183 {
184 uint64_t domainr = 0U;
185
186 /* the only ones described in the device tree at the moment */
187 write_icc_ppi_domainr(domainr, PPI_PMUIRQ, INTDMN_NS);
188 write_icc_ppi_domainr(domainr, PPI_GICMNT, INTDMN_NS);
189 write_icc_ppi_domainr(domainr, PPI_CNTHP, INTDMN_NS);
190 write_icc_ppi_domainr(domainr, PPI_CNTV, INTDMN_NS);
191 write_icc_ppi_domainr(domainr, PPI_CNTPS, INTDMN_NS);
192 write_icc_ppi_domainr(domainr, PPI_CNTP, INTDMN_NS);
193
194 write_icc_ppi_domainr0_el3(domainr);
195 }
196