162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/arm-smccc.h> 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/of.h> 962306a36Sopenharmony_ci#include <linux/panic_notifier.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define SMC_SIP_INVOKE_MCE 0xc2ffff00 1262306a36Sopenharmony_ci#define MCE_SMC_READ_MCA 12 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define MCA_ARI_CMD_RD_SERR 1 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define MCA_ARI_RW_SUBIDX_STAT 1 1762306a36Sopenharmony_ci#define SERR_STATUS_VAL BIT_ULL(63) 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define MCA_ARI_RW_SUBIDX_ADDR 2 2062306a36Sopenharmony_ci#define MCA_ARI_RW_SUBIDX_MSC1 3 2162306a36Sopenharmony_ci#define MCA_ARI_RW_SUBIDX_MSC2 4 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic const char * const bank_names[] = { 2462306a36Sopenharmony_ci "SYS:DPMU", "ROC:IOB", "ROC:MCB", "ROC:CCE", "ROC:CQX", "ROC:CTU", 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic void read_uncore_mca(u8 cmd, u8 idx, u8 subidx, u8 inst, u64 *data) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct arm_smccc_res res; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci arm_smccc_smc(SMC_SIP_INVOKE_MCE | MCE_SMC_READ_MCA, 3262306a36Sopenharmony_ci ((u64)inst << 24) | ((u64)idx << 16) | 3362306a36Sopenharmony_ci ((u64)subidx << 8) | ((u64)cmd << 0), 3462306a36Sopenharmony_ci 0, 0, 0, 0, 0, 0, &res); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci *data = res.a2; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int tegra186_ari_panic_handler(struct notifier_block *nb, 4062306a36Sopenharmony_ci unsigned long code, void *unused) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci u64 status; 4362306a36Sopenharmony_ci int i; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(bank_names); i++) { 4662306a36Sopenharmony_ci read_uncore_mca(MCA_ARI_CMD_RD_SERR, i, MCA_ARI_RW_SUBIDX_STAT, 4762306a36Sopenharmony_ci 0, &status); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (status & SERR_STATUS_VAL) { 5062306a36Sopenharmony_ci u64 addr, misc1, misc2; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci read_uncore_mca(MCA_ARI_CMD_RD_SERR, i, 5362306a36Sopenharmony_ci MCA_ARI_RW_SUBIDX_ADDR, 0, &addr); 5462306a36Sopenharmony_ci read_uncore_mca(MCA_ARI_CMD_RD_SERR, i, 5562306a36Sopenharmony_ci MCA_ARI_RW_SUBIDX_MSC1, 0, &misc1); 5662306a36Sopenharmony_ci read_uncore_mca(MCA_ARI_CMD_RD_SERR, i, 5762306a36Sopenharmony_ci MCA_ARI_RW_SUBIDX_MSC2, 0, &misc2); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci pr_crit("Machine Check Error in %s\n" 6062306a36Sopenharmony_ci " status=0x%llx addr=0x%llx\n" 6162306a36Sopenharmony_ci " msc1=0x%llx msc2=0x%llx\n", 6262306a36Sopenharmony_ci bank_names[i], status, addr, misc1, misc2); 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return NOTIFY_DONE; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic struct notifier_block tegra186_ari_panic_nb = { 7062306a36Sopenharmony_ci .notifier_call = tegra186_ari_panic_handler, 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int __init tegra186_ari_init(void) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci if (of_machine_is_compatible("nvidia,tegra186")) 7662306a36Sopenharmony_ci atomic_notifier_chain_register(&panic_notifier_list, &tegra186_ari_panic_nb); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return 0; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ciearly_initcall(tegra186_ari_init); 81