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 */ 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 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 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 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 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 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 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 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 */ 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 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 */ 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