18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2018, Mellanox Technologies. All rights reserved.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two
58c2ecf20Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
68c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
78c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the
88c2ecf20Sopenharmony_ci * OpenIB.org BSD license below:
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
118c2ecf20Sopenharmony_ci *     without modification, are permitted provided that the following
128c2ecf20Sopenharmony_ci *     conditions are met:
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci *      - Redistributions of source code must retain the above
158c2ecf20Sopenharmony_ci *        copyright notice, this list of conditions and the following
168c2ecf20Sopenharmony_ci *        disclaimer.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
198c2ecf20Sopenharmony_ci *        copyright notice, this list of conditions and the following
208c2ecf20Sopenharmony_ci *        disclaimer in the documentation and/or other materials
218c2ecf20Sopenharmony_ci *        provided with the distribution.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
248c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
258c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
268c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
278c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
288c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
298c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
308c2ecf20Sopenharmony_ci * SOFTWARE.
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include "mlx4.h"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define BAD_ACCESS			0xBADACCE5
368c2ecf20Sopenharmony_ci#define HEALTH_BUFFER_SIZE		0x40
378c2ecf20Sopenharmony_ci#define CR_ENABLE_BIT			swab32(BIT(6))
388c2ecf20Sopenharmony_ci#define CR_ENABLE_BIT_OFFSET		0xF3F04
398c2ecf20Sopenharmony_ci#define MAX_NUM_OF_DUMPS_TO_STORE	(8)
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define REGION_CR_SPACE "cr-space"
428c2ecf20Sopenharmony_ci#define REGION_FW_HEALTH "fw-health"
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic const char * const region_cr_space_str = REGION_CR_SPACE;
458c2ecf20Sopenharmony_cistatic const char * const region_fw_health_str = REGION_FW_HEALTH;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic const struct devlink_region_ops region_cr_space_ops = {
488c2ecf20Sopenharmony_ci	.name = REGION_CR_SPACE,
498c2ecf20Sopenharmony_ci	.destructor = &kvfree,
508c2ecf20Sopenharmony_ci};
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic const struct devlink_region_ops region_fw_health_ops = {
538c2ecf20Sopenharmony_ci	.name = REGION_FW_HEALTH,
548c2ecf20Sopenharmony_ci	.destructor = &kvfree,
558c2ecf20Sopenharmony_ci};
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/* Set to true in case cr enable bit was set to true before crdump */
588c2ecf20Sopenharmony_cistatic bool crdump_enbale_bit_set;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic void crdump_enable_crspace_access(struct mlx4_dev *dev,
618c2ecf20Sopenharmony_ci					 u8 __iomem *cr_space)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	/* Get current enable bit value */
648c2ecf20Sopenharmony_ci	crdump_enbale_bit_set =
658c2ecf20Sopenharmony_ci		readl(cr_space + CR_ENABLE_BIT_OFFSET) & CR_ENABLE_BIT;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	/* Enable FW CR filter (set bit6 to 0) */
688c2ecf20Sopenharmony_ci	if (crdump_enbale_bit_set)
698c2ecf20Sopenharmony_ci		writel(readl(cr_space + CR_ENABLE_BIT_OFFSET) & ~CR_ENABLE_BIT,
708c2ecf20Sopenharmony_ci		       cr_space + CR_ENABLE_BIT_OFFSET);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	/* Enable block volatile crspace accesses */
738c2ecf20Sopenharmony_ci	writel(swab32(1), cr_space + dev->caps.health_buffer_addrs +
748c2ecf20Sopenharmony_ci	       HEALTH_BUFFER_SIZE);
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic void crdump_disable_crspace_access(struct mlx4_dev *dev,
788c2ecf20Sopenharmony_ci					  u8 __iomem *cr_space)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	/* Disable block volatile crspace accesses */
818c2ecf20Sopenharmony_ci	writel(0, cr_space + dev->caps.health_buffer_addrs +
828c2ecf20Sopenharmony_ci	       HEALTH_BUFFER_SIZE);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	/* Restore FW CR filter value (set bit6 to original value) */
858c2ecf20Sopenharmony_ci	if (crdump_enbale_bit_set)
868c2ecf20Sopenharmony_ci		writel(readl(cr_space + CR_ENABLE_BIT_OFFSET) | CR_ENABLE_BIT,
878c2ecf20Sopenharmony_ci		       cr_space + CR_ENABLE_BIT_OFFSET);
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic void mlx4_crdump_collect_crspace(struct mlx4_dev *dev,
918c2ecf20Sopenharmony_ci					u8 __iomem *cr_space,
928c2ecf20Sopenharmony_ci					u32 id)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	struct mlx4_fw_crdump *crdump = &dev->persist->crdump;
958c2ecf20Sopenharmony_ci	struct pci_dev *pdev = dev->persist->pdev;
968c2ecf20Sopenharmony_ci	unsigned long cr_res_size;
978c2ecf20Sopenharmony_ci	u8 *crspace_data;
988c2ecf20Sopenharmony_ci	int offset;
998c2ecf20Sopenharmony_ci	int err;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (!crdump->region_crspace) {
1028c2ecf20Sopenharmony_ci		mlx4_err(dev, "crdump: cr-space region is NULL\n");
1038c2ecf20Sopenharmony_ci		return;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	/* Try to collect CR space */
1078c2ecf20Sopenharmony_ci	cr_res_size = pci_resource_len(pdev, 0);
1088c2ecf20Sopenharmony_ci	crspace_data = kvmalloc(cr_res_size, GFP_KERNEL);
1098c2ecf20Sopenharmony_ci	if (crspace_data) {
1108c2ecf20Sopenharmony_ci		for (offset = 0; offset < cr_res_size; offset += 4)
1118c2ecf20Sopenharmony_ci			*(u32 *)(crspace_data + offset) =
1128c2ecf20Sopenharmony_ci					readl(cr_space + offset);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci		err = devlink_region_snapshot_create(crdump->region_crspace,
1158c2ecf20Sopenharmony_ci						     crspace_data, id);
1168c2ecf20Sopenharmony_ci		if (err) {
1178c2ecf20Sopenharmony_ci			kvfree(crspace_data);
1188c2ecf20Sopenharmony_ci			mlx4_warn(dev, "crdump: devlink create %s snapshot id %d err %d\n",
1198c2ecf20Sopenharmony_ci				  region_cr_space_str, id, err);
1208c2ecf20Sopenharmony_ci		} else {
1218c2ecf20Sopenharmony_ci			mlx4_info(dev, "crdump: added snapshot %d to devlink region %s\n",
1228c2ecf20Sopenharmony_ci				  id, region_cr_space_str);
1238c2ecf20Sopenharmony_ci		}
1248c2ecf20Sopenharmony_ci	} else {
1258c2ecf20Sopenharmony_ci		mlx4_err(dev, "crdump: Failed to allocate crspace buffer\n");
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic void mlx4_crdump_collect_fw_health(struct mlx4_dev *dev,
1308c2ecf20Sopenharmony_ci					  u8 __iomem *cr_space,
1318c2ecf20Sopenharmony_ci					  u32 id)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct mlx4_fw_crdump *crdump = &dev->persist->crdump;
1348c2ecf20Sopenharmony_ci	u8 *health_data;
1358c2ecf20Sopenharmony_ci	int offset;
1368c2ecf20Sopenharmony_ci	int err;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	if (!crdump->region_fw_health) {
1398c2ecf20Sopenharmony_ci		mlx4_err(dev, "crdump: fw-health region is NULL\n");
1408c2ecf20Sopenharmony_ci		return;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/* Try to collect health buffer */
1448c2ecf20Sopenharmony_ci	health_data = kvmalloc(HEALTH_BUFFER_SIZE, GFP_KERNEL);
1458c2ecf20Sopenharmony_ci	if (health_data) {
1468c2ecf20Sopenharmony_ci		u8 __iomem *health_buf_start =
1478c2ecf20Sopenharmony_ci				cr_space + dev->caps.health_buffer_addrs;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci		for (offset = 0; offset < HEALTH_BUFFER_SIZE; offset += 4)
1508c2ecf20Sopenharmony_ci			*(u32 *)(health_data + offset) =
1518c2ecf20Sopenharmony_ci					readl(health_buf_start + offset);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci		err = devlink_region_snapshot_create(crdump->region_fw_health,
1548c2ecf20Sopenharmony_ci						     health_data, id);
1558c2ecf20Sopenharmony_ci		if (err) {
1568c2ecf20Sopenharmony_ci			kvfree(health_data);
1578c2ecf20Sopenharmony_ci			mlx4_warn(dev, "crdump: devlink create %s snapshot id %d err %d\n",
1588c2ecf20Sopenharmony_ci				  region_fw_health_str, id, err);
1598c2ecf20Sopenharmony_ci		} else {
1608c2ecf20Sopenharmony_ci			mlx4_info(dev, "crdump: added snapshot %d to devlink region %s\n",
1618c2ecf20Sopenharmony_ci				  id, region_fw_health_str);
1628c2ecf20Sopenharmony_ci		}
1638c2ecf20Sopenharmony_ci	} else {
1648c2ecf20Sopenharmony_ci		mlx4_err(dev, "crdump: Failed to allocate health buffer\n");
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ciint mlx4_crdump_collect(struct mlx4_dev *dev)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	struct devlink *devlink = priv_to_devlink(mlx4_priv(dev));
1718c2ecf20Sopenharmony_ci	struct mlx4_fw_crdump *crdump = &dev->persist->crdump;
1728c2ecf20Sopenharmony_ci	struct pci_dev *pdev = dev->persist->pdev;
1738c2ecf20Sopenharmony_ci	unsigned long cr_res_size;
1748c2ecf20Sopenharmony_ci	u8 __iomem *cr_space;
1758c2ecf20Sopenharmony_ci	int err;
1768c2ecf20Sopenharmony_ci	u32 id;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	if (!dev->caps.health_buffer_addrs) {
1798c2ecf20Sopenharmony_ci		mlx4_info(dev, "crdump: FW doesn't support health buffer access, skipping\n");
1808c2ecf20Sopenharmony_ci		return 0;
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (!crdump->snapshot_enable) {
1848c2ecf20Sopenharmony_ci		mlx4_info(dev, "crdump: devlink snapshot disabled, skipping\n");
1858c2ecf20Sopenharmony_ci		return 0;
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	cr_res_size = pci_resource_len(pdev, 0);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	cr_space = ioremap(pci_resource_start(pdev, 0), cr_res_size);
1918c2ecf20Sopenharmony_ci	if (!cr_space) {
1928c2ecf20Sopenharmony_ci		mlx4_err(dev, "crdump: Failed to map pci cr region\n");
1938c2ecf20Sopenharmony_ci		return -ENODEV;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	/* Get the available snapshot ID for the dumps */
1978c2ecf20Sopenharmony_ci	err = devlink_region_snapshot_id_get(devlink, &id);
1988c2ecf20Sopenharmony_ci	if (err) {
1998c2ecf20Sopenharmony_ci		mlx4_err(dev, "crdump: devlink get snapshot id err %d\n", err);
2008c2ecf20Sopenharmony_ci		iounmap(cr_space);
2018c2ecf20Sopenharmony_ci		return err;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	crdump_enable_crspace_access(dev, cr_space);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	/* Try to capture dumps */
2078c2ecf20Sopenharmony_ci	mlx4_crdump_collect_crspace(dev, cr_space, id);
2088c2ecf20Sopenharmony_ci	mlx4_crdump_collect_fw_health(dev, cr_space, id);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	/* Release reference on the snapshot id */
2118c2ecf20Sopenharmony_ci	devlink_region_snapshot_id_put(devlink, id);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	crdump_disable_crspace_access(dev, cr_space);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	iounmap(cr_space);
2168c2ecf20Sopenharmony_ci	return 0;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ciint mlx4_crdump_init(struct mlx4_dev *dev)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	struct devlink *devlink = priv_to_devlink(mlx4_priv(dev));
2228c2ecf20Sopenharmony_ci	struct mlx4_fw_crdump *crdump = &dev->persist->crdump;
2238c2ecf20Sopenharmony_ci	struct pci_dev *pdev = dev->persist->pdev;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	crdump->snapshot_enable = false;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	/* Create cr-space region */
2288c2ecf20Sopenharmony_ci	crdump->region_crspace =
2298c2ecf20Sopenharmony_ci		devlink_region_create(devlink,
2308c2ecf20Sopenharmony_ci				      &region_cr_space_ops,
2318c2ecf20Sopenharmony_ci				      MAX_NUM_OF_DUMPS_TO_STORE,
2328c2ecf20Sopenharmony_ci				      pci_resource_len(pdev, 0));
2338c2ecf20Sopenharmony_ci	if (IS_ERR(crdump->region_crspace))
2348c2ecf20Sopenharmony_ci		mlx4_warn(dev, "crdump: create devlink region %s err %ld\n",
2358c2ecf20Sopenharmony_ci			  region_cr_space_str,
2368c2ecf20Sopenharmony_ci			  PTR_ERR(crdump->region_crspace));
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	/* Create fw-health region */
2398c2ecf20Sopenharmony_ci	crdump->region_fw_health =
2408c2ecf20Sopenharmony_ci		devlink_region_create(devlink,
2418c2ecf20Sopenharmony_ci				      &region_fw_health_ops,
2428c2ecf20Sopenharmony_ci				      MAX_NUM_OF_DUMPS_TO_STORE,
2438c2ecf20Sopenharmony_ci				      HEALTH_BUFFER_SIZE);
2448c2ecf20Sopenharmony_ci	if (IS_ERR(crdump->region_fw_health))
2458c2ecf20Sopenharmony_ci		mlx4_warn(dev, "crdump: create devlink region %s err %ld\n",
2468c2ecf20Sopenharmony_ci			  region_fw_health_str,
2478c2ecf20Sopenharmony_ci			  PTR_ERR(crdump->region_fw_health));
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	return 0;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_civoid mlx4_crdump_end(struct mlx4_dev *dev)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	struct mlx4_fw_crdump *crdump = &dev->persist->crdump;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	devlink_region_destroy(crdump->region_fw_health);
2578c2ecf20Sopenharmony_ci	devlink_region_destroy(crdump->region_crspace);
2588c2ecf20Sopenharmony_ci}
259