18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
28c2ecf20Sopenharmony_ci/* QLogic qed NIC Driver
38c2ecf20Sopenharmony_ci * Copyright (c) 2015-2016  QLogic Corporation
48c2ecf20Sopenharmony_ci * Copyright (c) 2019-2020 Marvell International Ltd.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/crc32.h>
88c2ecf20Sopenharmony_ci#include "qed.h"
98c2ecf20Sopenharmony_ci#include "qed_dev_api.h"
108c2ecf20Sopenharmony_ci#include "qed_mcp.h"
118c2ecf20Sopenharmony_ci#include "qed_sp.h"
128c2ecf20Sopenharmony_ci#include "qed_selftest.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ciint qed_selftest_memory(struct qed_dev *cdev)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	int rc = 0, i;
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci	for_each_hwfn(cdev, i) {
198c2ecf20Sopenharmony_ci		rc = qed_sp_heartbeat_ramrod(&cdev->hwfns[i]);
208c2ecf20Sopenharmony_ci		if (rc)
218c2ecf20Sopenharmony_ci			return rc;
228c2ecf20Sopenharmony_ci	}
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	return rc;
258c2ecf20Sopenharmony_ci}
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ciint qed_selftest_interrupt(struct qed_dev *cdev)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	int rc = 0, i;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	for_each_hwfn(cdev, i) {
328c2ecf20Sopenharmony_ci		rc = qed_sp_heartbeat_ramrod(&cdev->hwfns[i]);
338c2ecf20Sopenharmony_ci		if (rc)
348c2ecf20Sopenharmony_ci			return rc;
358c2ecf20Sopenharmony_ci	}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	return rc;
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ciint qed_selftest_register(struct qed_dev *cdev)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	struct qed_hwfn *p_hwfn;
438c2ecf20Sopenharmony_ci	struct qed_ptt *p_ptt;
448c2ecf20Sopenharmony_ci	int rc = 0, i;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	/* although performed by MCP, this test is per engine */
478c2ecf20Sopenharmony_ci	for_each_hwfn(cdev, i) {
488c2ecf20Sopenharmony_ci		p_hwfn = &cdev->hwfns[i];
498c2ecf20Sopenharmony_ci		p_ptt = qed_ptt_acquire(p_hwfn);
508c2ecf20Sopenharmony_ci		if (!p_ptt) {
518c2ecf20Sopenharmony_ci			DP_ERR(p_hwfn, "failed to acquire ptt\n");
528c2ecf20Sopenharmony_ci			return -EBUSY;
538c2ecf20Sopenharmony_ci		}
548c2ecf20Sopenharmony_ci		rc = qed_mcp_bist_register_test(p_hwfn, p_ptt);
558c2ecf20Sopenharmony_ci		qed_ptt_release(p_hwfn, p_ptt);
568c2ecf20Sopenharmony_ci		if (rc)
578c2ecf20Sopenharmony_ci			break;
588c2ecf20Sopenharmony_ci	}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	return rc;
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ciint qed_selftest_clock(struct qed_dev *cdev)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct qed_hwfn *p_hwfn;
668c2ecf20Sopenharmony_ci	struct qed_ptt *p_ptt;
678c2ecf20Sopenharmony_ci	int rc = 0, i;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/* although performed by MCP, this test is per engine */
708c2ecf20Sopenharmony_ci	for_each_hwfn(cdev, i) {
718c2ecf20Sopenharmony_ci		p_hwfn = &cdev->hwfns[i];
728c2ecf20Sopenharmony_ci		p_ptt = qed_ptt_acquire(p_hwfn);
738c2ecf20Sopenharmony_ci		if (!p_ptt) {
748c2ecf20Sopenharmony_ci			DP_ERR(p_hwfn, "failed to acquire ptt\n");
758c2ecf20Sopenharmony_ci			return -EBUSY;
768c2ecf20Sopenharmony_ci		}
778c2ecf20Sopenharmony_ci		rc = qed_mcp_bist_clock_test(p_hwfn, p_ptt);
788c2ecf20Sopenharmony_ci		qed_ptt_release(p_hwfn, p_ptt);
798c2ecf20Sopenharmony_ci		if (rc)
808c2ecf20Sopenharmony_ci			break;
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	return rc;
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ciint qed_selftest_nvram(struct qed_dev *cdev)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
898c2ecf20Sopenharmony_ci	struct qed_ptt *p_ptt = qed_ptt_acquire(p_hwfn);
908c2ecf20Sopenharmony_ci	u32 num_images, i, j, nvm_crc, calc_crc;
918c2ecf20Sopenharmony_ci	struct bist_nvm_image_att image_att;
928c2ecf20Sopenharmony_ci	u8 *buf = NULL;
938c2ecf20Sopenharmony_ci	__be32 val;
948c2ecf20Sopenharmony_ci	int rc;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (!p_ptt) {
978c2ecf20Sopenharmony_ci		DP_ERR(p_hwfn, "failed to acquire ptt\n");
988c2ecf20Sopenharmony_ci		return -EBUSY;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	/* Acquire from MFW the amount of available images */
1028c2ecf20Sopenharmony_ci	rc = qed_mcp_bist_nvm_get_num_images(p_hwfn, p_ptt, &num_images);
1038c2ecf20Sopenharmony_ci	if (rc || !num_images) {
1048c2ecf20Sopenharmony_ci		DP_ERR(p_hwfn, "Failed getting number of images\n");
1058c2ecf20Sopenharmony_ci		rc = -EINVAL;
1068c2ecf20Sopenharmony_ci		goto err0;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	/* Iterate over images and validate CRC */
1108c2ecf20Sopenharmony_ci	for (i = 0; i < num_images; i++) {
1118c2ecf20Sopenharmony_ci		/* This mailbox returns information about the image required for
1128c2ecf20Sopenharmony_ci		 * reading it.
1138c2ecf20Sopenharmony_ci		 */
1148c2ecf20Sopenharmony_ci		rc = qed_mcp_bist_nvm_get_image_att(p_hwfn, p_ptt,
1158c2ecf20Sopenharmony_ci						    &image_att, i);
1168c2ecf20Sopenharmony_ci		if (rc) {
1178c2ecf20Sopenharmony_ci			DP_ERR(p_hwfn,
1188c2ecf20Sopenharmony_ci			       "Failed getting image index %d attributes\n",
1198c2ecf20Sopenharmony_ci			       i);
1208c2ecf20Sopenharmony_ci			goto err0;
1218c2ecf20Sopenharmony_ci		}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci		/* After MFW crash dump is collected - the image's CRC stops
1248c2ecf20Sopenharmony_ci		 * being valid.
1258c2ecf20Sopenharmony_ci		 */
1268c2ecf20Sopenharmony_ci		if (image_att.image_type == NVM_TYPE_MDUMP)
1278c2ecf20Sopenharmony_ci			continue;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci		DP_VERBOSE(p_hwfn, QED_MSG_SP, "image index %d, size %x\n",
1308c2ecf20Sopenharmony_ci			   i, image_att.len);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci		/* Allocate a buffer for holding the nvram image */
1338c2ecf20Sopenharmony_ci		buf = kzalloc(image_att.len, GFP_KERNEL);
1348c2ecf20Sopenharmony_ci		if (!buf) {
1358c2ecf20Sopenharmony_ci			rc = -ENOMEM;
1368c2ecf20Sopenharmony_ci			goto err0;
1378c2ecf20Sopenharmony_ci		}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci		/* Read image into buffer */
1408c2ecf20Sopenharmony_ci		rc = qed_mcp_nvm_read(p_hwfn->cdev, image_att.nvm_start_addr,
1418c2ecf20Sopenharmony_ci				      buf, image_att.len);
1428c2ecf20Sopenharmony_ci		if (rc) {
1438c2ecf20Sopenharmony_ci			DP_ERR(p_hwfn,
1448c2ecf20Sopenharmony_ci			       "Failed reading image index %d from nvm.\n", i);
1458c2ecf20Sopenharmony_ci			goto err1;
1468c2ecf20Sopenharmony_ci		}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci		/* Convert the buffer into big-endian format (excluding the
1498c2ecf20Sopenharmony_ci		 * closing 4 bytes of CRC).
1508c2ecf20Sopenharmony_ci		 */
1518c2ecf20Sopenharmony_ci		for (j = 0; j < image_att.len - 4; j += 4) {
1528c2ecf20Sopenharmony_ci			val = cpu_to_be32(*(u32 *)&buf[j]);
1538c2ecf20Sopenharmony_ci			*(u32 *)&buf[j] = (__force u32)val;
1548c2ecf20Sopenharmony_ci		}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci		/* Calc CRC for the "actual" image buffer, i.e. not including
1578c2ecf20Sopenharmony_ci		 * the last 4 CRC bytes.
1588c2ecf20Sopenharmony_ci		 */
1598c2ecf20Sopenharmony_ci		nvm_crc = *(u32 *)(buf + image_att.len - 4);
1608c2ecf20Sopenharmony_ci		calc_crc = crc32(0xffffffff, buf, image_att.len - 4);
1618c2ecf20Sopenharmony_ci		calc_crc = (__force u32)~cpu_to_be32(calc_crc);
1628c2ecf20Sopenharmony_ci		DP_VERBOSE(p_hwfn, QED_MSG_SP,
1638c2ecf20Sopenharmony_ci			   "nvm crc 0x%x, calc_crc 0x%x\n", nvm_crc, calc_crc);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		if (calc_crc != nvm_crc) {
1668c2ecf20Sopenharmony_ci			rc = -EINVAL;
1678c2ecf20Sopenharmony_ci			goto err1;
1688c2ecf20Sopenharmony_ci		}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci		/* Done with this image; Free to prevent double release
1718c2ecf20Sopenharmony_ci		 * on subsequent failure.
1728c2ecf20Sopenharmony_ci		 */
1738c2ecf20Sopenharmony_ci		kfree(buf);
1748c2ecf20Sopenharmony_ci		buf = NULL;
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	qed_ptt_release(p_hwfn, p_ptt);
1788c2ecf20Sopenharmony_ci	return 0;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cierr1:
1818c2ecf20Sopenharmony_ci	kfree(buf);
1828c2ecf20Sopenharmony_cierr0:
1838c2ecf20Sopenharmony_ci	qed_ptt_release(p_hwfn, p_ptt);
1848c2ecf20Sopenharmony_ci	return rc;
1858c2ecf20Sopenharmony_ci}
186