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