18cef63d6SBoyan Karatotev /* 28cef63d6SBoyan Karatotev * Copyright (c) 2025, Arm Limited and Contributors. All rights reserved. 38cef63d6SBoyan Karatotev * 48cef63d6SBoyan Karatotev * SPDX-License-Identifier: BSD-3-Clause 58cef63d6SBoyan Karatotev */ 613b62814SBoyan Karatotev 7dfb37a2dSBoyan Karatotev #include <assert.h> 813b62814SBoyan Karatotev #include <cdefs.h> 913b62814SBoyan Karatotev 1082b228baSBoyan Karatotev #include <arch.h> 1113b62814SBoyan Karatotev #include <arch_features.h> 1282b228baSBoyan Karatotev #include <arch_helpers.h> 1382b228baSBoyan Karatotev 1413b62814SBoyan Karatotev #include <bl31/interrupt_mgmt.h> 1513b62814SBoyan Karatotev #include <common/debug.h> 1613b62814SBoyan Karatotev #include <drivers/arm/gicv5.h> 1713b62814SBoyan Karatotev 18*4db6bf9fSBoyan Karatotev /* 19*4db6bf9fSBoyan Karatotev * Check for a bad platform configuration. 20*4db6bf9fSBoyan Karatotev * Not expected to be called on release builds. 21*4db6bf9fSBoyan Karatotev */ 22*4db6bf9fSBoyan Karatotev static inline bool probe_component(uintptr_t base_addr, uint8_t component) 23*4db6bf9fSBoyan Karatotev { 24*4db6bf9fSBoyan Karatotev uint32_t aidr = read_iri_aidr(base_addr); 25*4db6bf9fSBoyan Karatotev 26*4db6bf9fSBoyan Karatotev if (EXTRACT(IRI_AIDR_COMPONENT, aidr) != component) { 27*4db6bf9fSBoyan Karatotev ERROR("GICv5 frame belongs to wrong component\n"); 28*4db6bf9fSBoyan Karatotev return false; 29*4db6bf9fSBoyan Karatotev } 30*4db6bf9fSBoyan Karatotev 31*4db6bf9fSBoyan Karatotev if (EXTRACT(IRI_AIDR_ARCH_MAJOR, aidr) != IRI_AIDR_ARCH_MAJOR_V5) { 32*4db6bf9fSBoyan Karatotev ERROR("Bad GICv5 major version\n"); 33*4db6bf9fSBoyan Karatotev return false; 34*4db6bf9fSBoyan Karatotev } 35*4db6bf9fSBoyan Karatotev 36*4db6bf9fSBoyan Karatotev /* there was a bump in architecture and we've not updated the driver */ 37*4db6bf9fSBoyan Karatotev assert(EXTRACT(IRI_AIDR_ARCH_MINOR, aidr) == IRI_AIDR_ARCH_MINOR_P0); 38*4db6bf9fSBoyan Karatotev 39*4db6bf9fSBoyan Karatotev return true; 40*4db6bf9fSBoyan Karatotev } 41*4db6bf9fSBoyan Karatotev 4271799209SBoyan Karatotev static inline bool iwb_domain_supported(uint32_t idr0, uint8_t domain) 4371799209SBoyan Karatotev { 4471799209SBoyan Karatotev return (EXTRACT(IWB_IDR0_DOMAINS, idr0) & (1U << domain)) != 0U; 4571799209SBoyan Karatotev } 4671799209SBoyan Karatotev 4771799209SBoyan Karatotev static void iwb_configure_domainr(uintptr_t base_addr, struct gicv5_wire_props wire) 4871799209SBoyan Karatotev { 4971799209SBoyan Karatotev uint32_t reg_offset = (wire.id % 16U) * 2U; 5071799209SBoyan Karatotev uint32_t reg_index = wire.id / 16U; 5171799209SBoyan Karatotev uint32_t val = read_iwb_wdomainr(base_addr, reg_index) & 5271799209SBoyan Karatotev ~(IWB_WDOMAINR_DOMAINX_MASK << reg_offset); 5371799209SBoyan Karatotev 5471799209SBoyan Karatotev write_iwb_wdomainr(base_addr, reg_index, val | wire.domain << reg_offset); 5571799209SBoyan Karatotev } 5671799209SBoyan Karatotev 5771799209SBoyan Karatotev static void iwb_configure_wtmr(uintptr_t base_addr, struct gicv5_wire_props wire) 5871799209SBoyan Karatotev { 5971799209SBoyan Karatotev uint32_t reg_offset = wire.id % 32U; 6071799209SBoyan Karatotev uint32_t reg_index = wire.id / 32U; 6171799209SBoyan Karatotev uint32_t val = read_iwb_wtmr(base_addr, reg_index) & ~(1U << reg_offset); 6271799209SBoyan Karatotev 6371799209SBoyan Karatotev write_iwb_wtmr(base_addr, reg_index, val | wire.tm << reg_offset); 6471799209SBoyan Karatotev } 6571799209SBoyan Karatotev 6671799209SBoyan Karatotev static void iwb_enable(const struct gicv5_iwb *config) 6771799209SBoyan Karatotev { 6871799209SBoyan Karatotev uintptr_t base_addr = config->config_frame; 6971799209SBoyan Karatotev uint32_t idr0; 7071799209SBoyan Karatotev uint16_t num_regs; 7171799209SBoyan Karatotev 72*4db6bf9fSBoyan Karatotev assert(probe_component(base_addr, IRI_AIDR_COMPONENT_IWB)); 73*4db6bf9fSBoyan Karatotev 7471799209SBoyan Karatotev idr0 = read_iwb_idr0(base_addr); 7571799209SBoyan Karatotev num_regs = EXTRACT(IWB_IDR0_IWRANGE, idr0) + 1U; 7671799209SBoyan Karatotev 7771799209SBoyan Karatotev /* initialise all wires as disabled */ 7871799209SBoyan Karatotev for (int i = 0U; i < num_regs; i++) { 7971799209SBoyan Karatotev write_iwb_wenabler(base_addr, i, 0U); 8071799209SBoyan Karatotev } 8171799209SBoyan Karatotev 8271799209SBoyan Karatotev /* default all wires to the NS domain */ 8371799209SBoyan Karatotev for (int i = 0U; i < num_regs * 2; i++) { 8471799209SBoyan Karatotev write_iwb_wdomainr(base_addr, i, 0x55555555); 8571799209SBoyan Karatotev } 8671799209SBoyan Karatotev 8771799209SBoyan Karatotev for (uint32_t i = 0U; i < config->num_wires; i++) { 8871799209SBoyan Karatotev assert(iwb_domain_supported(idr0, config->wires[i].domain)); 8971799209SBoyan Karatotev assert(config->wires[i].id <= num_regs * 32); 9071799209SBoyan Karatotev 9171799209SBoyan Karatotev iwb_configure_domainr(base_addr, config->wires[i]); 9271799209SBoyan Karatotev iwb_configure_wtmr(base_addr, config->wires[i]); 9371799209SBoyan Karatotev } 9471799209SBoyan Karatotev 9571799209SBoyan Karatotev write_iwb_cr0(base_addr, IWB_CR0_IWBEN_BIT); 9671799209SBoyan Karatotev WAIT_FOR_IDLE_IWB_WENABLE_STATUSR(base_addr); 9771799209SBoyan Karatotev WAIT_FOR_IDLE_IWB_WDOMAIN_STATUSR(base_addr); 9871799209SBoyan Karatotev WAIT_FOR_IDLE_IWB_CR0(base_addr); 9971799209SBoyan Karatotev } 10071799209SBoyan Karatotev 101dfb37a2dSBoyan Karatotev static void irs_configure_wire(uintptr_t base_addr, uint32_t wire, uint8_t domain) 102dfb37a2dSBoyan Karatotev { 103dfb37a2dSBoyan Karatotev write_irs_spi_selr(base_addr, wire); 104dfb37a2dSBoyan Karatotev WAIT_FOR_VIDLE_IRS_SPI_STATUSR(base_addr); 105dfb37a2dSBoyan Karatotev 106dfb37a2dSBoyan Karatotev write_irs_spi_domainr(base_addr, domain); 107dfb37a2dSBoyan Karatotev WAIT_FOR_VIDLE_IRS_SPI_STATUSR(base_addr); 108dfb37a2dSBoyan Karatotev } 109dfb37a2dSBoyan Karatotev 110dfb37a2dSBoyan Karatotev static void irs_enable(const struct gicv5_irs *config) 111dfb37a2dSBoyan Karatotev { 112dfb37a2dSBoyan Karatotev uint32_t spi_base, spi_range; 113dfb37a2dSBoyan Karatotev uintptr_t base_addr = config->el3_config_frame; 114dfb37a2dSBoyan Karatotev 115dfb37a2dSBoyan Karatotev spi_base = EXTRACT(IRS_IDR7_SPI_BASE, read_irs_idr7(base_addr)); 116dfb37a2dSBoyan Karatotev spi_range = EXTRACT(IRS_IDR6_SPI_IRS_RANGE, read_irs_idr6(base_addr)); 117dfb37a2dSBoyan Karatotev 118*4db6bf9fSBoyan Karatotev assert(probe_component(base_addr, IRI_AIDR_COMPONENT_IRS)); 119*4db6bf9fSBoyan Karatotev 120dfb37a2dSBoyan Karatotev if (spi_range == 0U) { 121dfb37a2dSBoyan Karatotev assert(config->num_spis == 0U); 122dfb37a2dSBoyan Karatotev } 123dfb37a2dSBoyan Karatotev 124dfb37a2dSBoyan Karatotev /* default all wires to the NS domain */ 125dfb37a2dSBoyan Karatotev for (uint32_t i = spi_base; i < spi_base + spi_range; i++) { 126dfb37a2dSBoyan Karatotev irs_configure_wire(base_addr, i, INTDMN_NS); 127dfb37a2dSBoyan Karatotev } 128dfb37a2dSBoyan Karatotev 129dfb37a2dSBoyan Karatotev for (uint32_t i = 0U; i < config->num_spis; i++) { 130dfb37a2dSBoyan Karatotev assert((config->spis[i].id >= spi_base) && 131dfb37a2dSBoyan Karatotev (config->spis[i].id < spi_base + spi_range)); 132dfb37a2dSBoyan Karatotev 133dfb37a2dSBoyan Karatotev irs_configure_wire(base_addr, config->spis[i].id, config->spis[i].domain); 134dfb37a2dSBoyan Karatotev 135dfb37a2dSBoyan Karatotev /* don't (can't) configure TM of wires for other domains */ 136dfb37a2dSBoyan Karatotev if (config->spis[i].domain == INTDMN_EL3) { 137dfb37a2dSBoyan Karatotev write_irs_spi_cfgr(base_addr, config->spis[i].tm); 138dfb37a2dSBoyan Karatotev WAIT_FOR_VIDLE_IRS_SPI_STATUSR(base_addr); 139dfb37a2dSBoyan Karatotev } 140dfb37a2dSBoyan Karatotev } 141dfb37a2dSBoyan Karatotev } 142dfb37a2dSBoyan Karatotev 14313b62814SBoyan Karatotev void __init gicv5_driver_init(void) 14413b62814SBoyan Karatotev { 14571799209SBoyan Karatotev for (size_t i = 0U; i < plat_gicv5_driver_data.num_iwbs; i++) { 14671799209SBoyan Karatotev iwb_enable(&plat_gicv5_driver_data.iwbs[i]); 14771799209SBoyan Karatotev } 14871799209SBoyan Karatotev 149dfb37a2dSBoyan Karatotev for (size_t i = 0U; i < plat_gicv5_driver_data.num_irss; i++) { 150dfb37a2dSBoyan Karatotev irs_enable(&plat_gicv5_driver_data.irss[i]); 151dfb37a2dSBoyan Karatotev } 15213b62814SBoyan Karatotev } 15313b62814SBoyan Karatotev 15413b62814SBoyan Karatotev /* 15513b62814SBoyan Karatotev * There exists a theoretical configuration where FEAT_RME is enabled 15613b62814SBoyan Karatotev * without using TrustZone (i.e., no Secure world present). Currently, 15713b62814SBoyan Karatotev * there is no reliable mechanism to detect this scenario at runtime. 15813b62814SBoyan Karatotev * 15913b62814SBoyan Karatotev * TODO: Add support for this configuration in the future if required. 16013b62814SBoyan Karatotev */ 16113b62814SBoyan Karatotev bool gicv5_has_interrupt_type(unsigned int type) 16213b62814SBoyan Karatotev { 16313b62814SBoyan Karatotev switch (type) { 16413b62814SBoyan Karatotev case INTR_TYPE_EL3: 16513b62814SBoyan Karatotev case INTR_TYPE_S_EL1: 16613b62814SBoyan Karatotev case INTR_TYPE_NS: 16713b62814SBoyan Karatotev return true; 16813b62814SBoyan Karatotev case INTR_TYPE_RL: 16913b62814SBoyan Karatotev return is_feat_rme_supported(); 17013b62814SBoyan Karatotev default: 17113b62814SBoyan Karatotev return false; 17213b62814SBoyan Karatotev } 17313b62814SBoyan Karatotev } 17413b62814SBoyan Karatotev 17513b62814SBoyan Karatotev uint8_t gicv5_get_pending_interrupt_type(void) 17613b62814SBoyan Karatotev { 17713b62814SBoyan Karatotev /* there is no pending interrupt expected */ 17813b62814SBoyan Karatotev return INTR_TYPE_INVAL; 17913b62814SBoyan Karatotev } 18082b228baSBoyan Karatotev 18182b228baSBoyan Karatotev /* TODO: these will probably end up contexted. Make Linux work for now */ 18282b228baSBoyan Karatotev void gicv5_enable_ppis(void) 18382b228baSBoyan Karatotev { 18482b228baSBoyan Karatotev uint64_t domainr = 0U; 18582b228baSBoyan Karatotev 18682b228baSBoyan Karatotev /* the only ones described in the device tree at the moment */ 18782b228baSBoyan Karatotev write_icc_ppi_domainr(domainr, PPI_PMUIRQ, INTDMN_NS); 18882b228baSBoyan Karatotev write_icc_ppi_domainr(domainr, PPI_GICMNT, INTDMN_NS); 18982b228baSBoyan Karatotev write_icc_ppi_domainr(domainr, PPI_CNTHP, INTDMN_NS); 19082b228baSBoyan Karatotev write_icc_ppi_domainr(domainr, PPI_CNTV, INTDMN_NS); 19182b228baSBoyan Karatotev write_icc_ppi_domainr(domainr, PPI_CNTPS, INTDMN_NS); 19282b228baSBoyan Karatotev write_icc_ppi_domainr(domainr, PPI_CNTP, INTDMN_NS); 19382b228baSBoyan Karatotev 19482b228baSBoyan Karatotev write_icc_ppi_domainr0_el3(domainr); 19582b228baSBoyan Karatotev } 196