blob: 0f8f5fa0d3a190117f217ae42a369c6614dddcb1 [file] [log] [blame]
Boyan Karatoteve5629bd2025-06-16 11:45:34 +01001/*
2 * Copyright (c) 2025, Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <assert.h>
8
9#include <arch.h>
10#include <arch_features.h>
11#include <arch_helpers.h>
12#include <drivers/arm/gic_v5.h>
13#include <platform.h>
14
15#include <platform_def.h>
16
17/*
18 * Data structure to store the GIC per CPU context before entering
19 * a suspend to powerdown (with loss of context).
20 */
21struct gicv5_pcpu_ctx {
22 /* CPU IF registers. Only those currently in use */
23 u_register_t icc_cr0_el1;
24 /* PPI registers. We don't touch handling mode. */
25 u_register_t icc_ppi_enabler[2];
26 u_register_t icc_ppi_xpendr[2];
27 u_register_t icc_ppi_priorityr[16];
28};
29
30static uintptr_t irs_base;
31static struct gicv5_pcpu_ctx cpu_ctx[PLATFORM_CORE_COUNT];
32
33/* CPU MPIDR != GICv5 IAFFID. This holds the mapping, initialised on CPU_ON. */
34static uint16_t iaffids[PLATFORM_CORE_COUNT];
35
36/* the IST is a power of 2 since that's what goes in IRS_IST_CFGR.LPI_ID_BITS */
37struct l2_iste ist[next_power_of_2(PLATFORM_CORE_COUNT) * IRQ_NUM_SGIS];
38
39static inline uint8_t log2(uint32_t num)
40{
41 return (31 - __builtin_clz(num));
42}
43
44static inline bool is_interrupt(unsigned int interrupt_id)
45{
46 unsigned int_type = EXTRACT(INT_TYPE, interrupt_id);
47
48 return int_type == INT_PPI || int_type == INT_LPI || int_type == INT_SPI;
49}
50
51static inline u_register_t read_icc_ppi_priorityrn(unsigned n)
52{
53 switch (n) {
54 case 0:
55 return read_icc_ppi_priorityr0();
56 case 1:
57 return read_icc_ppi_priorityr1();
58 case 2:
59 return read_icc_ppi_priorityr2();
60 case 3:
61 return read_icc_ppi_priorityr3();
62 case 4:
63 return read_icc_ppi_priorityr4();
64 case 5:
65 return read_icc_ppi_priorityr5();
66 case 6:
67 return read_icc_ppi_priorityr6();
68 case 7:
69 return read_icc_ppi_priorityr7();
70 case 8:
71 return read_icc_ppi_priorityr8();
72 case 9:
73 return read_icc_ppi_priorityr9();
74 case 10:
75 return read_icc_ppi_priorityr10();
76 case 11:
77 return read_icc_ppi_priorityr11();
78 case 12:
79 return read_icc_ppi_priorityr12();
80 case 13:
81 return read_icc_ppi_priorityr13();
82 case 14:
83 return read_icc_ppi_priorityr14();
84 case 15:
85 return read_icc_ppi_priorityr15();
86 default:
87 panic();
88 }
89}
90
91static inline void write_icc_ppi_priorityrn(unsigned n, u_register_t val)
92{
93 switch (n) {
94 case 0:
95 return write_icc_ppi_priorityr0(val);
96 case 1:
97 return write_icc_ppi_priorityr1(val);
98 case 2:
99 return write_icc_ppi_priorityr2(val);
100 case 3:
101 return write_icc_ppi_priorityr3(val);
102 case 4:
103 return write_icc_ppi_priorityr4(val);
104 case 5:
105 return write_icc_ppi_priorityr5(val);
106 case 6:
107 return write_icc_ppi_priorityr6(val);
108 case 7:
109 return write_icc_ppi_priorityr7(val);
110 case 8:
111 return write_icc_ppi_priorityr8(val);
112 case 9:
113 return write_icc_ppi_priorityr9(val);
114 case 10:
115 return write_icc_ppi_priorityr10(val);
116 case 11:
117 return write_icc_ppi_priorityr11(val);
118 case 12:
119 return write_icc_ppi_priorityr12(val);
120 case 13:
121 return write_icc_ppi_priorityr13(val);
122 case 14:
123 return write_icc_ppi_priorityr14(val);
124 case 15:
125 return write_icc_ppi_priorityr15(val);
126 default:
127 panic();
128 }
129}
130
131bool is_gicv5_mode(void)
132{
133 return is_feat_gcie_supported();
134}
135
136bool gicv5_is_irq_spi(unsigned int irq_num)
137{
138 return EXTRACT(INT_TYPE, irq_num) == INT_SPI;
139}
140
141void gicv5_enable_cpuif(void)
142{
143 write_icc_cr0_el1(read_icc_cr0_el1() | ICC_CR0_EL1_EN_BIT);
144 /* make sure effects are visible */
145 isb();
146}
147
148void gicv5_setup_cpuif(void)
149{
150 iaffids[platform_get_core_pos(read_mpidr_el1())] = read_icc_iaffidr_el1();
151
152 write_icc_pcr_el1(GICV5_IDLE_PRIORITY);
153
154 gicv5_enable_cpuif();
155}
156
157void gicv5_disable_cpuif(void)
158{
159 write_icc_cr0_el1(read_icc_cr0_el1() & ~ICC_CR0_EL1_EN_BIT);
160 /* make sure effects are visible */
161 isb();
162}
163
164void gicv5_save_cpuif_context(void)
165{
166 unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
167
168 cpu_ctx[core_pos].icc_cr0_el1 = read_icc_cr0_el1();
169 cpu_ctx[core_pos].icc_ppi_enabler[0] = read_icc_ppi_enabler0();
170 cpu_ctx[core_pos].icc_ppi_enabler[1] = read_icc_ppi_enabler1();
171
172 cpu_ctx[core_pos].icc_ppi_xpendr[0] = read_icc_ppi_spendr0();
173 cpu_ctx[core_pos].icc_ppi_xpendr[1] = read_icc_ppi_spendr1();
174
175 for (int i = 0; i < 15; i++) {
176 cpu_ctx[core_pos].icc_ppi_priorityr[i] = read_icc_ppi_priorityrn(i);
177 }
178
179 /* Make sure all PPIs are inactive, i.e. not suspending mid interrupt */
180 assert(read_icc_ppi_sactiver0() == 0UL && read_icc_ppi_sactiver1() == 0UL);
181}
182
183void gicv5_restore_cpuif_context(void)
184{
185 unsigned int core_pos = platform_get_core_pos(read_mpidr_el1());
186
187 write_icc_ppi_enabler0(cpu_ctx[core_pos].icc_ppi_enabler[0]);
188 write_icc_ppi_enabler1(cpu_ctx[core_pos].icc_ppi_enabler[1]);
189
190 write_icc_ppi_spendr0(cpu_ctx[core_pos].icc_ppi_xpendr[0]);
191 write_icc_ppi_spendr1(cpu_ctx[core_pos].icc_ppi_xpendr[1]);
192 /* clear interrupts that shouldn't be pending */
193 write_icc_ppi_cpendr0(~cpu_ctx[core_pos].icc_ppi_xpendr[0]);
194 write_icc_ppi_cpendr1(~cpu_ctx[core_pos].icc_ppi_xpendr[1]);
195
196 for (int i = 0; i < 15; i++) {
197 write_icc_ppi_priorityrn(i, cpu_ctx[core_pos].icc_ppi_priorityr[i]);
198 }
199
200 /* don't bother saving it, just put the same value in */
201 write_icc_pcr_el1(GICV5_IDLE_PRIORITY);
202
203 write_icc_cr0_el1(cpu_ctx[core_pos].icc_cr0_el1);
204 /* make sure effects are visible */
205 isb();
206}
207
208void gicv5_set_priority(unsigned int interrupt_id, unsigned int priority)
209{
210 unsigned irq_idx, irq_reg;
211 u_register_t priorityr;
212
213 assert(priority < (1UL << GICCDPRI_PRIORITY_WIDTH));
214 assert(is_interrupt(interrupt_id));
215
216 if (EXTRACT(INT_TYPE, interrupt_id) != INT_PPI) {
217 giccdpri(interrupt_id | INPLACE(GICCDPRI_PRIORITY, priority));
218 return;
219 }
220
221 /* it's a PPI, get rid of the INTR TYPE field */
222 interrupt_id = EXTRACT(INT_ID, interrupt_id);
223 irq_reg = interrupt_id / ICC_PPI_PRIORITYR_FIELD_NUM;
224 irq_idx = interrupt_id % ICC_PPI_PRIORITYR_FIELD_NUM;
225
226 priorityr = read_icc_ppi_priorityrn(irq_reg) &
227 ICC_PPI_PRIORITYR_FIELD_MASK <<
228 (irq_idx * ICC_PPI_PRIORITYR_FIELD_NUM);
229 write_icc_ppi_priorityrn(irq_reg, priorityr |
230 (priority << (irq_idx * ICC_PPI_PRIORITYR_FIELD_NUM)));
231}
232
233void gicv5_send_sgi(unsigned int sgi_id, unsigned int core_pos)
234{
235 giccdpend(gicv5_get_sgi_num(sgi_id, core_pos) | GICCDPEND_PENDING_BIT);
236}
237
238void gicv5_set_intr_route(unsigned int interrupt_id, unsigned int core_pos)
239{
240 assert(is_interrupt(interrupt_id));
241
242 /* PPIs are local to the CPU, can't be rerouted */
243 if (EXTRACT(INT_TYPE, interrupt_id) == INT_PPI) {
244 return;
245 }
246
247 /*
248 * The expecation is that a core will be up (CPU_ON) before it gets
249 * targetted by interrupts. Otherwise the IAFFID isn't available yet
250 * and the interrupt will be misrouted.
251 */
252 assert(iaffids[core_pos] != 0 || core_pos == 0);
253 giccdaff(INPLACE(GICCDAFF_IAFFID, iaffids[core_pos]) | interrupt_id);
254
255 /* wait for the target to take effect so retargetting an already
256 * enabled interrupt ends up in the correct destination */
257 gsbsys();
258}
259
260void gicv5_intr_enable(unsigned int interrupt_id)
261{
262 unsigned int irq_idx;
263
264 assert(is_interrupt(interrupt_id));
265
266 if (EXTRACT(INT_TYPE, interrupt_id) != INT_PPI) {
267 giccden(interrupt_id);
268 return;
269 }
270
271 /* it's a PPI, get rid of the INTR TYPE field */
272 interrupt_id = EXTRACT(INT_ID, interrupt_id);
273 irq_idx = interrupt_id % ICC_PPI_ENABLER_FIELD_NUM;
274
275 if (interrupt_id / ICC_PPI_ENABLER_FIELD_NUM == 0) {
276 write_icc_ppi_enabler0(read_icc_ppi_enabler0() | (1UL << irq_idx));
277 } else {
278 write_icc_ppi_enabler1(read_icc_ppi_enabler1() | (1UL << irq_idx));
279 }
280}
281
282void gicv5_intr_disable(unsigned int interrupt_id)
283{
284 unsigned int irq_idx;
285
286 assert(is_interrupt(interrupt_id));
287
288 if (EXTRACT(INT_TYPE, interrupt_id) != INT_PPI) {
289 giccddis(interrupt_id);
290 /* wait for the interrupt to become disabled */
291 gsbsys();
292 return;
293 }
294
295 /* it's a PPI, get rid of the INTR TYPE field */
296 interrupt_id = EXTRACT(INT_ID, interrupt_id);
297 irq_idx = interrupt_id % ICC_PPI_ENABLER_FIELD_NUM;
298
299 if (interrupt_id / ICC_PPI_ENABLER_FIELD_NUM == 0) {
300 write_icc_ppi_enabler0(read_icc_ppi_enabler0() & ~(1UL << irq_idx));
301 } else {
302 write_icc_ppi_enabler1(read_icc_ppi_enabler1() & ~(1UL << irq_idx));
303 }
304}
305
306unsigned int gicv5_acknowledge_interrupt(void)
307{
308 u_register_t iar = gicrcdia();
309 assert((iar & GICRCDIA_VALID_BIT) != 0);
310
311 /* wait for the intr ack to complete (i.e. make it Active) and refetch
312 * instructions so they don't operate on anything stale */
313 gsback();
314 isb();
315
316 return iar & ~GICRCDIA_VALID_BIT;
317}
318
319unsigned int gicv5_is_intr_pending(unsigned int interrupt_id)
320{
321 u_register_t icsr;
322 u_register_t ppi_spendr;
323
324 assert(is_interrupt(interrupt_id));
325
326 if (EXTRACT(INT_TYPE, interrupt_id) != INT_PPI) {
327 /* request interrupt information */
328 giccdrcfg(interrupt_id);
329 /* wait for the register to update */
330 isb();
331 icsr = read_icc_icsr_el1();
332
333 /* interrupt is unreachable, something has gone wrong */
334 assert((icsr & ICC_ICSR_EL1_F_BIT) == 0);
335 return !!(icsr & ICC_ICSR_EL1_PENDING_BIT);
336 }
337
338 /* it's a PPI, get rid of the INTR TYPE field */
339 interrupt_id = EXTRACT(INT_ID, interrupt_id);
340
341 if (interrupt_id / ICC_PPI_XPENDR_FIELD_NUM == 0) {
342 ppi_spendr = read_icc_ppi_spendr0();
343 } else {
344 ppi_spendr = read_icc_ppi_spendr1();
345 }
346
347 return !!(ppi_spendr & BIT(interrupt_id % ICC_PPI_XPENDR_FIELD_NUM));
348}
349
350void gicv5_intr_clear(unsigned int interrupt_id)
351{
352 unsigned int irq_idx;
353
354 assert(is_interrupt(interrupt_id));
355
356 if (EXTRACT(INT_TYPE, interrupt_id) != INT_PPI) {
357 giccdpend(interrupt_id);
358 return;
359 }
360
361 /* it's a PPI, get rid of the INTR TYPE field */
362 interrupt_id = EXTRACT(INT_ID, interrupt_id);
363 irq_idx = interrupt_id % ICC_PPI_XPENDR_FIELD_NUM;
364
365 if (interrupt_id / ICC_PPI_XPENDR_FIELD_NUM == 0) {
366 write_icc_ppi_cpendr0(read_icc_ppi_cpendr0() | (1UL << irq_idx));
367 } else {
368 write_icc_ppi_cpendr1(read_icc_ppi_cpendr1() | (1UL << irq_idx));
369 }
370}
371
372void gicv5_end_of_interrupt(unsigned int raw_iar)
373{
374 giccddi(raw_iar);
375 giccdeoi();
376 /* no isb as we won't interact with the gic before the eret */
377}
378
379/* currently a single IRS is expected and an ITS/IWB are not used */
380void gicv5_setup(void)
381{
382#if ENABLE_ASSERTIONS
383 uint32_t irs_idr2 = read_IRS_IDR2(irs_base);
384#endif
385 uint8_t id_bits = log2(ARRAY_SIZE(ist));
386 /* min_id_bits <= log2(length(ist)) <= id_bits */
387 assert(EXTRACT(IRS_IDR2_MIN_LPI_ID_BITS, irs_idr2) <= id_bits);
388 assert(EXTRACT(IRS_IDR2_ID_BITS, irs_idr2) >= id_bits);
389
390 /* make sure all ISTEs aren't enabled */
391 memset(ist, 0x0, sizeof(ist));
392
393 /* write zeroes throughout except LPI_ID_Bits which is the lowest 5 bits */
394 write_IRS_IST_CFGR(irs_base, id_bits);
395 /* make the IST valid */
396 write_IRS_IST_BASER(irs_base,
397 (((uintptr_t) &ist) & MASK(IRS_IST_BASER_ADDR)) |
398 IRS_IST_BASER_VALID_BIT);
399 WAIT_FOR_IDLE(irs_base, IRS_IST_STATUSR);
400
401 /* enable the IRS */
402 write_IRS_CR0(irs_base, IRS_CR0_IRSEN_BIT);
403 WAIT_FOR_IDLE(irs_base, IRS_CR0);
404}
405
406uint32_t gicv5_get_sgi_num(uint32_t index, unsigned int core_pos)
407{
408 assert(index <= IRQ_NUM_SGIS);
409
410 return (core_pos * IRQ_NUM_SGIS + index) | INPLACE(INT_TYPE, INT_LPI);
411}
412
413void gicv5_init(uintptr_t irs_base_addr)
414{
415 irs_base = irs_base_addr;
416}