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*71799209SBoyan Karatotev static inline bool iwb_domain_supported(uint32_t idr0, uint8_t domain) 19*71799209SBoyan Karatotev { 20*71799209SBoyan Karatotev return (EXTRACT(IWB_IDR0_DOMAINS, idr0) & (1U << domain)) != 0U; 21*71799209SBoyan Karatotev } 22*71799209SBoyan Karatotev 23*71799209SBoyan Karatotev static void iwb_configure_domainr(uintptr_t base_addr, struct gicv5_wire_props wire) 24*71799209SBoyan Karatotev { 25*71799209SBoyan Karatotev uint32_t reg_offset = (wire.id % 16U) * 2U; 26*71799209SBoyan Karatotev uint32_t reg_index = wire.id / 16U; 27*71799209SBoyan Karatotev uint32_t val = read_iwb_wdomainr(base_addr, reg_index) & 28*71799209SBoyan Karatotev ~(IWB_WDOMAINR_DOMAINX_MASK << reg_offset); 29*71799209SBoyan Karatotev 30*71799209SBoyan Karatotev write_iwb_wdomainr(base_addr, reg_index, val | wire.domain << reg_offset); 31*71799209SBoyan Karatotev } 32*71799209SBoyan Karatotev 33*71799209SBoyan Karatotev static void iwb_configure_wtmr(uintptr_t base_addr, struct gicv5_wire_props wire) 34*71799209SBoyan Karatotev { 35*71799209SBoyan Karatotev uint32_t reg_offset = wire.id % 32U; 36*71799209SBoyan Karatotev uint32_t reg_index = wire.id / 32U; 37*71799209SBoyan Karatotev uint32_t val = read_iwb_wtmr(base_addr, reg_index) & ~(1U << reg_offset); 38*71799209SBoyan Karatotev 39*71799209SBoyan Karatotev write_iwb_wtmr(base_addr, reg_index, val | wire.tm << reg_offset); 40*71799209SBoyan Karatotev } 41*71799209SBoyan Karatotev 42*71799209SBoyan Karatotev static void iwb_enable(const struct gicv5_iwb *config) 43*71799209SBoyan Karatotev { 44*71799209SBoyan Karatotev uintptr_t base_addr = config->config_frame; 45*71799209SBoyan Karatotev uint32_t idr0; 46*71799209SBoyan Karatotev uint16_t num_regs; 47*71799209SBoyan Karatotev 48*71799209SBoyan Karatotev idr0 = read_iwb_idr0(base_addr); 49*71799209SBoyan Karatotev num_regs = EXTRACT(IWB_IDR0_IWRANGE, idr0) + 1U; 50*71799209SBoyan Karatotev 51*71799209SBoyan Karatotev /* initialise all wires as disabled */ 52*71799209SBoyan Karatotev for (int i = 0U; i < num_regs; i++) { 53*71799209SBoyan Karatotev write_iwb_wenabler(base_addr, i, 0U); 54*71799209SBoyan Karatotev } 55*71799209SBoyan Karatotev 56*71799209SBoyan Karatotev /* default all wires to the NS domain */ 57*71799209SBoyan Karatotev for (int i = 0U; i < num_regs * 2; i++) { 58*71799209SBoyan Karatotev write_iwb_wdomainr(base_addr, i, 0x55555555); 59*71799209SBoyan Karatotev } 60*71799209SBoyan Karatotev 61*71799209SBoyan Karatotev for (uint32_t i = 0U; i < config->num_wires; i++) { 62*71799209SBoyan Karatotev assert(iwb_domain_supported(idr0, config->wires[i].domain)); 63*71799209SBoyan Karatotev assert(config->wires[i].id <= num_regs * 32); 64*71799209SBoyan Karatotev 65*71799209SBoyan Karatotev iwb_configure_domainr(base_addr, config->wires[i]); 66*71799209SBoyan Karatotev iwb_configure_wtmr(base_addr, config->wires[i]); 67*71799209SBoyan Karatotev } 68*71799209SBoyan Karatotev 69*71799209SBoyan Karatotev write_iwb_cr0(base_addr, IWB_CR0_IWBEN_BIT); 70*71799209SBoyan Karatotev WAIT_FOR_IDLE_IWB_WENABLE_STATUSR(base_addr); 71*71799209SBoyan Karatotev WAIT_FOR_IDLE_IWB_WDOMAIN_STATUSR(base_addr); 72*71799209SBoyan Karatotev WAIT_FOR_IDLE_IWB_CR0(base_addr); 73*71799209SBoyan Karatotev } 74*71799209SBoyan Karatotev 75dfb37a2dSBoyan Karatotev static void irs_configure_wire(uintptr_t base_addr, uint32_t wire, uint8_t domain) 76dfb37a2dSBoyan Karatotev { 77dfb37a2dSBoyan Karatotev write_irs_spi_selr(base_addr, wire); 78dfb37a2dSBoyan Karatotev WAIT_FOR_VIDLE_IRS_SPI_STATUSR(base_addr); 79dfb37a2dSBoyan Karatotev 80dfb37a2dSBoyan Karatotev write_irs_spi_domainr(base_addr, domain); 81dfb37a2dSBoyan Karatotev WAIT_FOR_VIDLE_IRS_SPI_STATUSR(base_addr); 82dfb37a2dSBoyan Karatotev } 83dfb37a2dSBoyan Karatotev 84dfb37a2dSBoyan Karatotev static void irs_enable(const struct gicv5_irs *config) 85dfb37a2dSBoyan Karatotev { 86dfb37a2dSBoyan Karatotev uint32_t spi_base, spi_range; 87dfb37a2dSBoyan Karatotev uintptr_t base_addr = config->el3_config_frame; 88dfb37a2dSBoyan Karatotev 89dfb37a2dSBoyan Karatotev spi_base = EXTRACT(IRS_IDR7_SPI_BASE, read_irs_idr7(base_addr)); 90dfb37a2dSBoyan Karatotev spi_range = EXTRACT(IRS_IDR6_SPI_IRS_RANGE, read_irs_idr6(base_addr)); 91dfb37a2dSBoyan Karatotev 92dfb37a2dSBoyan Karatotev if (spi_range == 0U) { 93dfb37a2dSBoyan Karatotev assert(config->num_spis == 0U); 94dfb37a2dSBoyan Karatotev } 95dfb37a2dSBoyan Karatotev 96dfb37a2dSBoyan Karatotev /* default all wires to the NS domain */ 97dfb37a2dSBoyan Karatotev for (uint32_t i = spi_base; i < spi_base + spi_range; i++) { 98dfb37a2dSBoyan Karatotev irs_configure_wire(base_addr, i, INTDMN_NS); 99dfb37a2dSBoyan Karatotev } 100dfb37a2dSBoyan Karatotev 101dfb37a2dSBoyan Karatotev for (uint32_t i = 0U; i < config->num_spis; i++) { 102dfb37a2dSBoyan Karatotev assert((config->spis[i].id >= spi_base) && 103dfb37a2dSBoyan Karatotev (config->spis[i].id < spi_base + spi_range)); 104dfb37a2dSBoyan Karatotev 105dfb37a2dSBoyan Karatotev irs_configure_wire(base_addr, config->spis[i].id, config->spis[i].domain); 106dfb37a2dSBoyan Karatotev 107dfb37a2dSBoyan Karatotev /* don't (can't) configure TM of wires for other domains */ 108dfb37a2dSBoyan Karatotev if (config->spis[i].domain == INTDMN_EL3) { 109dfb37a2dSBoyan Karatotev write_irs_spi_cfgr(base_addr, config->spis[i].tm); 110dfb37a2dSBoyan Karatotev WAIT_FOR_VIDLE_IRS_SPI_STATUSR(base_addr); 111dfb37a2dSBoyan Karatotev } 112dfb37a2dSBoyan Karatotev } 113dfb37a2dSBoyan Karatotev } 114dfb37a2dSBoyan Karatotev 11513b62814SBoyan Karatotev void __init gicv5_driver_init(void) 11613b62814SBoyan Karatotev { 117*71799209SBoyan Karatotev for (size_t i = 0U; i < plat_gicv5_driver_data.num_iwbs; i++) { 118*71799209SBoyan Karatotev iwb_enable(&plat_gicv5_driver_data.iwbs[i]); 119*71799209SBoyan Karatotev } 120*71799209SBoyan Karatotev 121dfb37a2dSBoyan Karatotev for (size_t i = 0U; i < plat_gicv5_driver_data.num_irss; i++) { 122dfb37a2dSBoyan Karatotev irs_enable(&plat_gicv5_driver_data.irss[i]); 123dfb37a2dSBoyan Karatotev } 12413b62814SBoyan Karatotev } 12513b62814SBoyan Karatotev 12613b62814SBoyan Karatotev /* 12713b62814SBoyan Karatotev * There exists a theoretical configuration where FEAT_RME is enabled 12813b62814SBoyan Karatotev * without using TrustZone (i.e., no Secure world present). Currently, 12913b62814SBoyan Karatotev * there is no reliable mechanism to detect this scenario at runtime. 13013b62814SBoyan Karatotev * 13113b62814SBoyan Karatotev * TODO: Add support for this configuration in the future if required. 13213b62814SBoyan Karatotev */ 13313b62814SBoyan Karatotev bool gicv5_has_interrupt_type(unsigned int type) 13413b62814SBoyan Karatotev { 13513b62814SBoyan Karatotev switch (type) { 13613b62814SBoyan Karatotev case INTR_TYPE_EL3: 13713b62814SBoyan Karatotev case INTR_TYPE_S_EL1: 13813b62814SBoyan Karatotev case INTR_TYPE_NS: 13913b62814SBoyan Karatotev return true; 14013b62814SBoyan Karatotev case INTR_TYPE_RL: 14113b62814SBoyan Karatotev return is_feat_rme_supported(); 14213b62814SBoyan Karatotev default: 14313b62814SBoyan Karatotev return false; 14413b62814SBoyan Karatotev } 14513b62814SBoyan Karatotev } 14613b62814SBoyan Karatotev 14713b62814SBoyan Karatotev uint8_t gicv5_get_pending_interrupt_type(void) 14813b62814SBoyan Karatotev { 14913b62814SBoyan Karatotev /* there is no pending interrupt expected */ 15013b62814SBoyan Karatotev return INTR_TYPE_INVAL; 15113b62814SBoyan Karatotev } 15282b228baSBoyan Karatotev 15382b228baSBoyan Karatotev /* TODO: these will probably end up contexted. Make Linux work for now */ 15482b228baSBoyan Karatotev void gicv5_enable_ppis(void) 15582b228baSBoyan Karatotev { 15682b228baSBoyan Karatotev uint64_t domainr = 0U; 15782b228baSBoyan Karatotev 15882b228baSBoyan Karatotev /* the only ones described in the device tree at the moment */ 15982b228baSBoyan Karatotev write_icc_ppi_domainr(domainr, PPI_PMUIRQ, INTDMN_NS); 16082b228baSBoyan Karatotev write_icc_ppi_domainr(domainr, PPI_GICMNT, INTDMN_NS); 16182b228baSBoyan Karatotev write_icc_ppi_domainr(domainr, PPI_CNTHP, INTDMN_NS); 16282b228baSBoyan Karatotev write_icc_ppi_domainr(domainr, PPI_CNTV, INTDMN_NS); 16382b228baSBoyan Karatotev write_icc_ppi_domainr(domainr, PPI_CNTPS, INTDMN_NS); 16482b228baSBoyan Karatotev write_icc_ppi_domainr(domainr, PPI_CNTP, INTDMN_NS); 16582b228baSBoyan Karatotev 16682b228baSBoyan Karatotev write_icc_ppi_domainr0_el3(domainr); 16782b228baSBoyan Karatotev } 168