blob: b52a35c9d2670b28129869de7301080300ff92a2 [file] [log] [blame]
/*
* Copyright (c) 2020, NVIDIA Corporation. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <debug.h>
#include <events.h>
#include <lib/irq.h>
#include <power_management.h>
#include <sdei.h>
#include <test_helpers.h>
#include <tftf_lib.h>
#include <platform.h>
#include "include/tegra194_ras.h"
/* Macro to indicate CPU to start an action */
#define START U(0xAA55)
/* Global flag to indicate that a fault was received */
static volatile uint64_t fault_received;
/* SDEI handler to receive RAS UC errors */
extern int serror_sdei_event_handler(int ev, uint64_t arg);
/* NVIDIA Pseudo fault generation registers */
#define T194_ERXPFGCTL_EL1 S3_0_C15_C1_4
#define T194_ERXPFGCDN_EL1 S3_0_C15_C1_6
DEFINE_RENAME_SYSREG_RW_FUNCS(erxpfgctl_el1, T194_ERXPFGCTL_EL1)
DEFINE_RENAME_SYSREG_RW_FUNCS(erxpfgcdn_el1, T194_ERXPFGCDN_EL1)
/* Instantiate RAS nodes */
PER_CORE_RAS_NODE_LIST(DEFINE_ONE_RAS_NODE);
PER_CLUSTER_RAS_NODE_LIST(DEFINE_ONE_RAS_NODE);
SCF_L3_BANK_RAS_NODE_LIST(DEFINE_ONE_RAS_NODE);
CCPLEX_RAS_NODE_LIST(DEFINE_ONE_RAS_NODE);
/* Instantiate RAS node groups */
static __unused struct ras_aux_data per_core_ras_group[] = {
PER_CORE_RAS_GROUP_NODES
};
static __unused struct ras_aux_data per_cluster_ras_group[] = {
PER_CLUSTER_RAS_GROUP_NODES
};
static __unused struct ras_aux_data scf_l3_ras_group[] = {
SCF_L3_BANK_RAS_GROUP_NODES
};
static __unused struct ras_aux_data ccplex_ras_group[] = {
CCPLEX_RAS_GROUP_NODES
};
/*
* we have same probe and handler for each error record group, use a macro to
* simply the record definition.
*/
#define ADD_ONE_ERR_GROUP(errselr_start, group) \
{ \
.sysreg.idx_start = (errselr_start), \
.sysreg.num_idx = ARRAY_SIZE((group)), \
.aux_data = (group) \
}
/* RAS error record group information */
static struct err_record_info tegra194_ras_records[] = {
/*
* Per core RAS error records
*
* ERRSELR starts from (0*256 + Logical_CPU_ID*16 + 0) to
* (0*256 + Logical_CPU_ID*16 + 5) for each group.
* 8 cores/groups, 6 * 8 nodes in total.
*/
ADD_ONE_ERR_GROUP(0x000, per_core_ras_group),
ADD_ONE_ERR_GROUP(0x010, per_core_ras_group),
ADD_ONE_ERR_GROUP(0x020, per_core_ras_group),
ADD_ONE_ERR_GROUP(0x030, per_core_ras_group),
ADD_ONE_ERR_GROUP(0x040, per_core_ras_group),
ADD_ONE_ERR_GROUP(0x050, per_core_ras_group),
ADD_ONE_ERR_GROUP(0x060, per_core_ras_group),
ADD_ONE_ERR_GROUP(0x070, per_core_ras_group),
/*
* Per cluster ras error records
*
* ERRSELR starts from 2*256 + Logical_Cluster_ID*16 + 0 to
* 2*256 + Logical_Cluster_ID*16 + 3.
* 4 clusters/groups, 3 * 4 nodes in total.
*/
ADD_ONE_ERR_GROUP(0x200, per_cluster_ras_group),
ADD_ONE_ERR_GROUP(0x210, per_cluster_ras_group),
ADD_ONE_ERR_GROUP(0x220, per_cluster_ras_group),
ADD_ONE_ERR_GROUP(0x230, per_cluster_ras_group),
/*
* SCF L3_Bank ras error records
*
* ERRSELR: 3*256 + L3_Bank_ID, L3_Bank_ID: 0-3
* 1 groups, 4 nodes in total.
*/
ADD_ONE_ERR_GROUP(0x300, scf_l3_ras_group),
/*
* CCPLEX ras error records
*
* ERRSELR: 4*256 + Unit_ID, Unit_ID: 0 - 4
* 1 groups, 5 nodes in total.
*/
ADD_ONE_ERR_GROUP(0x400, ccplex_ras_group),
};
static void test_ras_inject_serror(uint32_t errselr_el1, uint64_t pfg_ctlr)
{
unsigned int core_pos = platform_get_core_pos(read_mpidr_el1() & MPID_MASK);
/*
* The per-cluster frequency monitoring nodes should be accessed from
* CPUs in the cluster that the node belongs to. e.g. nodes 0x200 and
* 0x201 should be accessed from CPUs in cluster 0, nodes 0x210 and
* 0x211 should be accessed from CPUs in cluster 1 and so on.
*/
if (((errselr_el1 & 0xF00) == 0x200) && ((errselr_el1 >> 4) & 0xF) != (core_pos >> 1)) {
return;
}
/* clear the flag before we inject SError */
fault_received = 0;
dccvac((uint64_t)&fault_received);
dmbish();
INFO("mpidr=0x%lx, errselr_el1=0x%x, pfg_ctlr=0x%llx\n",
read_mpidr_el1(), errselr_el1, pfg_ctlr);
/* Choose error record */
write_errselr_el1(errselr_el1);
/* Program count down timer to 1 */
write_erxpfgcdn_el1(1);
/* Start count down to generate error on expiry */
write_erxpfgctl_el1(ERXPFGCTL_UC_BIT | ERXPFGCTL_CDEN_BIT | pfg_ctlr);
/* wait until the SError fires */
do {
dccivac((uint64_t)&fault_received);
dmbish();
} while (fault_received == 0);
/*
* ACLR_EL1, Bit13 = RESET_RAS_FMON
*
* A write of 1 to this write-only bit re-enables checking for RAS
* frequency monitoring errors which are temporarily disabled when
* detected.
*/
if (((errselr_el1 & 0xF00) == 0x200) && ((errselr_el1 >> 4) & 0xF) == (core_pos >> 1))
write_actlr_el1(read_actlr_el1() | BIT_32(13));
else if ((errselr_el1 == 0x404))
write_actlr_el1(read_actlr_el1() | BIT_32(13));
}
static void generate_uncorrectable_faults(void)
{
unsigned int i;
unsigned int j;
unsigned int k;
unsigned int total = 0;
for (i = 0; i < ARRAY_SIZE(tegra194_ras_records); i++)
total += tegra194_ras_records[i].sysreg.num_idx;
VERBOSE("Total Nodes:%u\n", total);
for (i = 0; i < ARRAY_SIZE(tegra194_ras_records); i++) {
const struct err_record_info *info = &tegra194_ras_records[i];
uint32_t idx_start = info->sysreg.idx_start;
uint32_t num_idx = info->sysreg.num_idx;
const struct ras_aux_data *aux_data =
(const struct ras_aux_data *)info->aux_data;
for (j = 0; j < num_idx; j++) {
uint32_t errselr_el1 = idx_start + j;
uint64_t __unused err_fr;
uint64_t uncorr_errs;
/* Write to ERRSELR_EL1 to select the error record */
write_errselr_el1(errselr_el1);
/*
* all supported errors for this node exist in the
* top 32 bits
*/
err_fr = read_erxfr_el1();
err_fr >>= 32;
err_fr <<= 32;
/*
* Mask the uncorrectable errors that are disabled
* in the ERXFR register
*/
uncorr_errs = aux_data[j].err_ctrl();
uncorr_errs &= err_fr;
for (k = 32; k < 64; k++) {
if (uncorr_errs & BIT_64(k)) {
VERBOSE("ERR<x>CTLR bit%d\n", k);
test_ras_inject_serror(errselr_el1, BIT_64(k));
}
}
}
}
}
int __unused sdei_handler(int ev, uint64_t arg)
{
fault_received = 1;
dccvac((uint64_t)&fault_received);
dsbish();
VERBOSE("SError SDEI event received.\n");
return 0;
}
static event_t cpu_booted[PLATFORM_CORE_COUNT];
static volatile uint64_t cpu_powerdown[PLATFORM_CORE_COUNT];
static volatile uint64_t cpu_start_test[PLATFORM_CORE_COUNT];
static volatile uint64_t cpu_test_completed[PLATFORM_CORE_COUNT];
static void sdei_register_for_event(int event_id)
{
int64_t ret = 0;
/* Register SDEI handler */
ret = sdei_event_register(event_id, serror_sdei_event_handler, 0,
SDEI_REGF_RM_PE, read_mpidr_el1());
if (ret < 0)
tftf_testcase_printf("SDEI event register failed: 0x%llx\n",
ret);
ret = sdei_event_enable(event_id);
if (ret < 0)
tftf_testcase_printf("SDEI event enable failed: 0x%llx\n", ret);
ret = sdei_pe_unmask();
if (ret < 0)
tftf_testcase_printf("SDEI pe unmask failed: 0x%llx\n", ret);
}
static test_result_t test_cpu_serrors(void)
{
unsigned int mpid = read_mpidr_el1() & MPID_MASK;
unsigned int core_pos = platform_get_core_pos(mpid);
VERBOSE("Hello from core 0x%x\n", mpid);
/* register for the SDEI event ID */
sdei_register_for_event(300 + core_pos);
/* Tell the lead CPU that the calling CPU has entered the test */
tftf_send_event(&cpu_booted[core_pos]);
/* Wait until lead CPU asks us to start the test */
do {
dccivac((uintptr_t)&cpu_start_test[core_pos]);
dmbish();
} while (!cpu_start_test[core_pos]);
generate_uncorrectable_faults();
VERBOSE("0x%lx: test complete\n", read_mpidr_el1());
/* Inform lead CPU of test completion */
cpu_test_completed[core_pos] = true;
dccvac((uintptr_t)&cpu_test_completed[core_pos]);
dsbish();
/* Wait until lead CPU asks us to power down */
do {
dccivac((uintptr_t)&cpu_powerdown[core_pos]);
dmbish();
} while (!cpu_powerdown[core_pos]);
return TEST_RESULT_SUCCESS;
}
test_result_t test_ras_uncorrectable(void)
{
const int __unused event_id = 300;
int64_t __unused ret = 0;
unsigned int cpu_node, cpu_mpid;
unsigned int lead_mpid = read_mpidr_el1() & MPID_MASK;
unsigned int core_pos;
tftf_testcase_printf("Tegra194 uncorrectable RAS errors.\n");
/* long execution test; reset watchdog */
tftf_platform_watchdog_reset();
/* Power on all CPUs */
for_each_cpu(cpu_node) {
cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
/* Skip lead CPU, it is already powered on */
if (cpu_mpid == lead_mpid)
continue;
ret = tftf_cpu_on(cpu_mpid,
(uintptr_t) test_cpu_serrors,
0);
if (ret != PSCI_E_SUCCESS)
ret = TEST_RESULT_FAIL;
}
/*
* The lead CPU needs to wait for all other CPUs to enter the test.
* This is because the test framework declares the end of a test when no
* CPU is in the test. Therefore, if the lead CPU goes ahead and exits
* the test then potentially there could be no CPU executing the test at
* this time because none of them have entered the test yet, hence the
* framework will be misled in thinking the test is finished.
*/
for_each_cpu(cpu_node) {
cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
/* Skip lead CPU */
if (cpu_mpid == lead_mpid)
continue;
core_pos = platform_get_core_pos(cpu_mpid);
tftf_wait_for_event(&cpu_booted[core_pos]);
}
/* register for the SDEI event ID */
sdei_register_for_event(300);
/* Ask all CPUs to start the test */
for_each_cpu(cpu_node) {
cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
/*
* Except lead CPU, Wait for all cores to be powered off
* by framework
*/
if (cpu_mpid == lead_mpid)
continue;
/* Allow the CPU to start the test */
core_pos = platform_get_core_pos(cpu_mpid);
cpu_start_test[core_pos] = START;
dccvac((uintptr_t)&cpu_start_test[core_pos]);
dsbish();
/* Wait for the CPU to complete the test */
do {
dccivac((uintptr_t)&cpu_test_completed[core_pos]);
dmbish();
} while (!cpu_test_completed[core_pos]);
}
/* run through all supported uncorrectable faults */
generate_uncorrectable_faults();
VERBOSE("0x%lx: test complete\n", read_mpidr_el1());
/* Wait for all CPUs to power off */
for_each_cpu(cpu_node) {
cpu_mpid = tftf_get_mpidr_from_node(cpu_node);
/*
* Except lead CPU, Wait for all cores to be powered off
* by framework
*/
if (cpu_mpid == lead_mpid)
continue;
/* Allow other CPUs to start power down sequence */
core_pos = platform_get_core_pos(cpu_mpid);
cpu_powerdown[core_pos] = START;
dccvac((uintptr_t)&cpu_powerdown[core_pos]);
dsbish();
/* Wait for the CPU to actually power off */
while (tftf_psci_affinity_info(cpu_mpid, MPIDR_AFFLVL0) != PSCI_STATE_OFF)
dsbsy();
}
return TEST_RESULT_SUCCESS;
}