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