162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Huawei HiNIC PCI Express Linux driver 362306a36Sopenharmony_ci * Copyright(c) 2017 Huawei Technologies Co., Ltd 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 662306a36Sopenharmony_ci * under the terms and conditions of the GNU General Public License, 762306a36Sopenharmony_ci * version 2, as published by the Free Software Foundation. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This program is distributed in the hope it will be useful, but WITHOUT 1062306a36Sopenharmony_ci * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1162306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1262306a36Sopenharmony_ci * for more details. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci#include <linux/netlink.h> 1662306a36Sopenharmony_ci#include <net/devlink.h> 1762306a36Sopenharmony_ci#include <linux/firmware.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "hinic_port.h" 2062306a36Sopenharmony_ci#include "hinic_devlink.h" 2162306a36Sopenharmony_ci#include "hinic_hw_dev.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic bool check_image_valid(struct hinic_devlink_priv *priv, const u8 *buf, 2462306a36Sopenharmony_ci u32 image_size, struct host_image_st *host_image) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct fw_image_st *fw_image = NULL; 2762306a36Sopenharmony_ci u32 len = 0; 2862306a36Sopenharmony_ci u32 i; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci fw_image = (struct fw_image_st *)buf; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci if (fw_image->fw_magic != HINIC_MAGIC_NUM) { 3362306a36Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "Wrong fw_magic read from file, fw_magic: 0x%x\n", 3462306a36Sopenharmony_ci fw_image->fw_magic); 3562306a36Sopenharmony_ci return false; 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (fw_image->fw_info.fw_section_cnt > MAX_FW_TYPE_NUM) { 3962306a36Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "Wrong fw_type_num read from file, fw_type_num: 0x%x\n", 4062306a36Sopenharmony_ci fw_image->fw_info.fw_section_cnt); 4162306a36Sopenharmony_ci return false; 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci for (i = 0; i < fw_image->fw_info.fw_section_cnt; i++) { 4562306a36Sopenharmony_ci len += fw_image->fw_section_info[i].fw_section_len; 4662306a36Sopenharmony_ci host_image->image_section_info[i] = fw_image->fw_section_info[i]; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (len != fw_image->fw_len || 5062306a36Sopenharmony_ci (fw_image->fw_len + UPDATEFW_IMAGE_HEAD_SIZE) != image_size) { 5162306a36Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "Wrong data size read from file\n"); 5262306a36Sopenharmony_ci return false; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci host_image->image_info.up_total_len = fw_image->fw_len; 5662306a36Sopenharmony_ci host_image->image_info.fw_version = fw_image->fw_version; 5762306a36Sopenharmony_ci host_image->section_type_num = fw_image->fw_info.fw_section_cnt; 5862306a36Sopenharmony_ci host_image->device_id = fw_image->device_id; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci return true; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic bool check_image_integrity(struct hinic_devlink_priv *priv, 6462306a36Sopenharmony_ci struct host_image_st *host_image, 6562306a36Sopenharmony_ci u32 update_type) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci u32 collect_section_type = 0; 6862306a36Sopenharmony_ci u32 i, type; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci for (i = 0; i < host_image->section_type_num; i++) { 7162306a36Sopenharmony_ci type = host_image->image_section_info[i].fw_section_type; 7262306a36Sopenharmony_ci if (collect_section_type & (1U << type)) { 7362306a36Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "Duplicate section type: %u\n", 7462306a36Sopenharmony_ci type); 7562306a36Sopenharmony_ci return false; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci collect_section_type |= (1U << type); 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (update_type == FW_UPDATE_COLD && 8162306a36Sopenharmony_ci (((collect_section_type & _IMAGE_COLD_SUB_MODULES_MUST_IN) == 8262306a36Sopenharmony_ci _IMAGE_COLD_SUB_MODULES_MUST_IN) || 8362306a36Sopenharmony_ci collect_section_type == _IMAGE_CFG_SUB_MODULES_MUST_IN)) 8462306a36Sopenharmony_ci return true; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (update_type == FW_UPDATE_HOT && 8762306a36Sopenharmony_ci (collect_section_type & _IMAGE_HOT_SUB_MODULES_MUST_IN) == 8862306a36Sopenharmony_ci _IMAGE_HOT_SUB_MODULES_MUST_IN) 8962306a36Sopenharmony_ci return true; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (update_type == FW_UPDATE_COLD) 9262306a36Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "Check file integrity failed, valid: 0x%x or 0x%lx, current: 0x%x\n", 9362306a36Sopenharmony_ci _IMAGE_COLD_SUB_MODULES_MUST_IN, 9462306a36Sopenharmony_ci _IMAGE_CFG_SUB_MODULES_MUST_IN, collect_section_type); 9562306a36Sopenharmony_ci else 9662306a36Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "Check file integrity failed, valid:0x%x, current: 0x%x\n", 9762306a36Sopenharmony_ci _IMAGE_HOT_SUB_MODULES_MUST_IN, collect_section_type); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return false; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int check_image_device_type(struct hinic_devlink_priv *priv, 10362306a36Sopenharmony_ci u32 image_device_type) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct hinic_comm_board_info board_info = {0}; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (hinic_get_board_info(priv->hwdev, &board_info)) { 10862306a36Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "Get board info failed\n"); 10962306a36Sopenharmony_ci return false; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (image_device_type == board_info.info.board_type) 11362306a36Sopenharmony_ci return true; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "The device type of upgrade file doesn't match the device type of current firmware, please check the upgrade file\n"); 11662306a36Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "The image device type: 0x%x, firmware device type: 0x%x\n", 11762306a36Sopenharmony_ci image_device_type, board_info.info.board_type); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return false; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int hinic_flash_fw(struct hinic_devlink_priv *priv, const u8 *data, 12362306a36Sopenharmony_ci struct host_image_st *host_image) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci u32 section_remain_send_len, send_fragment_len, send_pos, up_total_len; 12662306a36Sopenharmony_ci struct hinic_cmd_update_fw *fw_update_msg = NULL; 12762306a36Sopenharmony_ci u32 section_type, section_crc, section_version; 12862306a36Sopenharmony_ci u32 i, len, section_len, section_offset; 12962306a36Sopenharmony_ci u16 out_size = sizeof(*fw_update_msg); 13062306a36Sopenharmony_ci int total_len_flag = 0; 13162306a36Sopenharmony_ci int err; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci fw_update_msg = kzalloc(sizeof(*fw_update_msg), GFP_KERNEL); 13462306a36Sopenharmony_ci if (!fw_update_msg) 13562306a36Sopenharmony_ci return -ENOMEM; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci up_total_len = host_image->image_info.up_total_len; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci for (i = 0; i < host_image->section_type_num; i++) { 14062306a36Sopenharmony_ci len = host_image->image_section_info[i].fw_section_len; 14162306a36Sopenharmony_ci if (host_image->image_section_info[i].fw_section_type == 14262306a36Sopenharmony_ci UP_FW_UPDATE_BOOT) { 14362306a36Sopenharmony_ci up_total_len = up_total_len - len; 14462306a36Sopenharmony_ci break; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci for (i = 0; i < host_image->section_type_num; i++) { 14962306a36Sopenharmony_ci section_len = 15062306a36Sopenharmony_ci host_image->image_section_info[i].fw_section_len; 15162306a36Sopenharmony_ci section_offset = 15262306a36Sopenharmony_ci host_image->image_section_info[i].fw_section_offset; 15362306a36Sopenharmony_ci section_remain_send_len = section_len; 15462306a36Sopenharmony_ci section_type = 15562306a36Sopenharmony_ci host_image->image_section_info[i].fw_section_type; 15662306a36Sopenharmony_ci section_crc = host_image->image_section_info[i].fw_section_crc; 15762306a36Sopenharmony_ci section_version = 15862306a36Sopenharmony_ci host_image->image_section_info[i].fw_section_version; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (section_type == UP_FW_UPDATE_BOOT) 16162306a36Sopenharmony_ci continue; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci send_fragment_len = 0; 16462306a36Sopenharmony_ci send_pos = 0; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci while (section_remain_send_len > 0) { 16762306a36Sopenharmony_ci if (!total_len_flag) { 16862306a36Sopenharmony_ci fw_update_msg->total_len = up_total_len; 16962306a36Sopenharmony_ci total_len_flag = 1; 17062306a36Sopenharmony_ci } else { 17162306a36Sopenharmony_ci fw_update_msg->total_len = 0; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci memset(fw_update_msg->data, 0, MAX_FW_FRAGMENT_LEN); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci fw_update_msg->ctl_info.SF = 17762306a36Sopenharmony_ci (section_remain_send_len == section_len) ? 17862306a36Sopenharmony_ci true : false; 17962306a36Sopenharmony_ci fw_update_msg->section_info.FW_section_CRC = section_crc; 18062306a36Sopenharmony_ci fw_update_msg->fw_section_version = section_version; 18162306a36Sopenharmony_ci fw_update_msg->ctl_info.flag = UP_TYPE_A; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (section_type <= UP_FW_UPDATE_UP_DATA_B) { 18462306a36Sopenharmony_ci fw_update_msg->section_info.FW_section_type = 18562306a36Sopenharmony_ci (section_type % 2) ? 18662306a36Sopenharmony_ci UP_FW_UPDATE_UP_DATA : 18762306a36Sopenharmony_ci UP_FW_UPDATE_UP_TEXT; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci fw_update_msg->ctl_info.flag = UP_TYPE_B; 19062306a36Sopenharmony_ci if (section_type <= UP_FW_UPDATE_UP_DATA_A) 19162306a36Sopenharmony_ci fw_update_msg->ctl_info.flag = UP_TYPE_A; 19262306a36Sopenharmony_ci } else { 19362306a36Sopenharmony_ci fw_update_msg->section_info.FW_section_type = 19462306a36Sopenharmony_ci section_type - 0x2; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci fw_update_msg->setion_total_len = section_len; 19862306a36Sopenharmony_ci fw_update_msg->section_offset = send_pos; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (section_remain_send_len <= MAX_FW_FRAGMENT_LEN) { 20162306a36Sopenharmony_ci fw_update_msg->ctl_info.SL = true; 20262306a36Sopenharmony_ci fw_update_msg->ctl_info.fragment_len = 20362306a36Sopenharmony_ci section_remain_send_len; 20462306a36Sopenharmony_ci send_fragment_len += section_remain_send_len; 20562306a36Sopenharmony_ci } else { 20662306a36Sopenharmony_ci fw_update_msg->ctl_info.SL = false; 20762306a36Sopenharmony_ci fw_update_msg->ctl_info.fragment_len = 20862306a36Sopenharmony_ci MAX_FW_FRAGMENT_LEN; 20962306a36Sopenharmony_ci send_fragment_len += MAX_FW_FRAGMENT_LEN; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci memcpy(fw_update_msg->data, 21362306a36Sopenharmony_ci data + UPDATEFW_IMAGE_HEAD_SIZE + 21462306a36Sopenharmony_ci section_offset + send_pos, 21562306a36Sopenharmony_ci fw_update_msg->ctl_info.fragment_len); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci err = hinic_port_msg_cmd(priv->hwdev, 21862306a36Sopenharmony_ci HINIC_PORT_CMD_UPDATE_FW, 21962306a36Sopenharmony_ci fw_update_msg, 22062306a36Sopenharmony_ci sizeof(*fw_update_msg), 22162306a36Sopenharmony_ci fw_update_msg, &out_size); 22262306a36Sopenharmony_ci if (err || !out_size || fw_update_msg->status) { 22362306a36Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "Failed to update firmware, err: %d, status: 0x%x, out size: 0x%x\n", 22462306a36Sopenharmony_ci err, fw_update_msg->status, out_size); 22562306a36Sopenharmony_ci err = fw_update_msg->status ? 22662306a36Sopenharmony_ci fw_update_msg->status : -EIO; 22762306a36Sopenharmony_ci kfree(fw_update_msg); 22862306a36Sopenharmony_ci return err; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci send_pos = send_fragment_len; 23262306a36Sopenharmony_ci section_remain_send_len = section_len - 23362306a36Sopenharmony_ci send_fragment_len; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci kfree(fw_update_msg); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return 0; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic int hinic_firmware_update(struct hinic_devlink_priv *priv, 24362306a36Sopenharmony_ci const struct firmware *fw, 24462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci struct host_image_st host_image; 24762306a36Sopenharmony_ci int err; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci memset(&host_image, 0, sizeof(struct host_image_st)); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (!check_image_valid(priv, fw->data, fw->size, &host_image) || 25262306a36Sopenharmony_ci !check_image_integrity(priv, &host_image, FW_UPDATE_COLD) || 25362306a36Sopenharmony_ci !check_image_device_type(priv, host_image.device_id)) { 25462306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Check image failed"); 25562306a36Sopenharmony_ci return -EINVAL; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci dev_info(&priv->hwdev->hwif->pdev->dev, "Flash firmware begin\n"); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci err = hinic_flash_fw(priv, fw->data, &host_image); 26162306a36Sopenharmony_ci if (err) { 26262306a36Sopenharmony_ci if (err == HINIC_FW_DISMATCH_ERROR) { 26362306a36Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "Firmware image doesn't match this card, please use newer image, err: %d\n", 26462306a36Sopenharmony_ci err); 26562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 26662306a36Sopenharmony_ci "Firmware image doesn't match this card, please use newer image"); 26762306a36Sopenharmony_ci } else { 26862306a36Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "Send firmware image data failed, err: %d\n", 26962306a36Sopenharmony_ci err); 27062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Send firmware image data failed"); 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return err; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci dev_info(&priv->hwdev->hwif->pdev->dev, "Flash firmware end\n"); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic int hinic_devlink_flash_update(struct devlink *devlink, 28262306a36Sopenharmony_ci struct devlink_flash_update_params *params, 28362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct hinic_devlink_priv *priv = devlink_priv(devlink); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci return hinic_firmware_update(priv, params->fw, extack); 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic const struct devlink_ops hinic_devlink_ops = { 29162306a36Sopenharmony_ci .flash_update = hinic_devlink_flash_update, 29262306a36Sopenharmony_ci}; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistruct devlink *hinic_devlink_alloc(struct device *dev) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci return devlink_alloc(&hinic_devlink_ops, sizeof(struct hinic_dev), dev); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_civoid hinic_devlink_free(struct devlink *devlink) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci devlink_free(devlink); 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_civoid hinic_devlink_register(struct hinic_devlink_priv *priv) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct devlink *devlink = priv_to_devlink(priv); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci devlink_register(devlink); 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_civoid hinic_devlink_unregister(struct hinic_devlink_priv *priv) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci struct devlink *devlink = priv_to_devlink(priv); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci devlink_unregister(devlink); 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int chip_fault_show(struct devlink_fmsg *fmsg, 31962306a36Sopenharmony_ci struct hinic_fault_event *event) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci const char * const level_str[FAULT_LEVEL_MAX + 1] = { 32262306a36Sopenharmony_ci "fatal", "reset", "flr", "general", "suggestion", "Unknown"}; 32362306a36Sopenharmony_ci u8 fault_level; 32462306a36Sopenharmony_ci int err; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci fault_level = (event->event.chip.err_level < FAULT_LEVEL_MAX) ? 32762306a36Sopenharmony_ci event->event.chip.err_level : FAULT_LEVEL_MAX; 32862306a36Sopenharmony_ci if (fault_level == FAULT_LEVEL_SERIOUS_FLR) { 32962306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "Function level err func_id", 33062306a36Sopenharmony_ci (u32)event->event.chip.func_id); 33162306a36Sopenharmony_ci if (err) 33262306a36Sopenharmony_ci return err; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci err = devlink_fmsg_u8_pair_put(fmsg, "module_id", event->event.chip.node_id); 33662306a36Sopenharmony_ci if (err) 33762306a36Sopenharmony_ci return err; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "err_type", (u32)event->event.chip.err_type); 34062306a36Sopenharmony_ci if (err) 34162306a36Sopenharmony_ci return err; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci err = devlink_fmsg_string_pair_put(fmsg, "err_level", level_str[fault_level]); 34462306a36Sopenharmony_ci if (err) 34562306a36Sopenharmony_ci return err; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "err_csr_addr", 34862306a36Sopenharmony_ci event->event.chip.err_csr_addr); 34962306a36Sopenharmony_ci if (err) 35062306a36Sopenharmony_ci return err; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "err_csr_value", 35362306a36Sopenharmony_ci event->event.chip.err_csr_value); 35462306a36Sopenharmony_ci if (err) 35562306a36Sopenharmony_ci return err; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic int fault_report_show(struct devlink_fmsg *fmsg, 36162306a36Sopenharmony_ci struct hinic_fault_event *event) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci const char * const type_str[FAULT_TYPE_MAX + 1] = { 36462306a36Sopenharmony_ci "chip", "ucode", "mem rd timeout", "mem wr timeout", 36562306a36Sopenharmony_ci "reg rd timeout", "reg wr timeout", "phy fault", "Unknown"}; 36662306a36Sopenharmony_ci u8 fault_type; 36762306a36Sopenharmony_ci int err; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci fault_type = (event->type < FAULT_TYPE_MAX) ? event->type : FAULT_TYPE_MAX; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci err = devlink_fmsg_string_pair_put(fmsg, "Fault type", type_str[fault_type]); 37262306a36Sopenharmony_ci if (err) 37362306a36Sopenharmony_ci return err; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci err = devlink_fmsg_binary_pair_put(fmsg, "Fault raw data", 37662306a36Sopenharmony_ci event->event.val, sizeof(event->event.val)); 37762306a36Sopenharmony_ci if (err) 37862306a36Sopenharmony_ci return err; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci switch (event->type) { 38162306a36Sopenharmony_ci case FAULT_TYPE_CHIP: 38262306a36Sopenharmony_ci err = chip_fault_show(fmsg, event); 38362306a36Sopenharmony_ci if (err) 38462306a36Sopenharmony_ci return err; 38562306a36Sopenharmony_ci break; 38662306a36Sopenharmony_ci case FAULT_TYPE_UCODE: 38762306a36Sopenharmony_ci err = devlink_fmsg_u8_pair_put(fmsg, "Cause_id", event->event.ucode.cause_id); 38862306a36Sopenharmony_ci if (err) 38962306a36Sopenharmony_ci return err; 39062306a36Sopenharmony_ci err = devlink_fmsg_u8_pair_put(fmsg, "core_id", event->event.ucode.core_id); 39162306a36Sopenharmony_ci if (err) 39262306a36Sopenharmony_ci return err; 39362306a36Sopenharmony_ci err = devlink_fmsg_u8_pair_put(fmsg, "c_id", event->event.ucode.c_id); 39462306a36Sopenharmony_ci if (err) 39562306a36Sopenharmony_ci return err; 39662306a36Sopenharmony_ci err = devlink_fmsg_u8_pair_put(fmsg, "epc", event->event.ucode.epc); 39762306a36Sopenharmony_ci if (err) 39862306a36Sopenharmony_ci return err; 39962306a36Sopenharmony_ci break; 40062306a36Sopenharmony_ci case FAULT_TYPE_MEM_RD_TIMEOUT: 40162306a36Sopenharmony_ci case FAULT_TYPE_MEM_WR_TIMEOUT: 40262306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "Err_csr_ctrl", 40362306a36Sopenharmony_ci event->event.mem_timeout.err_csr_ctrl); 40462306a36Sopenharmony_ci if (err) 40562306a36Sopenharmony_ci return err; 40662306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "err_csr_data", 40762306a36Sopenharmony_ci event->event.mem_timeout.err_csr_data); 40862306a36Sopenharmony_ci if (err) 40962306a36Sopenharmony_ci return err; 41062306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "ctrl_tab", 41162306a36Sopenharmony_ci event->event.mem_timeout.ctrl_tab); 41262306a36Sopenharmony_ci if (err) 41362306a36Sopenharmony_ci return err; 41462306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "mem_index", 41562306a36Sopenharmony_ci event->event.mem_timeout.mem_index); 41662306a36Sopenharmony_ci if (err) 41762306a36Sopenharmony_ci return err; 41862306a36Sopenharmony_ci break; 41962306a36Sopenharmony_ci case FAULT_TYPE_REG_RD_TIMEOUT: 42062306a36Sopenharmony_ci case FAULT_TYPE_REG_WR_TIMEOUT: 42162306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "Err_csr", event->event.reg_timeout.err_csr); 42262306a36Sopenharmony_ci if (err) 42362306a36Sopenharmony_ci return err; 42462306a36Sopenharmony_ci break; 42562306a36Sopenharmony_ci case FAULT_TYPE_PHY_FAULT: 42662306a36Sopenharmony_ci err = devlink_fmsg_u8_pair_put(fmsg, "Op_type", event->event.phy_fault.op_type); 42762306a36Sopenharmony_ci if (err) 42862306a36Sopenharmony_ci return err; 42962306a36Sopenharmony_ci err = devlink_fmsg_u8_pair_put(fmsg, "port_id", event->event.phy_fault.port_id); 43062306a36Sopenharmony_ci if (err) 43162306a36Sopenharmony_ci return err; 43262306a36Sopenharmony_ci err = devlink_fmsg_u8_pair_put(fmsg, "dev_ad", event->event.phy_fault.dev_ad); 43362306a36Sopenharmony_ci if (err) 43462306a36Sopenharmony_ci return err; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "csr_addr", event->event.phy_fault.csr_addr); 43762306a36Sopenharmony_ci if (err) 43862306a36Sopenharmony_ci return err; 43962306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "op_data", event->event.phy_fault.op_data); 44062306a36Sopenharmony_ci if (err) 44162306a36Sopenharmony_ci return err; 44262306a36Sopenharmony_ci break; 44362306a36Sopenharmony_ci default: 44462306a36Sopenharmony_ci break; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci return 0; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic int hinic_hw_reporter_dump(struct devlink_health_reporter *reporter, 45162306a36Sopenharmony_ci struct devlink_fmsg *fmsg, void *priv_ctx, 45262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci if (priv_ctx) 45562306a36Sopenharmony_ci return fault_report_show(fmsg, priv_ctx); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci return 0; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic int mgmt_watchdog_report_show(struct devlink_fmsg *fmsg, 46162306a36Sopenharmony_ci struct hinic_mgmt_watchdog_info *watchdog_info) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci int err; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "Mgmt deadloop time_h", watchdog_info->curr_time_h); 46662306a36Sopenharmony_ci if (err) 46762306a36Sopenharmony_ci return err; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "time_l", watchdog_info->curr_time_l); 47062306a36Sopenharmony_ci if (err) 47162306a36Sopenharmony_ci return err; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "task_id", watchdog_info->task_id); 47462306a36Sopenharmony_ci if (err) 47562306a36Sopenharmony_ci return err; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "sp", watchdog_info->sp); 47862306a36Sopenharmony_ci if (err) 47962306a36Sopenharmony_ci return err; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "stack_current_used", watchdog_info->curr_used); 48262306a36Sopenharmony_ci if (err) 48362306a36Sopenharmony_ci return err; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "peak_used", watchdog_info->peak_used); 48662306a36Sopenharmony_ci if (err) 48762306a36Sopenharmony_ci return err; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "\n Overflow_flag", watchdog_info->is_overflow); 49062306a36Sopenharmony_ci if (err) 49162306a36Sopenharmony_ci return err; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "stack_top", watchdog_info->stack_top); 49462306a36Sopenharmony_ci if (err) 49562306a36Sopenharmony_ci return err; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "stack_bottom", watchdog_info->stack_bottom); 49862306a36Sopenharmony_ci if (err) 49962306a36Sopenharmony_ci return err; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "mgmt_pc", watchdog_info->pc); 50262306a36Sopenharmony_ci if (err) 50362306a36Sopenharmony_ci return err; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "lr", watchdog_info->lr); 50662306a36Sopenharmony_ci if (err) 50762306a36Sopenharmony_ci return err; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "cpsr", watchdog_info->cpsr); 51062306a36Sopenharmony_ci if (err) 51162306a36Sopenharmony_ci return err; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci err = devlink_fmsg_binary_pair_put(fmsg, "Mgmt register info", 51462306a36Sopenharmony_ci watchdog_info->reg, sizeof(watchdog_info->reg)); 51562306a36Sopenharmony_ci if (err) 51662306a36Sopenharmony_ci return err; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci err = devlink_fmsg_binary_pair_put(fmsg, "Mgmt dump stack(start from sp)", 51962306a36Sopenharmony_ci watchdog_info->data, sizeof(watchdog_info->data)); 52062306a36Sopenharmony_ci if (err) 52162306a36Sopenharmony_ci return err; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci return 0; 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic int hinic_fw_reporter_dump(struct devlink_health_reporter *reporter, 52762306a36Sopenharmony_ci struct devlink_fmsg *fmsg, void *priv_ctx, 52862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci if (priv_ctx) 53162306a36Sopenharmony_ci return mgmt_watchdog_report_show(fmsg, priv_ctx); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci return 0; 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic const struct devlink_health_reporter_ops hinic_hw_fault_reporter_ops = { 53762306a36Sopenharmony_ci .name = "hw", 53862306a36Sopenharmony_ci .dump = hinic_hw_reporter_dump, 53962306a36Sopenharmony_ci}; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic const struct devlink_health_reporter_ops hinic_fw_fault_reporter_ops = { 54262306a36Sopenharmony_ci .name = "fw", 54362306a36Sopenharmony_ci .dump = hinic_fw_reporter_dump, 54462306a36Sopenharmony_ci}; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ciint hinic_health_reporters_create(struct hinic_devlink_priv *priv) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci struct devlink *devlink = priv_to_devlink(priv); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci priv->hw_fault_reporter = 55162306a36Sopenharmony_ci devlink_health_reporter_create(devlink, &hinic_hw_fault_reporter_ops, 55262306a36Sopenharmony_ci 0, priv); 55362306a36Sopenharmony_ci if (IS_ERR(priv->hw_fault_reporter)) { 55462306a36Sopenharmony_ci dev_warn(&priv->hwdev->hwif->pdev->dev, "Failed to create hw fault reporter, err: %ld\n", 55562306a36Sopenharmony_ci PTR_ERR(priv->hw_fault_reporter)); 55662306a36Sopenharmony_ci return PTR_ERR(priv->hw_fault_reporter); 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci priv->fw_fault_reporter = 56062306a36Sopenharmony_ci devlink_health_reporter_create(devlink, &hinic_fw_fault_reporter_ops, 56162306a36Sopenharmony_ci 0, priv); 56262306a36Sopenharmony_ci if (IS_ERR(priv->fw_fault_reporter)) { 56362306a36Sopenharmony_ci dev_warn(&priv->hwdev->hwif->pdev->dev, "Failed to create fw fault reporter, err: %ld\n", 56462306a36Sopenharmony_ci PTR_ERR(priv->fw_fault_reporter)); 56562306a36Sopenharmony_ci devlink_health_reporter_destroy(priv->hw_fault_reporter); 56662306a36Sopenharmony_ci priv->hw_fault_reporter = NULL; 56762306a36Sopenharmony_ci return PTR_ERR(priv->fw_fault_reporter); 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return 0; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_civoid hinic_health_reporters_destroy(struct hinic_devlink_priv *priv) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(priv->fw_fault_reporter)) { 57662306a36Sopenharmony_ci devlink_health_reporter_destroy(priv->fw_fault_reporter); 57762306a36Sopenharmony_ci priv->fw_fault_reporter = NULL; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(priv->hw_fault_reporter)) { 58162306a36Sopenharmony_ci devlink_health_reporter_destroy(priv->hw_fault_reporter); 58262306a36Sopenharmony_ci priv->hw_fault_reporter = NULL; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci} 585