1 /*
2 * Copyright (c) 2025-2026, Arm Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <bl31/interrupt_mgmt.h>
8 #include <lib/extensions/ras.h>
9 #include <plat/arm/css/common/css_pm.h>
10 #include <plat/common/platform.h>
11
12 #include <cper.h>
13 #include <platform_def.h>
14 #include <rdaspen_ras.h>
15
16 #define CORE_RAM_ERR_RECORD U(1)
17
plat_handle_uncontainable_ea(void)18 void plat_handle_uncontainable_ea(void)
19 {
20 uint64_t esr = read_esr_el3();
21
22 INFO("RAS: Uncontainable RAS Error Handler (EL3)\n");
23 VERBOSE("RAS: ESR_EL3 = 0x%lx (EC = 0x%lx, FSC = 0x%lx)\n",
24 esr, (esr >> 26) & 0x3F, esr & 0x3F);
25 /* There is no mitigation to UC errors and platform must panic */
26 panic();
27 }
28
29 /* Initialise CPU RAS features for FHI configuration */
rdaspen_setup_cpu_ras_config(void)30 static void rdaspen_setup_cpu_ras_config(void)
31 {
32 uint64_t reg_erxctlr_el1 = 0;
33 unsigned int core_pos = plat_my_core_pos();
34
35 /* Select Error Record 1, Error record 0 is for the DSU */
36 write_errselr_el1(CORE_RAM_ERR_RECORD);
37 reg_erxctlr_el1 = read_erxctlr_el1();
38
39 /* Enable FI, CFI, UI for all configuration */
40 reg_erxctlr_el1 |= ERX_CTRL_UI_ENABLE | ERX_CTRL_FI_ENABLE | ERX_CTRL_CFI_ENABLE |
41 ERX_CTRL_ED_ENABLE;
42
43 write_erxctlr_el1(reg_erxctlr_el1);
44 VERBOSE("RAS: Platform RAS Init on CPU %u : ERXCTLR_EL1=0x%lx successful\n",
45 core_pos, reg_erxctlr_el1);
46 }
47
48 /*
49 * Initialization function for the framework.
50 *
51 * Registers RAS config provided by the platform and then configures and
52 * enables interrupt for each registered error.
53 */
rdaspen_ras_init_per_cpu(void)54 void rdaspen_ras_init_per_cpu(void)
55 {
56 rdaspen_setup_cpu_ras_config();
57 }
58
59 /*
60 * Extend default CPU power-on behavior.
61 *
62 * Wraps the default CSS pwr_domain_on_finish handler to ensure
63 * platform-specific RAS initialization is performed on each CPU as it comes
64 * online via PSCI.
65 *
66 */
rdaspen_css_pwr_domain_on_finish(const psci_power_state_t * target_state)67 void rdaspen_css_pwr_domain_on_finish(const psci_power_state_t *target_state)
68 {
69 css_pwr_domain_on_finish(target_state);
70 rdaspen_ras_init_per_cpu();
71 }
72
73 /*
74 * Rising-edge SPI: clear any stale pending,
75 * then set to trigger a new edge.
76 */
rdaspen_set_error_notification_irq(unsigned int irq)77 static void rdaspen_set_error_notification_irq(unsigned int irq)
78 {
79 plat_ic_clear_interrupt_pending(irq);
80 plat_ic_set_interrupt_pending(irq);
81 VERBOSE("RAS: IRQ %u triggered to EL1\n", irq);
82 }
83
rdaspen_ras_cpu_intr_handler(const struct err_record_info * err_rec,int probe_data,const struct err_handler_data * const data)84 static int rdaspen_ras_cpu_intr_handler(
85 const struct err_record_info *err_rec,
86 int probe_data,
87 const struct err_handler_data *const data)
88 {
89 uint32_t errx_status;
90 (void)err_rec;
91 (void)probe_data;
92
93 if (data == NULL)
94 return -1;
95
96 WARN("CPU RAS: Interrupt Received ID: 0x%x\n", data->interrupt);
97
98 /* Warning: Incase Error Record is already cleared this prevents queued interrupts */
99 if ((read_erxstatus_el1() & (ERX_STATUS_V)) == 0) {
100 WARN("CPU RAS: Spurious RAS interrupt observed %x\n", data->interrupt);
101 plat_ic_end_of_interrupt(data->interrupt);
102 return -1;
103 }
104
105 WARN("CPU RAS: Error Status value : 0x%lx\n", read_erxstatus_el1());
106
107 size_t esb_len =
108 cper_write_cpu_record((void *)RDASPEN_CPER_BUF_BASE, RDASPEN_CPER_BUF_SIZE);
109
110 if (esb_len) {
111 print_cper((const void *)RDASPEN_CPER_BUF_BASE);
112 flush_dcache_range(RDASPEN_CPER_BUF_BASE, esb_len);
113 VERBOSE("RAS: ESB written len=%zu @0x%lx\n",
114 esb_len, (unsigned long)RDASPEN_CPER_BUF_BASE);
115 }
116
117 /* Clear the Inband Error. Inband Errors are CE and DE */
118 errx_status = read_erxstatus_el1();
119 write_erxstatus_el1(errx_status);
120 clear_cpu_erx_misc0_register();
121
122 #if FAULT_INJECTION_SUPPORT
123 /* Pseudo generation registers are cleared to avoid interrupt flood from NS */
124 clear_cpu_pfg_ctrl_register();
125 /* Injected Errors cannot be stopped until these registers are cleared */
126 clear_cpu_pfg_cdn_register();
127 #endif /* FAULT_INJECTION_SUPPORT */
128
129 WARN("CPU RAS: Error Status Clear Value : 0x%lx\n", read_erxstatus_el1());
130
131 plat_ic_end_of_interrupt(data->interrupt);
132 rdaspen_set_error_notification_irq(ERROR_NOTIFICATION_IRQ);
133 return 0;
134 }
135
136 /* RAS error record list definition, used by the common RAS framework. */
137 static struct err_record_info plat_err_records[] = {
138 ERR_RECORD_SYSREG_V1(0, 1, NULL, &rdaspen_ras_cpu_intr_handler, 0),
139 };
140
141 /* RAS error interrupt list definition, used by the common RAS framework. */
142 static struct ras_interrupt plat_ras_interrupts[] = {
143 {
144 /* This Fault IRQ is common for all Arm platforms - PPI 17 */
145 .intr_number = CPU_FAULT_IRQ,
146 .err_record = &plat_err_records[0],
147 },
148 };
149
150 /* Registers the RAS error record list with common RAS framework. */
151 REGISTER_ERR_RECORD_INFO(plat_err_records);
152 /* Registers the RAS error interrupt info list with common RAS framework. */
153 REGISTER_RAS_INTERRUPTS(plat_ras_interrupts);
154