xref: /rk3399_ARM-atf/plat/arm/board/automotive_rd/platform/rdaspen/ras/rdaspen_ras.c (revision 10d33abec01d820ad749fdc61afc8dbc6b702b05)
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