18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Huawei HiNIC PCI Express Linux driver 38c2ecf20Sopenharmony_ci * Copyright(c) 2017 Huawei Technologies Co., Ltd 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 68c2ecf20Sopenharmony_ci * under the terms and conditions of the GNU General Public License, 78c2ecf20Sopenharmony_ci * version 2, as published by the Free Software Foundation. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This program is distributed in the hope it will be useful, but WITHOUT 108c2ecf20Sopenharmony_ci * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 118c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 128c2ecf20Sopenharmony_ci * for more details. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci#include <linux/netlink.h> 168c2ecf20Sopenharmony_ci#include <net/devlink.h> 178c2ecf20Sopenharmony_ci#include <linux/firmware.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "hinic_port.h" 208c2ecf20Sopenharmony_ci#include "hinic_devlink.h" 218c2ecf20Sopenharmony_ci#include "hinic_hw_dev.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic bool check_image_valid(struct hinic_devlink_priv *priv, const u8 *buf, 248c2ecf20Sopenharmony_ci u32 image_size, struct host_image_st *host_image) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci struct fw_image_st *fw_image = NULL; 278c2ecf20Sopenharmony_ci u32 len = 0; 288c2ecf20Sopenharmony_ci u32 i; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci fw_image = (struct fw_image_st *)buf; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci if (fw_image->fw_magic != HINIC_MAGIC_NUM) { 338c2ecf20Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "Wrong fw_magic read from file, fw_magic: 0x%x\n", 348c2ecf20Sopenharmony_ci fw_image->fw_magic); 358c2ecf20Sopenharmony_ci return false; 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci if (fw_image->fw_info.fw_section_cnt > MAX_FW_TYPE_NUM) { 398c2ecf20Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "Wrong fw_type_num read from file, fw_type_num: 0x%x\n", 408c2ecf20Sopenharmony_ci fw_image->fw_info.fw_section_cnt); 418c2ecf20Sopenharmony_ci return false; 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci for (i = 0; i < fw_image->fw_info.fw_section_cnt; i++) { 458c2ecf20Sopenharmony_ci len += fw_image->fw_section_info[i].fw_section_len; 468c2ecf20Sopenharmony_ci memcpy(&host_image->image_section_info[i], 478c2ecf20Sopenharmony_ci &fw_image->fw_section_info[i], 488c2ecf20Sopenharmony_ci sizeof(struct fw_section_info_st)); 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (len != fw_image->fw_len || 528c2ecf20Sopenharmony_ci (fw_image->fw_len + UPDATEFW_IMAGE_HEAD_SIZE) != image_size) { 538c2ecf20Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "Wrong data size read from file\n"); 548c2ecf20Sopenharmony_ci return false; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci host_image->image_info.up_total_len = fw_image->fw_len; 588c2ecf20Sopenharmony_ci host_image->image_info.fw_version = fw_image->fw_version; 598c2ecf20Sopenharmony_ci host_image->section_type_num = fw_image->fw_info.fw_section_cnt; 608c2ecf20Sopenharmony_ci host_image->device_id = fw_image->device_id; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return true; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic bool check_image_integrity(struct hinic_devlink_priv *priv, 668c2ecf20Sopenharmony_ci struct host_image_st *host_image, 678c2ecf20Sopenharmony_ci u32 update_type) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci u32 collect_section_type = 0; 708c2ecf20Sopenharmony_ci u32 i, type; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci for (i = 0; i < host_image->section_type_num; i++) { 738c2ecf20Sopenharmony_ci type = host_image->image_section_info[i].fw_section_type; 748c2ecf20Sopenharmony_ci if (collect_section_type & (1U << type)) { 758c2ecf20Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "Duplicate section type: %u\n", 768c2ecf20Sopenharmony_ci type); 778c2ecf20Sopenharmony_ci return false; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci collect_section_type |= (1U << type); 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (update_type == FW_UPDATE_COLD && 838c2ecf20Sopenharmony_ci (((collect_section_type & _IMAGE_COLD_SUB_MODULES_MUST_IN) == 848c2ecf20Sopenharmony_ci _IMAGE_COLD_SUB_MODULES_MUST_IN) || 858c2ecf20Sopenharmony_ci collect_section_type == _IMAGE_CFG_SUB_MODULES_MUST_IN)) 868c2ecf20Sopenharmony_ci return true; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (update_type == FW_UPDATE_HOT && 898c2ecf20Sopenharmony_ci (collect_section_type & _IMAGE_HOT_SUB_MODULES_MUST_IN) == 908c2ecf20Sopenharmony_ci _IMAGE_HOT_SUB_MODULES_MUST_IN) 918c2ecf20Sopenharmony_ci return true; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (update_type == FW_UPDATE_COLD) 948c2ecf20Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "Check file integrity failed, valid: 0x%x or 0x%lx, current: 0x%x\n", 958c2ecf20Sopenharmony_ci _IMAGE_COLD_SUB_MODULES_MUST_IN, 968c2ecf20Sopenharmony_ci _IMAGE_CFG_SUB_MODULES_MUST_IN, collect_section_type); 978c2ecf20Sopenharmony_ci else 988c2ecf20Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "Check file integrity failed, valid:0x%x, current: 0x%x\n", 998c2ecf20Sopenharmony_ci _IMAGE_HOT_SUB_MODULES_MUST_IN, collect_section_type); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return false; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int check_image_device_type(struct hinic_devlink_priv *priv, 1058c2ecf20Sopenharmony_ci u32 image_device_type) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct hinic_comm_board_info board_info = {0}; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (hinic_get_board_info(priv->hwdev, &board_info)) { 1108c2ecf20Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "Get board info failed\n"); 1118c2ecf20Sopenharmony_ci return false; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (image_device_type == board_info.info.board_type) 1158c2ecf20Sopenharmony_ci return true; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_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"); 1188c2ecf20Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "The image device type: 0x%x, firmware device type: 0x%x\n", 1198c2ecf20Sopenharmony_ci image_device_type, board_info.info.board_type); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return false; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic int hinic_flash_fw(struct hinic_devlink_priv *priv, const u8 *data, 1258c2ecf20Sopenharmony_ci struct host_image_st *host_image) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci u32 section_remain_send_len, send_fragment_len, send_pos, up_total_len; 1288c2ecf20Sopenharmony_ci struct hinic_cmd_update_fw *fw_update_msg = NULL; 1298c2ecf20Sopenharmony_ci u32 section_type, section_crc, section_version; 1308c2ecf20Sopenharmony_ci u32 i, len, section_len, section_offset; 1318c2ecf20Sopenharmony_ci u16 out_size = sizeof(*fw_update_msg); 1328c2ecf20Sopenharmony_ci int total_len_flag = 0; 1338c2ecf20Sopenharmony_ci int err; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci fw_update_msg = kzalloc(sizeof(*fw_update_msg), GFP_KERNEL); 1368c2ecf20Sopenharmony_ci if (!fw_update_msg) 1378c2ecf20Sopenharmony_ci return -ENOMEM; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci up_total_len = host_image->image_info.up_total_len; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci for (i = 0; i < host_image->section_type_num; i++) { 1428c2ecf20Sopenharmony_ci len = host_image->image_section_info[i].fw_section_len; 1438c2ecf20Sopenharmony_ci if (host_image->image_section_info[i].fw_section_type == 1448c2ecf20Sopenharmony_ci UP_FW_UPDATE_BOOT) { 1458c2ecf20Sopenharmony_ci up_total_len = up_total_len - len; 1468c2ecf20Sopenharmony_ci break; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci for (i = 0; i < host_image->section_type_num; i++) { 1518c2ecf20Sopenharmony_ci section_len = 1528c2ecf20Sopenharmony_ci host_image->image_section_info[i].fw_section_len; 1538c2ecf20Sopenharmony_ci section_offset = 1548c2ecf20Sopenharmony_ci host_image->image_section_info[i].fw_section_offset; 1558c2ecf20Sopenharmony_ci section_remain_send_len = section_len; 1568c2ecf20Sopenharmony_ci section_type = 1578c2ecf20Sopenharmony_ci host_image->image_section_info[i].fw_section_type; 1588c2ecf20Sopenharmony_ci section_crc = host_image->image_section_info[i].fw_section_crc; 1598c2ecf20Sopenharmony_ci section_version = 1608c2ecf20Sopenharmony_ci host_image->image_section_info[i].fw_section_version; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (section_type == UP_FW_UPDATE_BOOT) 1638c2ecf20Sopenharmony_ci continue; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci send_fragment_len = 0; 1668c2ecf20Sopenharmony_ci send_pos = 0; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci while (section_remain_send_len > 0) { 1698c2ecf20Sopenharmony_ci if (!total_len_flag) { 1708c2ecf20Sopenharmony_ci fw_update_msg->total_len = up_total_len; 1718c2ecf20Sopenharmony_ci total_len_flag = 1; 1728c2ecf20Sopenharmony_ci } else { 1738c2ecf20Sopenharmony_ci fw_update_msg->total_len = 0; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci memset(fw_update_msg->data, 0, MAX_FW_FRAGMENT_LEN); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci fw_update_msg->ctl_info.SF = 1798c2ecf20Sopenharmony_ci (section_remain_send_len == section_len) ? 1808c2ecf20Sopenharmony_ci true : false; 1818c2ecf20Sopenharmony_ci fw_update_msg->section_info.FW_section_CRC = section_crc; 1828c2ecf20Sopenharmony_ci fw_update_msg->fw_section_version = section_version; 1838c2ecf20Sopenharmony_ci fw_update_msg->ctl_info.flag = UP_TYPE_A; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (section_type <= UP_FW_UPDATE_UP_DATA_B) { 1868c2ecf20Sopenharmony_ci fw_update_msg->section_info.FW_section_type = 1878c2ecf20Sopenharmony_ci (section_type % 2) ? 1888c2ecf20Sopenharmony_ci UP_FW_UPDATE_UP_DATA : 1898c2ecf20Sopenharmony_ci UP_FW_UPDATE_UP_TEXT; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci fw_update_msg->ctl_info.flag = UP_TYPE_B; 1928c2ecf20Sopenharmony_ci if (section_type <= UP_FW_UPDATE_UP_DATA_A) 1938c2ecf20Sopenharmony_ci fw_update_msg->ctl_info.flag = UP_TYPE_A; 1948c2ecf20Sopenharmony_ci } else { 1958c2ecf20Sopenharmony_ci fw_update_msg->section_info.FW_section_type = 1968c2ecf20Sopenharmony_ci section_type - 0x2; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci fw_update_msg->setion_total_len = section_len; 2008c2ecf20Sopenharmony_ci fw_update_msg->section_offset = send_pos; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (section_remain_send_len <= MAX_FW_FRAGMENT_LEN) { 2038c2ecf20Sopenharmony_ci fw_update_msg->ctl_info.SL = true; 2048c2ecf20Sopenharmony_ci fw_update_msg->ctl_info.fragment_len = 2058c2ecf20Sopenharmony_ci section_remain_send_len; 2068c2ecf20Sopenharmony_ci send_fragment_len += section_remain_send_len; 2078c2ecf20Sopenharmony_ci } else { 2088c2ecf20Sopenharmony_ci fw_update_msg->ctl_info.SL = false; 2098c2ecf20Sopenharmony_ci fw_update_msg->ctl_info.fragment_len = 2108c2ecf20Sopenharmony_ci MAX_FW_FRAGMENT_LEN; 2118c2ecf20Sopenharmony_ci send_fragment_len += MAX_FW_FRAGMENT_LEN; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci memcpy(fw_update_msg->data, 2158c2ecf20Sopenharmony_ci data + UPDATEFW_IMAGE_HEAD_SIZE + 2168c2ecf20Sopenharmony_ci section_offset + send_pos, 2178c2ecf20Sopenharmony_ci fw_update_msg->ctl_info.fragment_len); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci err = hinic_port_msg_cmd(priv->hwdev, 2208c2ecf20Sopenharmony_ci HINIC_PORT_CMD_UPDATE_FW, 2218c2ecf20Sopenharmony_ci fw_update_msg, 2228c2ecf20Sopenharmony_ci sizeof(*fw_update_msg), 2238c2ecf20Sopenharmony_ci fw_update_msg, &out_size); 2248c2ecf20Sopenharmony_ci if (err || !out_size || fw_update_msg->status) { 2258c2ecf20Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "Failed to update firmware, err: %d, status: 0x%x, out size: 0x%x\n", 2268c2ecf20Sopenharmony_ci err, fw_update_msg->status, out_size); 2278c2ecf20Sopenharmony_ci err = fw_update_msg->status ? 2288c2ecf20Sopenharmony_ci fw_update_msg->status : -EIO; 2298c2ecf20Sopenharmony_ci kfree(fw_update_msg); 2308c2ecf20Sopenharmony_ci return err; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci send_pos = send_fragment_len; 2348c2ecf20Sopenharmony_ci section_remain_send_len = section_len - 2358c2ecf20Sopenharmony_ci send_fragment_len; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci kfree(fw_update_msg); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic int hinic_firmware_update(struct hinic_devlink_priv *priv, 2458c2ecf20Sopenharmony_ci const struct firmware *fw, 2468c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct host_image_st host_image; 2498c2ecf20Sopenharmony_ci int err; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci memset(&host_image, 0, sizeof(struct host_image_st)); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (!check_image_valid(priv, fw->data, fw->size, &host_image) || 2548c2ecf20Sopenharmony_ci !check_image_integrity(priv, &host_image, FW_UPDATE_COLD) || 2558c2ecf20Sopenharmony_ci !check_image_device_type(priv, host_image.device_id)) { 2568c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Check image failed"); 2578c2ecf20Sopenharmony_ci return -EINVAL; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci dev_info(&priv->hwdev->hwif->pdev->dev, "Flash firmware begin\n"); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci err = hinic_flash_fw(priv, fw->data, &host_image); 2638c2ecf20Sopenharmony_ci if (err) { 2648c2ecf20Sopenharmony_ci if (err == HINIC_FW_DISMATCH_ERROR) { 2658c2ecf20Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "Firmware image doesn't match this card, please use newer image, err: %d\n", 2668c2ecf20Sopenharmony_ci err); 2678c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 2688c2ecf20Sopenharmony_ci "Firmware image doesn't match this card, please use newer image"); 2698c2ecf20Sopenharmony_ci } else { 2708c2ecf20Sopenharmony_ci dev_err(&priv->hwdev->hwif->pdev->dev, "Send firmware image data failed, err: %d\n", 2718c2ecf20Sopenharmony_ci err); 2728c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Send firmware image data failed"); 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return err; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci dev_info(&priv->hwdev->hwif->pdev->dev, "Flash firmware end\n"); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return 0; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int hinic_devlink_flash_update(struct devlink *devlink, 2848c2ecf20Sopenharmony_ci struct devlink_flash_update_params *params, 2858c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci struct hinic_devlink_priv *priv = devlink_priv(devlink); 2888c2ecf20Sopenharmony_ci const struct firmware *fw; 2898c2ecf20Sopenharmony_ci int err; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci err = request_firmware_direct(&fw, params->file_name, 2928c2ecf20Sopenharmony_ci &priv->hwdev->hwif->pdev->dev); 2938c2ecf20Sopenharmony_ci if (err) 2948c2ecf20Sopenharmony_ci return err; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci err = hinic_firmware_update(priv, fw, extack); 2978c2ecf20Sopenharmony_ci release_firmware(fw); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci return err; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic const struct devlink_ops hinic_devlink_ops = { 3038c2ecf20Sopenharmony_ci .flash_update = hinic_devlink_flash_update, 3048c2ecf20Sopenharmony_ci}; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistruct devlink *hinic_devlink_alloc(void) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci return devlink_alloc(&hinic_devlink_ops, sizeof(struct hinic_dev)); 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_civoid hinic_devlink_free(struct devlink *devlink) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci devlink_free(devlink); 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ciint hinic_devlink_register(struct hinic_devlink_priv *priv, struct device *dev) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct devlink *devlink = priv_to_devlink(priv); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci return devlink_register(devlink, dev); 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_civoid hinic_devlink_unregister(struct hinic_devlink_priv *priv) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct devlink *devlink = priv_to_devlink(priv); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci devlink_unregister(devlink); 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic int chip_fault_show(struct devlink_fmsg *fmsg, 3318c2ecf20Sopenharmony_ci struct hinic_fault_event *event) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci const char * const level_str[FAULT_LEVEL_MAX + 1] = { 3348c2ecf20Sopenharmony_ci "fatal", "reset", "flr", "general", "suggestion", "Unknown"}; 3358c2ecf20Sopenharmony_ci u8 fault_level; 3368c2ecf20Sopenharmony_ci int err; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci fault_level = (event->event.chip.err_level < FAULT_LEVEL_MAX) ? 3398c2ecf20Sopenharmony_ci event->event.chip.err_level : FAULT_LEVEL_MAX; 3408c2ecf20Sopenharmony_ci if (fault_level == FAULT_LEVEL_SERIOUS_FLR) { 3418c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "Function level err func_id", 3428c2ecf20Sopenharmony_ci (u32)event->event.chip.func_id); 3438c2ecf20Sopenharmony_ci if (err) 3448c2ecf20Sopenharmony_ci return err; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci err = devlink_fmsg_u8_pair_put(fmsg, "module_id", event->event.chip.node_id); 3488c2ecf20Sopenharmony_ci if (err) 3498c2ecf20Sopenharmony_ci return err; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "err_type", (u32)event->event.chip.err_type); 3528c2ecf20Sopenharmony_ci if (err) 3538c2ecf20Sopenharmony_ci return err; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci err = devlink_fmsg_string_pair_put(fmsg, "err_level", level_str[fault_level]); 3568c2ecf20Sopenharmony_ci if (err) 3578c2ecf20Sopenharmony_ci return err; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "err_csr_addr", 3608c2ecf20Sopenharmony_ci event->event.chip.err_csr_addr); 3618c2ecf20Sopenharmony_ci if (err) 3628c2ecf20Sopenharmony_ci return err; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "err_csr_value", 3658c2ecf20Sopenharmony_ci event->event.chip.err_csr_value); 3668c2ecf20Sopenharmony_ci if (err) 3678c2ecf20Sopenharmony_ci return err; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci return 0; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic int fault_report_show(struct devlink_fmsg *fmsg, 3738c2ecf20Sopenharmony_ci struct hinic_fault_event *event) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci const char * const type_str[FAULT_TYPE_MAX + 1] = { 3768c2ecf20Sopenharmony_ci "chip", "ucode", "mem rd timeout", "mem wr timeout", 3778c2ecf20Sopenharmony_ci "reg rd timeout", "reg wr timeout", "phy fault", "Unknown"}; 3788c2ecf20Sopenharmony_ci u8 fault_type; 3798c2ecf20Sopenharmony_ci int err; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci fault_type = (event->type < FAULT_TYPE_MAX) ? event->type : FAULT_TYPE_MAX; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci err = devlink_fmsg_string_pair_put(fmsg, "Fault type", type_str[fault_type]); 3848c2ecf20Sopenharmony_ci if (err) 3858c2ecf20Sopenharmony_ci return err; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci err = devlink_fmsg_binary_pair_put(fmsg, "Fault raw data", 3888c2ecf20Sopenharmony_ci event->event.val, sizeof(event->event.val)); 3898c2ecf20Sopenharmony_ci if (err) 3908c2ecf20Sopenharmony_ci return err; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci switch (event->type) { 3938c2ecf20Sopenharmony_ci case FAULT_TYPE_CHIP: 3948c2ecf20Sopenharmony_ci err = chip_fault_show(fmsg, event); 3958c2ecf20Sopenharmony_ci if (err) 3968c2ecf20Sopenharmony_ci return err; 3978c2ecf20Sopenharmony_ci break; 3988c2ecf20Sopenharmony_ci case FAULT_TYPE_UCODE: 3998c2ecf20Sopenharmony_ci err = devlink_fmsg_u8_pair_put(fmsg, "Cause_id", event->event.ucode.cause_id); 4008c2ecf20Sopenharmony_ci if (err) 4018c2ecf20Sopenharmony_ci return err; 4028c2ecf20Sopenharmony_ci err = devlink_fmsg_u8_pair_put(fmsg, "core_id", event->event.ucode.core_id); 4038c2ecf20Sopenharmony_ci if (err) 4048c2ecf20Sopenharmony_ci return err; 4058c2ecf20Sopenharmony_ci err = devlink_fmsg_u8_pair_put(fmsg, "c_id", event->event.ucode.c_id); 4068c2ecf20Sopenharmony_ci if (err) 4078c2ecf20Sopenharmony_ci return err; 4088c2ecf20Sopenharmony_ci err = devlink_fmsg_u8_pair_put(fmsg, "epc", event->event.ucode.epc); 4098c2ecf20Sopenharmony_ci if (err) 4108c2ecf20Sopenharmony_ci return err; 4118c2ecf20Sopenharmony_ci break; 4128c2ecf20Sopenharmony_ci case FAULT_TYPE_MEM_RD_TIMEOUT: 4138c2ecf20Sopenharmony_ci case FAULT_TYPE_MEM_WR_TIMEOUT: 4148c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "Err_csr_ctrl", 4158c2ecf20Sopenharmony_ci event->event.mem_timeout.err_csr_ctrl); 4168c2ecf20Sopenharmony_ci if (err) 4178c2ecf20Sopenharmony_ci return err; 4188c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "err_csr_data", 4198c2ecf20Sopenharmony_ci event->event.mem_timeout.err_csr_data); 4208c2ecf20Sopenharmony_ci if (err) 4218c2ecf20Sopenharmony_ci return err; 4228c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "ctrl_tab", 4238c2ecf20Sopenharmony_ci event->event.mem_timeout.ctrl_tab); 4248c2ecf20Sopenharmony_ci if (err) 4258c2ecf20Sopenharmony_ci return err; 4268c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "mem_index", 4278c2ecf20Sopenharmony_ci event->event.mem_timeout.mem_index); 4288c2ecf20Sopenharmony_ci if (err) 4298c2ecf20Sopenharmony_ci return err; 4308c2ecf20Sopenharmony_ci break; 4318c2ecf20Sopenharmony_ci case FAULT_TYPE_REG_RD_TIMEOUT: 4328c2ecf20Sopenharmony_ci case FAULT_TYPE_REG_WR_TIMEOUT: 4338c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "Err_csr", event->event.reg_timeout.err_csr); 4348c2ecf20Sopenharmony_ci if (err) 4358c2ecf20Sopenharmony_ci return err; 4368c2ecf20Sopenharmony_ci break; 4378c2ecf20Sopenharmony_ci case FAULT_TYPE_PHY_FAULT: 4388c2ecf20Sopenharmony_ci err = devlink_fmsg_u8_pair_put(fmsg, "Op_type", event->event.phy_fault.op_type); 4398c2ecf20Sopenharmony_ci if (err) 4408c2ecf20Sopenharmony_ci return err; 4418c2ecf20Sopenharmony_ci err = devlink_fmsg_u8_pair_put(fmsg, "port_id", event->event.phy_fault.port_id); 4428c2ecf20Sopenharmony_ci if (err) 4438c2ecf20Sopenharmony_ci return err; 4448c2ecf20Sopenharmony_ci err = devlink_fmsg_u8_pair_put(fmsg, "dev_ad", event->event.phy_fault.dev_ad); 4458c2ecf20Sopenharmony_ci if (err) 4468c2ecf20Sopenharmony_ci return err; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "csr_addr", event->event.phy_fault.csr_addr); 4498c2ecf20Sopenharmony_ci if (err) 4508c2ecf20Sopenharmony_ci return err; 4518c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "op_data", event->event.phy_fault.op_data); 4528c2ecf20Sopenharmony_ci if (err) 4538c2ecf20Sopenharmony_ci return err; 4548c2ecf20Sopenharmony_ci break; 4558c2ecf20Sopenharmony_ci default: 4568c2ecf20Sopenharmony_ci break; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci return 0; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic int hinic_hw_reporter_dump(struct devlink_health_reporter *reporter, 4638c2ecf20Sopenharmony_ci struct devlink_fmsg *fmsg, void *priv_ctx, 4648c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci if (priv_ctx) 4678c2ecf20Sopenharmony_ci return fault_report_show(fmsg, priv_ctx); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci return 0; 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic int mgmt_watchdog_report_show(struct devlink_fmsg *fmsg, 4738c2ecf20Sopenharmony_ci struct hinic_mgmt_watchdog_info *watchdog_info) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci int err; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "Mgmt deadloop time_h", watchdog_info->curr_time_h); 4788c2ecf20Sopenharmony_ci if (err) 4798c2ecf20Sopenharmony_ci return err; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "time_l", watchdog_info->curr_time_l); 4828c2ecf20Sopenharmony_ci if (err) 4838c2ecf20Sopenharmony_ci return err; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "task_id", watchdog_info->task_id); 4868c2ecf20Sopenharmony_ci if (err) 4878c2ecf20Sopenharmony_ci return err; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "sp", watchdog_info->sp); 4908c2ecf20Sopenharmony_ci if (err) 4918c2ecf20Sopenharmony_ci return err; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "stack_current_used", watchdog_info->curr_used); 4948c2ecf20Sopenharmony_ci if (err) 4958c2ecf20Sopenharmony_ci return err; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "peak_used", watchdog_info->peak_used); 4988c2ecf20Sopenharmony_ci if (err) 4998c2ecf20Sopenharmony_ci return err; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "\n Overflow_flag", watchdog_info->is_overflow); 5028c2ecf20Sopenharmony_ci if (err) 5038c2ecf20Sopenharmony_ci return err; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "stack_top", watchdog_info->stack_top); 5068c2ecf20Sopenharmony_ci if (err) 5078c2ecf20Sopenharmony_ci return err; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "stack_bottom", watchdog_info->stack_bottom); 5108c2ecf20Sopenharmony_ci if (err) 5118c2ecf20Sopenharmony_ci return err; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "mgmt_pc", watchdog_info->pc); 5148c2ecf20Sopenharmony_ci if (err) 5158c2ecf20Sopenharmony_ci return err; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "lr", watchdog_info->lr); 5188c2ecf20Sopenharmony_ci if (err) 5198c2ecf20Sopenharmony_ci return err; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci err = devlink_fmsg_u32_pair_put(fmsg, "cpsr", watchdog_info->cpsr); 5228c2ecf20Sopenharmony_ci if (err) 5238c2ecf20Sopenharmony_ci return err; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci err = devlink_fmsg_binary_pair_put(fmsg, "Mgmt register info", 5268c2ecf20Sopenharmony_ci watchdog_info->reg, sizeof(watchdog_info->reg)); 5278c2ecf20Sopenharmony_ci if (err) 5288c2ecf20Sopenharmony_ci return err; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci err = devlink_fmsg_binary_pair_put(fmsg, "Mgmt dump stack(start from sp)", 5318c2ecf20Sopenharmony_ci watchdog_info->data, sizeof(watchdog_info->data)); 5328c2ecf20Sopenharmony_ci if (err) 5338c2ecf20Sopenharmony_ci return err; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci return 0; 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistatic int hinic_fw_reporter_dump(struct devlink_health_reporter *reporter, 5398c2ecf20Sopenharmony_ci struct devlink_fmsg *fmsg, void *priv_ctx, 5408c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci if (priv_ctx) 5438c2ecf20Sopenharmony_ci return mgmt_watchdog_report_show(fmsg, priv_ctx); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci return 0; 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cistatic const struct devlink_health_reporter_ops hinic_hw_fault_reporter_ops = { 5498c2ecf20Sopenharmony_ci .name = "hw", 5508c2ecf20Sopenharmony_ci .dump = hinic_hw_reporter_dump, 5518c2ecf20Sopenharmony_ci}; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic const struct devlink_health_reporter_ops hinic_fw_fault_reporter_ops = { 5548c2ecf20Sopenharmony_ci .name = "fw", 5558c2ecf20Sopenharmony_ci .dump = hinic_fw_reporter_dump, 5568c2ecf20Sopenharmony_ci}; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ciint hinic_health_reporters_create(struct hinic_devlink_priv *priv) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci struct devlink *devlink = priv_to_devlink(priv); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci priv->hw_fault_reporter = 5638c2ecf20Sopenharmony_ci devlink_health_reporter_create(devlink, &hinic_hw_fault_reporter_ops, 5648c2ecf20Sopenharmony_ci 0, priv); 5658c2ecf20Sopenharmony_ci if (IS_ERR(priv->hw_fault_reporter)) { 5668c2ecf20Sopenharmony_ci dev_warn(&priv->hwdev->hwif->pdev->dev, "Failed to create hw fault reporter, err: %ld\n", 5678c2ecf20Sopenharmony_ci PTR_ERR(priv->hw_fault_reporter)); 5688c2ecf20Sopenharmony_ci return PTR_ERR(priv->hw_fault_reporter); 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci priv->fw_fault_reporter = 5728c2ecf20Sopenharmony_ci devlink_health_reporter_create(devlink, &hinic_fw_fault_reporter_ops, 5738c2ecf20Sopenharmony_ci 0, priv); 5748c2ecf20Sopenharmony_ci if (IS_ERR(priv->fw_fault_reporter)) { 5758c2ecf20Sopenharmony_ci dev_warn(&priv->hwdev->hwif->pdev->dev, "Failed to create fw fault reporter, err: %ld\n", 5768c2ecf20Sopenharmony_ci PTR_ERR(priv->fw_fault_reporter)); 5778c2ecf20Sopenharmony_ci devlink_health_reporter_destroy(priv->hw_fault_reporter); 5788c2ecf20Sopenharmony_ci priv->hw_fault_reporter = NULL; 5798c2ecf20Sopenharmony_ci return PTR_ERR(priv->fw_fault_reporter); 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci return 0; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_civoid hinic_health_reporters_destroy(struct hinic_devlink_priv *priv) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(priv->fw_fault_reporter)) { 5888c2ecf20Sopenharmony_ci devlink_health_reporter_destroy(priv->fw_fault_reporter); 5898c2ecf20Sopenharmony_ci priv->fw_fault_reporter = NULL; 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(priv->hw_fault_reporter)) { 5938c2ecf20Sopenharmony_ci devlink_health_reporter_destroy(priv->hw_fault_reporter); 5948c2ecf20Sopenharmony_ci priv->hw_fault_reporter = NULL; 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci} 597