xref: /rk3399_ARM-atf/drivers/arm/gicv5/gicv5_main.c (revision d154fe2bf0616f2e78965207c32b6e83ba12292e)
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  */
probe_component(uintptr_t base_addr,uint8_t component)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 
iwb_domain_supported(uint32_t idr0,uint8_t domain)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 
iwb_configure_domainr(uintptr_t base_addr,struct gicv5_wire_props wire)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 
iwb_configure_wtmr(uintptr_t base_addr,struct gicv5_wire_props wire)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 
iwb_enable(const struct gicv5_iwb * config)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 
irs_configure_wire(uintptr_t base_addr,uint32_t wire,uint8_t domain)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 
irs_enable(const struct gicv5_irs * config)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 
gicv5_driver_init(void)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  */
gicv5_has_interrupt_type(unsigned int type)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 
gicv5_get_pending_interrupt_type(void)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 */
gicv5_enable_ppis(void)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