111820Sjulian// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
211820Sjulian/* Copyright (c) 2019 Mellanox Technologies */
311820Sjulian
411820Sjulian#include <linux/mlx5/driver.h>
511820Sjulian#include "mlx5_core.h"
611820Sjulian#include "lib/pci_vsc.h"
711820Sjulian#include "lib/mlx5.h"
811820Sjulian
911820Sjulian#define BAD_ACCESS			0xBADACCE5
1011820Sjulian#define MLX5_PROTECTED_CR_SCAN_CRSPACE	0x7
1111820Sjulian
1211820Sjulianstatic bool mlx5_crdump_enabled(struct mlx5_core_dev *dev)
1311820Sjulian{
1411820Sjulian	return !!dev->priv.health.crdump_size;
1511820Sjulian}
1611820Sjulian
1711820Sjulianstatic int mlx5_crdump_fill(struct mlx5_core_dev *dev, u32 *cr_data)
1811820Sjulian{
1911820Sjulian	u32 crdump_size = dev->priv.health.crdump_size;
2011820Sjulian	int i, ret;
2111820Sjulian
2211820Sjulian	for (i = 0; i < (crdump_size / 4); i++)
2311820Sjulian		cr_data[i] = BAD_ACCESS;
2411820Sjulian
2511820Sjulian	ret = mlx5_vsc_gw_read_block_fast(dev, cr_data, crdump_size);
2611820Sjulian	if (ret <= 0) {
2711820Sjulian		if (ret == 0)
2811820Sjulian			return -EIO;
2911820Sjulian		return ret;
3011820Sjulian	}
3111820Sjulian
3211820Sjulian	if (crdump_size != ret) {
3311820Sjulian		mlx5_core_warn(dev, "failed to read full dump, read %d out of %u\n",
3411820Sjulian			       ret, crdump_size);
3511820Sjulian		return -EINVAL;
3611820Sjulian	}
3750479Speter
3811820Sjulian	return 0;
3911820Sjulian}
4011820Sjulian
41122760Strhodesint mlx5_crdump_collect(struct mlx5_core_dev *dev, u32 *cr_data)
4211820Sjulian{
4311820Sjulian	int ret;
4411820Sjulian
4511820Sjulian	if (!mlx5_crdump_enabled(dev))
4611820Sjulian		return -ENODEV;
4711820Sjulian
4811820Sjulian	ret = mlx5_vsc_gw_lock(dev);
4911820Sjulian	if (ret) {
5011820Sjulian		mlx5_core_warn(dev, "crdump: failed to lock vsc gw err %d\n",
5111820Sjulian			       ret);
5211820Sjulian		return ret;
5311820Sjulian	}
5411820Sjulian	/* Verify no other PF is running cr-dump or sw reset */
5511820Sjulian	ret = mlx5_vsc_sem_set_space(dev, MLX5_SEMAPHORE_SW_RESET,
5611820Sjulian				     MLX5_VSC_LOCK);
5711820Sjulian	if (ret) {
5811820Sjulian		if (ret == -EBUSY)
5911820Sjulian			mlx5_core_info(dev, "SW reset semaphore is already in use\n");
6011820Sjulian		else
6127244Sjhay			mlx5_core_warn(dev, "Failed to lock SW reset semaphore\n");
6211820Sjulian		goto unlock_gw;
6311820Sjulian	}
6411820Sjulian
6511820Sjulian	ret = mlx5_vsc_gw_set_space(dev, MLX5_VSC_SPACE_SCAN_CRSPACE, NULL);
6611820Sjulian	if (ret)
6711820Sjulian		goto unlock_sem;
6811820Sjulian
6911820Sjulian	ret = mlx5_crdump_fill(dev, cr_data);
7011820Sjulian
7111820Sjulianunlock_sem:
7211820Sjulian	mlx5_vsc_sem_set_space(dev, MLX5_SEMAPHORE_SW_RESET, MLX5_VSC_UNLOCK);
7311820Sjulianunlock_gw:
7411820Sjulian	mlx5_vsc_gw_unlock(dev);
7511820Sjulian	return ret;
7611820Sjulian}
7711820Sjulian
7811820Sjulianint mlx5_crdump_enable(struct mlx5_core_dev *dev)
7911820Sjulian{
8011820Sjulian	struct mlx5_priv *priv = &dev->priv;
8111820Sjulian	u32 space_size;
8211820Sjulian	int ret;
8311820Sjulian
8411820Sjulian	if (!mlx5_core_is_pf(dev) || !mlx5_vsc_accessible(dev) ||
8511820Sjulian	    mlx5_crdump_enabled(dev))
8611820Sjulian		return 0;
8711820Sjulian
8811820Sjulian	ret = mlx5_vsc_gw_lock(dev);
8911820Sjulian	if (ret)
9011820Sjulian		return ret;
9111820Sjulian
9211820Sjulian	/* Check if space is supported and get space size */
9311820Sjulian	ret = mlx5_vsc_gw_set_space(dev, MLX5_VSC_SPACE_SCAN_CRSPACE,
9411820Sjulian				    &space_size);
9511820Sjulian	if (ret) {
9611820Sjulian		/* Unlock and mask error since space is not supported */
9711820Sjulian		mlx5_vsc_gw_unlock(dev);
9811820Sjulian		return 0;
9911820Sjulian	}
10011820Sjulian
10111820Sjulian	if (!space_size) {
10211820Sjulian		mlx5_core_warn(dev, "Invalid Crspace size, zero\n");
10311820Sjulian		mlx5_vsc_gw_unlock(dev);
10411820Sjulian		return -EINVAL;
10511820Sjulian	}
10611820Sjulian
10711820Sjulian	ret = mlx5_vsc_gw_unlock(dev);
10811820Sjulian	if (ret)
10911820Sjulian		return ret;
11011820Sjulian
11111820Sjulian	priv->health.crdump_size = space_size;
11211820Sjulian	return 0;
11311820Sjulian}
11411820Sjulian
11511820Sjulianvoid mlx5_crdump_disable(struct mlx5_core_dev *dev)
11611820Sjulian{
11711820Sjulian	dev->priv.health.crdump_size = 0;
11811820Sjulian}
11911820Sjulian