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