/* * Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include static uint32_t hsm_id; static int bpmp_get_ec_status(uint32_t hsm_id, struct cmd_ec_status_get_response *ec_status) { struct mrq_ec_request req; struct mrq_ec_response reply; int ret; req.cmd_id = CMD_EC_STATUS_GET; req.ec_status_get.ec_hsm_id = hsm_id; ret = tegra_bpmp_send_receive(MRQ_EC, &req, sizeof(req), &reply, sizeof(reply)); if (ret < 0) return ret; *ec_status = reply.ec_status_get; return 0; } static int ec_status_show(struct seq_file *s, void *data) { int i, ret; uint32_t hid = hsm_id; struct cmd_ec_status_get_response ec_status; uint32_t *flags = &ec_status.ec_status_flags; ret = bpmp_get_ec_status(hid, &ec_status); if (ret) { seq_printf(s, "HSM id %u\nError: ", hid); seq_printf(s, "failed (%d)\n", ret); return 0; } seq_printf(s, "HSM id %u\nError: ", ec_status.ec_hsm_id); if (*flags & EC_STATUS_FLAG_NO_ERROR) { seq_printf(s, "none\n"); return 0; } seq_printf(s, "idx %u (%s%s)\n", ec_status.error_idx, *flags & EC_STATUS_FLAG_LATENT_ERROR ? "latent" : "mission", *flags & EC_STATUS_FLAG_LAST_ERROR ? ", last" : ""); seq_printf(s, "type %u\n", ec_status.error_type); for (i = 0; i < ec_status.error_desc_num; i++) { union ec_err_desc *desc = &ec_status.error_descs[i]; switch (ec_status.error_type) { case EC_ERR_TYPE_CLOCK_MONITOR: if (desc->fmon_desc.desc_flags & EC_DESC_FLAG_NO_ID) { seq_printf(s, "id unknown\n"); break; } seq_printf(s, "id %u faults 0x%x err %d flags 0x%x\n", desc->fmon_desc.fmon_clk_id, desc->fmon_desc.fmon_faults, desc->fmon_desc.fmon_access_error, desc->fmon_desc.desc_flags); break; case EC_ERR_TYPE_VOLTAGE_MONITOR: if (desc->vmon_desc.desc_flags & EC_DESC_FLAG_NO_ID) { seq_printf(s, "id unknown\n"); break; } seq_printf(s, "id %u faults 0x%x err %d flags 0x%x\n", desc->vmon_desc.vmon_adc_id, desc->vmon_desc.vmon_faults, desc->vmon_desc.vmon_access_error, desc->vmon_desc.desc_flags); break; case EC_ERR_TYPE_REGISTER_PARITY: if (desc->reg_parity_desc.desc_flags & EC_DESC_FLAG_NO_ID) { seq_printf(s, "id unknown\n"); break; } seq_printf(s, "id %u group %u flags 0x%x\n", desc->reg_parity_desc.reg_id, desc->reg_parity_desc.reg_group, desc->reg_parity_desc.desc_flags); break; default: if (desc->simple_desc.desc_flags & EC_DESC_FLAG_NO_ID) { seq_printf(s, "id unknown\n"); break; } seq_printf(s, "id %u flags 0x%x\n", desc->simple_desc.err_source_id, desc->simple_desc.desc_flags); break; } } return 0; } static int ec_status_open(struct inode *inode, struct file *file) { return single_open(file, ec_status_show, inode->i_private); } static const struct file_operations ec_status_fops = { .open = ec_status_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int __init bpmp_ec_debugfs_init(void) { struct dentry *d, *dir; dir = debugfs_create_dir("tegra_ec", NULL); if (!dir) return -ENOMEM; d = debugfs_create_file("ec_status", S_IRUGO, dir, NULL, &ec_status_fops); if (!d) return -ENOMEM; d = debugfs_create_u32("hsm_id", S_IRUGO|S_IWUSR, dir, &hsm_id); if (!d) return -ENOMEM; return 0; } late_initcall(bpmp_ec_debugfs_init);