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