162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2018, Mellanox Technologies. All rights reserved.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * This software is available to you under a choice of one of two
562306a36Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
662306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
762306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the
862306a36Sopenharmony_ci * OpenIB.org BSD license below:
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
1162306a36Sopenharmony_ci *     without modification, are permitted provided that the following
1262306a36Sopenharmony_ci *     conditions are met:
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *      - Redistributions of source code must retain the above
1562306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
1662306a36Sopenharmony_ci *        disclaimer.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
1962306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
2062306a36Sopenharmony_ci *        disclaimer in the documentation and/or other materials
2162306a36Sopenharmony_ci *        provided with the distribution.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2462306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2562306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2662306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
2762306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
2862306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
2962306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3062306a36Sopenharmony_ci * SOFTWARE.
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include "mlx4.h"
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define BAD_ACCESS			0xBADACCE5
3662306a36Sopenharmony_ci#define HEALTH_BUFFER_SIZE		0x40
3762306a36Sopenharmony_ci#define CR_ENABLE_BIT			swab32(BIT(6))
3862306a36Sopenharmony_ci#define CR_ENABLE_BIT_OFFSET		0xF3F04
3962306a36Sopenharmony_ci#define MAX_NUM_OF_DUMPS_TO_STORE	(8)
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define REGION_CR_SPACE "cr-space"
4262306a36Sopenharmony_ci#define REGION_FW_HEALTH "fw-health"
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic const char * const region_cr_space_str = REGION_CR_SPACE;
4562306a36Sopenharmony_cistatic const char * const region_fw_health_str = REGION_FW_HEALTH;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic const struct devlink_region_ops region_cr_space_ops = {
4862306a36Sopenharmony_ci	.name = REGION_CR_SPACE,
4962306a36Sopenharmony_ci	.destructor = &kvfree,
5062306a36Sopenharmony_ci};
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic const struct devlink_region_ops region_fw_health_ops = {
5362306a36Sopenharmony_ci	.name = REGION_FW_HEALTH,
5462306a36Sopenharmony_ci	.destructor = &kvfree,
5562306a36Sopenharmony_ci};
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/* Set to true in case cr enable bit was set to true before crdump */
5862306a36Sopenharmony_cistatic bool crdump_enbale_bit_set;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic void crdump_enable_crspace_access(struct mlx4_dev *dev,
6162306a36Sopenharmony_ci					 u8 __iomem *cr_space)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	/* Get current enable bit value */
6462306a36Sopenharmony_ci	crdump_enbale_bit_set =
6562306a36Sopenharmony_ci		readl(cr_space + CR_ENABLE_BIT_OFFSET) & CR_ENABLE_BIT;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	/* Enable FW CR filter (set bit6 to 0) */
6862306a36Sopenharmony_ci	if (crdump_enbale_bit_set)
6962306a36Sopenharmony_ci		writel(readl(cr_space + CR_ENABLE_BIT_OFFSET) & ~CR_ENABLE_BIT,
7062306a36Sopenharmony_ci		       cr_space + CR_ENABLE_BIT_OFFSET);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	/* Enable block volatile crspace accesses */
7362306a36Sopenharmony_ci	writel(swab32(1), cr_space + dev->caps.health_buffer_addrs +
7462306a36Sopenharmony_ci	       HEALTH_BUFFER_SIZE);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic void crdump_disable_crspace_access(struct mlx4_dev *dev,
7862306a36Sopenharmony_ci					  u8 __iomem *cr_space)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	/* Disable block volatile crspace accesses */
8162306a36Sopenharmony_ci	writel(0, cr_space + dev->caps.health_buffer_addrs +
8262306a36Sopenharmony_ci	       HEALTH_BUFFER_SIZE);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/* Restore FW CR filter value (set bit6 to original value) */
8562306a36Sopenharmony_ci	if (crdump_enbale_bit_set)
8662306a36Sopenharmony_ci		writel(readl(cr_space + CR_ENABLE_BIT_OFFSET) | CR_ENABLE_BIT,
8762306a36Sopenharmony_ci		       cr_space + CR_ENABLE_BIT_OFFSET);
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic void mlx4_crdump_collect_crspace(struct mlx4_dev *dev,
9162306a36Sopenharmony_ci					u8 __iomem *cr_space,
9262306a36Sopenharmony_ci					u32 id)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct mlx4_fw_crdump *crdump = &dev->persist->crdump;
9562306a36Sopenharmony_ci	struct pci_dev *pdev = dev->persist->pdev;
9662306a36Sopenharmony_ci	unsigned long cr_res_size;
9762306a36Sopenharmony_ci	u8 *crspace_data;
9862306a36Sopenharmony_ci	int offset;
9962306a36Sopenharmony_ci	int err;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (!crdump->region_crspace) {
10262306a36Sopenharmony_ci		mlx4_err(dev, "crdump: cr-space region is NULL\n");
10362306a36Sopenharmony_ci		return;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/* Try to collect CR space */
10762306a36Sopenharmony_ci	cr_res_size = pci_resource_len(pdev, 0);
10862306a36Sopenharmony_ci	crspace_data = kvmalloc(cr_res_size, GFP_KERNEL);
10962306a36Sopenharmony_ci	if (crspace_data) {
11062306a36Sopenharmony_ci		for (offset = 0; offset < cr_res_size; offset += 4)
11162306a36Sopenharmony_ci			*(u32 *)(crspace_data + offset) =
11262306a36Sopenharmony_ci					readl(cr_space + offset);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		err = devlink_region_snapshot_create(crdump->region_crspace,
11562306a36Sopenharmony_ci						     crspace_data, id);
11662306a36Sopenharmony_ci		if (err) {
11762306a36Sopenharmony_ci			kvfree(crspace_data);
11862306a36Sopenharmony_ci			mlx4_warn(dev, "crdump: devlink create %s snapshot id %d err %d\n",
11962306a36Sopenharmony_ci				  region_cr_space_str, id, err);
12062306a36Sopenharmony_ci		} else {
12162306a36Sopenharmony_ci			mlx4_info(dev, "crdump: added snapshot %d to devlink region %s\n",
12262306a36Sopenharmony_ci				  id, region_cr_space_str);
12362306a36Sopenharmony_ci		}
12462306a36Sopenharmony_ci	} else {
12562306a36Sopenharmony_ci		mlx4_err(dev, "crdump: Failed to allocate crspace buffer\n");
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic void mlx4_crdump_collect_fw_health(struct mlx4_dev *dev,
13062306a36Sopenharmony_ci					  u8 __iomem *cr_space,
13162306a36Sopenharmony_ci					  u32 id)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	struct mlx4_fw_crdump *crdump = &dev->persist->crdump;
13462306a36Sopenharmony_ci	u8 *health_data;
13562306a36Sopenharmony_ci	int offset;
13662306a36Sopenharmony_ci	int err;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	if (!crdump->region_fw_health) {
13962306a36Sopenharmony_ci		mlx4_err(dev, "crdump: fw-health region is NULL\n");
14062306a36Sopenharmony_ci		return;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	/* Try to collect health buffer */
14462306a36Sopenharmony_ci	health_data = kvmalloc(HEALTH_BUFFER_SIZE, GFP_KERNEL);
14562306a36Sopenharmony_ci	if (health_data) {
14662306a36Sopenharmony_ci		u8 __iomem *health_buf_start =
14762306a36Sopenharmony_ci				cr_space + dev->caps.health_buffer_addrs;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci		for (offset = 0; offset < HEALTH_BUFFER_SIZE; offset += 4)
15062306a36Sopenharmony_ci			*(u32 *)(health_data + offset) =
15162306a36Sopenharmony_ci					readl(health_buf_start + offset);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci		err = devlink_region_snapshot_create(crdump->region_fw_health,
15462306a36Sopenharmony_ci						     health_data, id);
15562306a36Sopenharmony_ci		if (err) {
15662306a36Sopenharmony_ci			kvfree(health_data);
15762306a36Sopenharmony_ci			mlx4_warn(dev, "crdump: devlink create %s snapshot id %d err %d\n",
15862306a36Sopenharmony_ci				  region_fw_health_str, id, err);
15962306a36Sopenharmony_ci		} else {
16062306a36Sopenharmony_ci			mlx4_info(dev, "crdump: added snapshot %d to devlink region %s\n",
16162306a36Sopenharmony_ci				  id, region_fw_health_str);
16262306a36Sopenharmony_ci		}
16362306a36Sopenharmony_ci	} else {
16462306a36Sopenharmony_ci		mlx4_err(dev, "crdump: Failed to allocate health buffer\n");
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ciint mlx4_crdump_collect(struct mlx4_dev *dev)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct devlink *devlink = priv_to_devlink(mlx4_priv(dev));
17162306a36Sopenharmony_ci	struct mlx4_fw_crdump *crdump = &dev->persist->crdump;
17262306a36Sopenharmony_ci	struct pci_dev *pdev = dev->persist->pdev;
17362306a36Sopenharmony_ci	unsigned long cr_res_size;
17462306a36Sopenharmony_ci	u8 __iomem *cr_space;
17562306a36Sopenharmony_ci	int err;
17662306a36Sopenharmony_ci	u32 id;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (!dev->caps.health_buffer_addrs) {
17962306a36Sopenharmony_ci		mlx4_info(dev, "crdump: FW doesn't support health buffer access, skipping\n");
18062306a36Sopenharmony_ci		return 0;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	if (!crdump->snapshot_enable) {
18462306a36Sopenharmony_ci		mlx4_info(dev, "crdump: devlink snapshot disabled, skipping\n");
18562306a36Sopenharmony_ci		return 0;
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	cr_res_size = pci_resource_len(pdev, 0);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	cr_space = ioremap(pci_resource_start(pdev, 0), cr_res_size);
19162306a36Sopenharmony_ci	if (!cr_space) {
19262306a36Sopenharmony_ci		mlx4_err(dev, "crdump: Failed to map pci cr region\n");
19362306a36Sopenharmony_ci		return -ENODEV;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/* Get the available snapshot ID for the dumps */
19762306a36Sopenharmony_ci	err = devlink_region_snapshot_id_get(devlink, &id);
19862306a36Sopenharmony_ci	if (err) {
19962306a36Sopenharmony_ci		mlx4_err(dev, "crdump: devlink get snapshot id err %d\n", err);
20062306a36Sopenharmony_ci		iounmap(cr_space);
20162306a36Sopenharmony_ci		return err;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	crdump_enable_crspace_access(dev, cr_space);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/* Try to capture dumps */
20762306a36Sopenharmony_ci	mlx4_crdump_collect_crspace(dev, cr_space, id);
20862306a36Sopenharmony_ci	mlx4_crdump_collect_fw_health(dev, cr_space, id);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	/* Release reference on the snapshot id */
21162306a36Sopenharmony_ci	devlink_region_snapshot_id_put(devlink, id);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	crdump_disable_crspace_access(dev, cr_space);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	iounmap(cr_space);
21662306a36Sopenharmony_ci	return 0;
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ciint mlx4_crdump_init(struct mlx4_dev *dev)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct devlink *devlink = priv_to_devlink(mlx4_priv(dev));
22262306a36Sopenharmony_ci	struct mlx4_fw_crdump *crdump = &dev->persist->crdump;
22362306a36Sopenharmony_ci	struct pci_dev *pdev = dev->persist->pdev;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	crdump->snapshot_enable = false;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	/* Create cr-space region */
22862306a36Sopenharmony_ci	crdump->region_crspace =
22962306a36Sopenharmony_ci		devl_region_create(devlink,
23062306a36Sopenharmony_ci				   &region_cr_space_ops,
23162306a36Sopenharmony_ci				   MAX_NUM_OF_DUMPS_TO_STORE,
23262306a36Sopenharmony_ci				   pci_resource_len(pdev, 0));
23362306a36Sopenharmony_ci	if (IS_ERR(crdump->region_crspace))
23462306a36Sopenharmony_ci		mlx4_warn(dev, "crdump: create devlink region %s err %ld\n",
23562306a36Sopenharmony_ci			  region_cr_space_str,
23662306a36Sopenharmony_ci			  PTR_ERR(crdump->region_crspace));
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* Create fw-health region */
23962306a36Sopenharmony_ci	crdump->region_fw_health =
24062306a36Sopenharmony_ci		devl_region_create(devlink,
24162306a36Sopenharmony_ci				   &region_fw_health_ops,
24262306a36Sopenharmony_ci				   MAX_NUM_OF_DUMPS_TO_STORE,
24362306a36Sopenharmony_ci				   HEALTH_BUFFER_SIZE);
24462306a36Sopenharmony_ci	if (IS_ERR(crdump->region_fw_health))
24562306a36Sopenharmony_ci		mlx4_warn(dev, "crdump: create devlink region %s err %ld\n",
24662306a36Sopenharmony_ci			  region_fw_health_str,
24762306a36Sopenharmony_ci			  PTR_ERR(crdump->region_fw_health));
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	return 0;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_civoid mlx4_crdump_end(struct mlx4_dev *dev)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct mlx4_fw_crdump *crdump = &dev->persist->crdump;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	devl_region_destroy(crdump->region_fw_health);
25762306a36Sopenharmony_ci	devl_region_destroy(crdump->region_crspace);
25862306a36Sopenharmony_ci}
259