1 /* 2 * Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved. 3 * Copyright (c) 2020, NVIDIA Corporation. All rights reserved. 4 * 5 * SPDX-License-Identifier: BSD-3-Clause 6 */ 7 8 #include <assert.h> 9 10 #include <arch_helpers.h> 11 #include <bl31/interrupt_mgmt.h> 12 #include <common/bl_common.h> 13 #include <common/debug.h> 14 #include <context.h> 15 #include <denver.h> 16 #include <lib/el3_runtime/context_mgmt.h> 17 #include <plat/common/platform.h> 18 19 #if ENABLE_WDT_LEGACY_FIQ_HANDLING 20 #include <flowctrl.h> 21 #endif 22 #include <tegra_def.h> 23 #include <tegra_private.h> 24 25 /* Legacy FIQ used by earlier Tegra platforms */ 26 #define LEGACY_FIQ_PPI_WDT 28U 27 28 /******************************************************************************* 29 * Static variables 30 ******************************************************************************/ 31 static uint64_t ns_fiq_handler_addr; 32 static uint32_t fiq_handler_active; 33 static pcpu_fiq_state_t fiq_state[PLATFORM_CORE_COUNT]; 34 35 /******************************************************************************* 36 * Handler for FIQ interrupts 37 ******************************************************************************/ 38 static uint64_t tegra_fiq_interrupt_handler(uint32_t id, 39 uint32_t flags, 40 void *handle, 41 void *cookie) 42 { 43 cpu_context_t *ctx = cm_get_context(NON_SECURE); 44 el3_state_t *el3state_ctx = get_el3state_ctx(ctx); 45 uint32_t cpu = plat_my_core_pos(); 46 uint32_t irq; 47 48 (void)id; 49 (void)flags; 50 (void)handle; 51 (void)cookie; 52 53 /* 54 * Read the pending interrupt ID 55 */ 56 irq = plat_ic_get_pending_interrupt_id(); 57 58 /* 59 * Jump to NS world only if the NS world's FIQ handler has 60 * been registered 61 */ 62 if (ns_fiq_handler_addr != 0U) { 63 64 /* 65 * The FIQ was generated when the execution was in the non-secure 66 * world. Save the context registers to start with. 67 */ 68 cm_el1_sysregs_context_save(NON_SECURE); 69 70 /* 71 * Save elr_el3 and spsr_el3 from the saved context, and overwrite 72 * the context with the NS fiq_handler_addr and SPSR value. 73 */ 74 fiq_state[cpu].elr_el3 = read_ctx_reg((el3state_ctx), (uint32_t)(CTX_ELR_EL3)); 75 fiq_state[cpu].spsr_el3 = read_ctx_reg((el3state_ctx), (uint32_t)(CTX_SPSR_EL3)); 76 77 /* 78 * Set the new ELR to continue execution in the NS world using the 79 * FIQ handler registered earlier. 80 */ 81 cm_set_elr_el3(NON_SECURE, ns_fiq_handler_addr); 82 } 83 84 #if ENABLE_WDT_LEGACY_FIQ_HANDLING 85 /* 86 * Tegra platforms that use LEGACY_FIQ as the watchdog timer FIQ 87 * need to issue an IPI to other CPUs, to allow them to handle 88 * the "system hung" scenario. This interrupt is passed to the GICD 89 * via the Flow Controller. So, once we receive this interrupt, 90 * disable the routing so that we can mark it as "complete" in the 91 * GIC later. 92 */ 93 if (irq == LEGACY_FIQ_PPI_WDT) { 94 tegra_fc_disable_fiq_to_ccplex_routing(); 95 } 96 #endif 97 98 /* 99 * Mark this interrupt as complete to avoid a FIQ storm. 100 */ 101 if (irq < 1022U) { 102 (void)plat_ic_acknowledge_interrupt(); 103 plat_ic_end_of_interrupt(irq); 104 } 105 106 return 0; 107 } 108 109 /******************************************************************************* 110 * Setup handler for FIQ interrupts 111 ******************************************************************************/ 112 void tegra_fiq_handler_setup(void) 113 { 114 uint32_t flags; 115 int32_t rc; 116 117 /* return if already registered */ 118 if (fiq_handler_active == 0U) { 119 /* 120 * Register an interrupt handler for FIQ interrupts generated for 121 * NS interrupt sources 122 */ 123 flags = 0U; 124 set_interrupt_rm_flag((flags), (NON_SECURE)); 125 rc = register_interrupt_type_handler(INTR_TYPE_EL3, 126 tegra_fiq_interrupt_handler, 127 flags); 128 if (rc != 0) { 129 panic(); 130 } 131 132 /* handler is now active */ 133 fiq_handler_active = 1; 134 } 135 } 136 137 /******************************************************************************* 138 * Validate and store NS world's entrypoint for FIQ interrupts 139 ******************************************************************************/ 140 void tegra_fiq_set_ns_entrypoint(uint64_t entrypoint) 141 { 142 ns_fiq_handler_addr = entrypoint; 143 } 144 145 /******************************************************************************* 146 * Handler to return the NS EL1/EL0 CPU context 147 ******************************************************************************/ 148 int32_t tegra_fiq_get_intr_context(void) 149 { 150 cpu_context_t *ctx = cm_get_context(NON_SECURE); 151 gp_regs_t *gpregs_ctx = get_gpregs_ctx(ctx); 152 const el1_sysregs_t *el1state_ctx = get_el1_sysregs_ctx(ctx); 153 uint32_t cpu = plat_my_core_pos(); 154 uint64_t val; 155 156 /* 157 * We store the ELR_EL3, SPSR_EL3, SP_EL0 and SP_EL1 registers so 158 * that el3_exit() sends these values back to the NS world. 159 */ 160 write_ctx_reg((gpregs_ctx), (uint32_t)(CTX_GPREG_X0), (fiq_state[cpu].elr_el3)); 161 write_ctx_reg((gpregs_ctx), (uint32_t)(CTX_GPREG_X1), (fiq_state[cpu].spsr_el3)); 162 163 val = read_ctx_reg((gpregs_ctx), (uint32_t)(CTX_GPREG_SP_EL0)); 164 write_ctx_reg((gpregs_ctx), (uint32_t)(CTX_GPREG_X2), (val)); 165 166 val = read_ctx_reg((el1state_ctx), (uint32_t)(CTX_SP_EL1)); 167 write_ctx_reg((gpregs_ctx), (uint32_t)(CTX_GPREG_X3), (val)); 168 169 return 0; 170 } 171