18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2007 Cisco Systems, Inc. All rights reserved.
38c2ecf20Sopenharmony_ci * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two
68c2ecf20Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
78c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
88c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the
98c2ecf20Sopenharmony_ci * OpenIB.org BSD license below:
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
128c2ecf20Sopenharmony_ci *     without modification, are permitted provided that the following
138c2ecf20Sopenharmony_ci *     conditions are met:
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci *      - Redistributions of source code must retain the above
168c2ecf20Sopenharmony_ci *        copyright notice, this list of conditions and the following
178c2ecf20Sopenharmony_ci *        disclaimer.
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
208c2ecf20Sopenharmony_ci *        copyright notice, this list of conditions and the following
218c2ecf20Sopenharmony_ci *        disclaimer in the documentation and/or other materials
228c2ecf20Sopenharmony_ci *        provided with the distribution.
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
258c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
268c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
278c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
288c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
298c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
308c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
318c2ecf20Sopenharmony_ci * SOFTWARE.
328c2ecf20Sopenharmony_ci */
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
358c2ecf20Sopenharmony_ci#include <linux/module.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include "mlx4.h"
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cienum {
408c2ecf20Sopenharmony_ci	MLX4_CATAS_POLL_INTERVAL	= 5 * HZ,
418c2ecf20Sopenharmony_ci};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ciint mlx4_internal_err_reset = 1;
468c2ecf20Sopenharmony_cimodule_param_named(internal_err_reset, mlx4_internal_err_reset,  int, 0644);
478c2ecf20Sopenharmony_ciMODULE_PARM_DESC(internal_err_reset,
488c2ecf20Sopenharmony_ci		 "Reset device on internal errors if non-zero (default 1)");
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic int read_vendor_id(struct mlx4_dev *dev)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	u16 vendor_id = 0;
538c2ecf20Sopenharmony_ci	int ret;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	ret = pci_read_config_word(dev->persist->pdev, 0, &vendor_id);
568c2ecf20Sopenharmony_ci	if (ret) {
578c2ecf20Sopenharmony_ci		mlx4_err(dev, "Failed to read vendor ID, ret=%d\n", ret);
588c2ecf20Sopenharmony_ci		return ret;
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	if (vendor_id == 0xffff) {
628c2ecf20Sopenharmony_ci		mlx4_err(dev, "PCI can't be accessed to read vendor id\n");
638c2ecf20Sopenharmony_ci		return -EINVAL;
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	return 0;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic int mlx4_reset_master(struct mlx4_dev *dev)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	int err = 0;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if (mlx4_is_master(dev))
748c2ecf20Sopenharmony_ci		mlx4_report_internal_err_comm_event(dev);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	if (!pci_channel_offline(dev->persist->pdev)) {
778c2ecf20Sopenharmony_ci		err = read_vendor_id(dev);
788c2ecf20Sopenharmony_ci		/* If PCI can't be accessed to read vendor ID we assume that its
798c2ecf20Sopenharmony_ci		 * link was disabled and chip was already reset.
808c2ecf20Sopenharmony_ci		 */
818c2ecf20Sopenharmony_ci		if (err)
828c2ecf20Sopenharmony_ci			return 0;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci		err = mlx4_reset(dev);
858c2ecf20Sopenharmony_ci		if (err)
868c2ecf20Sopenharmony_ci			mlx4_err(dev, "Fail to reset HCA\n");
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	return err;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic int mlx4_reset_slave(struct mlx4_dev *dev)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci#define COM_CHAN_RST_REQ_OFFSET 0x10
958c2ecf20Sopenharmony_ci#define COM_CHAN_RST_ACK_OFFSET 0x08
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	u32 comm_flags;
988c2ecf20Sopenharmony_ci	u32 rst_req;
998c2ecf20Sopenharmony_ci	u32 rst_ack;
1008c2ecf20Sopenharmony_ci	unsigned long end;
1018c2ecf20Sopenharmony_ci	struct mlx4_priv *priv = mlx4_priv(dev);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (pci_channel_offline(dev->persist->pdev))
1048c2ecf20Sopenharmony_ci		return 0;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	comm_flags = swab32(readl((__iomem char *)priv->mfunc.comm +
1078c2ecf20Sopenharmony_ci				  MLX4_COMM_CHAN_FLAGS));
1088c2ecf20Sopenharmony_ci	if (comm_flags == 0xffffffff) {
1098c2ecf20Sopenharmony_ci		mlx4_err(dev, "VF reset is not needed\n");
1108c2ecf20Sopenharmony_ci		return 0;
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	if (!(dev->caps.vf_caps & MLX4_VF_CAP_FLAG_RESET)) {
1148c2ecf20Sopenharmony_ci		mlx4_err(dev, "VF reset is not supported\n");
1158c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	rst_req = (comm_flags & (u32)(1 << COM_CHAN_RST_REQ_OFFSET)) >>
1198c2ecf20Sopenharmony_ci		COM_CHAN_RST_REQ_OFFSET;
1208c2ecf20Sopenharmony_ci	rst_ack = (comm_flags & (u32)(1 << COM_CHAN_RST_ACK_OFFSET)) >>
1218c2ecf20Sopenharmony_ci		COM_CHAN_RST_ACK_OFFSET;
1228c2ecf20Sopenharmony_ci	if (rst_req != rst_ack) {
1238c2ecf20Sopenharmony_ci		mlx4_err(dev, "Communication channel isn't sync, fail to send reset\n");
1248c2ecf20Sopenharmony_ci		return -EIO;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	rst_req ^= 1;
1288c2ecf20Sopenharmony_ci	mlx4_warn(dev, "VF is sending reset request to Firmware\n");
1298c2ecf20Sopenharmony_ci	comm_flags = rst_req << COM_CHAN_RST_REQ_OFFSET;
1308c2ecf20Sopenharmony_ci	__raw_writel((__force u32)cpu_to_be32(comm_flags),
1318c2ecf20Sopenharmony_ci		     (__iomem char *)priv->mfunc.comm + MLX4_COMM_CHAN_FLAGS);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	end = msecs_to_jiffies(MLX4_COMM_TIME) + jiffies;
1348c2ecf20Sopenharmony_ci	while (time_before(jiffies, end)) {
1358c2ecf20Sopenharmony_ci		comm_flags = swab32(readl((__iomem char *)priv->mfunc.comm +
1368c2ecf20Sopenharmony_ci					  MLX4_COMM_CHAN_FLAGS));
1378c2ecf20Sopenharmony_ci		rst_ack = (comm_flags & (u32)(1 << COM_CHAN_RST_ACK_OFFSET)) >>
1388c2ecf20Sopenharmony_ci			COM_CHAN_RST_ACK_OFFSET;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci		/* Reading rst_req again since the communication channel can
1418c2ecf20Sopenharmony_ci		 * be reset at any time by the PF and all its bits will be
1428c2ecf20Sopenharmony_ci		 * set to zero.
1438c2ecf20Sopenharmony_ci		 */
1448c2ecf20Sopenharmony_ci		rst_req = (comm_flags & (u32)(1 << COM_CHAN_RST_REQ_OFFSET)) >>
1458c2ecf20Sopenharmony_ci			COM_CHAN_RST_REQ_OFFSET;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci		if (rst_ack == rst_req) {
1488c2ecf20Sopenharmony_ci			mlx4_warn(dev, "VF Reset succeed\n");
1498c2ecf20Sopenharmony_ci			return 0;
1508c2ecf20Sopenharmony_ci		}
1518c2ecf20Sopenharmony_ci		cond_resched();
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci	mlx4_err(dev, "Fail to send reset over the communication channel\n");
1548c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ciint mlx4_comm_internal_err(u32 slave_read)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	return (u32)COMM_CHAN_EVENT_INTERNAL_ERR ==
1608c2ecf20Sopenharmony_ci		(slave_read & (u32)COMM_CHAN_EVENT_INTERNAL_ERR) ? 1 : 0;
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_civoid mlx4_enter_error_state(struct mlx4_dev_persistent *persist)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	int err;
1668c2ecf20Sopenharmony_ci	struct mlx4_dev *dev;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (!mlx4_internal_err_reset)
1698c2ecf20Sopenharmony_ci		return;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	mutex_lock(&persist->device_state_mutex);
1728c2ecf20Sopenharmony_ci	if (persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR)
1738c2ecf20Sopenharmony_ci		goto out;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	dev = persist->dev;
1768c2ecf20Sopenharmony_ci	mlx4_err(dev, "device is going to be reset\n");
1778c2ecf20Sopenharmony_ci	if (mlx4_is_slave(dev)) {
1788c2ecf20Sopenharmony_ci		err = mlx4_reset_slave(dev);
1798c2ecf20Sopenharmony_ci	} else {
1808c2ecf20Sopenharmony_ci		mlx4_crdump_collect(dev);
1818c2ecf20Sopenharmony_ci		err = mlx4_reset_master(dev);
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (!err) {
1858c2ecf20Sopenharmony_ci		mlx4_err(dev, "device was reset successfully\n");
1868c2ecf20Sopenharmony_ci	} else {
1878c2ecf20Sopenharmony_ci		/* EEH could have disabled the PCI channel during reset. That's
1888c2ecf20Sopenharmony_ci		 * recoverable and the PCI error flow will handle it.
1898c2ecf20Sopenharmony_ci		 */
1908c2ecf20Sopenharmony_ci		if (!pci_channel_offline(dev->persist->pdev))
1918c2ecf20Sopenharmony_ci			BUG_ON(1);
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci	dev->persist->state |= MLX4_DEVICE_STATE_INTERNAL_ERROR;
1948c2ecf20Sopenharmony_ci	mutex_unlock(&persist->device_state_mutex);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	/* At that step HW was already reset, now notify clients */
1978c2ecf20Sopenharmony_ci	mlx4_dispatch_event(dev, MLX4_DEV_EVENT_CATASTROPHIC_ERROR, 0);
1988c2ecf20Sopenharmony_ci	mlx4_cmd_wake_completions(dev);
1998c2ecf20Sopenharmony_ci	return;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ciout:
2028c2ecf20Sopenharmony_ci	mutex_unlock(&persist->device_state_mutex);
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic void mlx4_handle_error_state(struct mlx4_dev_persistent *persist)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	int err = 0;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	mlx4_enter_error_state(persist);
2108c2ecf20Sopenharmony_ci	mutex_lock(&persist->interface_state_mutex);
2118c2ecf20Sopenharmony_ci	if (persist->interface_state & MLX4_INTERFACE_STATE_UP &&
2128c2ecf20Sopenharmony_ci	    !(persist->interface_state & MLX4_INTERFACE_STATE_DELETION)) {
2138c2ecf20Sopenharmony_ci		err = mlx4_restart_one(persist->pdev);
2148c2ecf20Sopenharmony_ci		mlx4_info(persist->dev, "mlx4_restart_one was ended, ret=%d\n",
2158c2ecf20Sopenharmony_ci			  err);
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci	mutex_unlock(&persist->interface_state_mutex);
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic void dump_err_buf(struct mlx4_dev *dev)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct mlx4_priv *priv = mlx4_priv(dev);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	int i;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	mlx4_err(dev, "Internal error detected:\n");
2278c2ecf20Sopenharmony_ci	for (i = 0; i < priv->fw.catas_size; ++i)
2288c2ecf20Sopenharmony_ci		mlx4_err(dev, "  buf[%02x]: %08x\n",
2298c2ecf20Sopenharmony_ci			 i, swab32(readl(priv->catas_err.map + i)));
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic void poll_catas(struct timer_list *t)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	struct mlx4_priv *priv = from_timer(priv, t, catas_err.timer);
2358c2ecf20Sopenharmony_ci	struct mlx4_dev *dev = &priv->dev;
2368c2ecf20Sopenharmony_ci	u32 slave_read;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	if (mlx4_is_slave(dev)) {
2398c2ecf20Sopenharmony_ci		slave_read = swab32(readl(&priv->mfunc.comm->slave_read));
2408c2ecf20Sopenharmony_ci		if (mlx4_comm_internal_err(slave_read)) {
2418c2ecf20Sopenharmony_ci			mlx4_warn(dev, "Internal error detected on the communication channel\n");
2428c2ecf20Sopenharmony_ci			goto internal_err;
2438c2ecf20Sopenharmony_ci		}
2448c2ecf20Sopenharmony_ci	} else if (readl(priv->catas_err.map)) {
2458c2ecf20Sopenharmony_ci		dump_err_buf(dev);
2468c2ecf20Sopenharmony_ci		goto internal_err;
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	if (dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR) {
2508c2ecf20Sopenharmony_ci		mlx4_warn(dev, "Internal error mark was detected on device\n");
2518c2ecf20Sopenharmony_ci		goto internal_err;
2528c2ecf20Sopenharmony_ci	}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	mod_timer(&priv->catas_err.timer,
2558c2ecf20Sopenharmony_ci		  round_jiffies(jiffies + MLX4_CATAS_POLL_INTERVAL));
2568c2ecf20Sopenharmony_ci	return;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ciinternal_err:
2598c2ecf20Sopenharmony_ci	if (mlx4_internal_err_reset)
2608c2ecf20Sopenharmony_ci		queue_work(dev->persist->catas_wq, &dev->persist->catas_work);
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic void catas_reset(struct work_struct *work)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	struct mlx4_dev_persistent *persist =
2668c2ecf20Sopenharmony_ci		container_of(work, struct mlx4_dev_persistent,
2678c2ecf20Sopenharmony_ci			     catas_work);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	mlx4_handle_error_state(persist);
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_civoid mlx4_start_catas_poll(struct mlx4_dev *dev)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	struct mlx4_priv *priv = mlx4_priv(dev);
2758c2ecf20Sopenharmony_ci	phys_addr_t addr;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&priv->catas_err.list);
2788c2ecf20Sopenharmony_ci	timer_setup(&priv->catas_err.timer, poll_catas, 0);
2798c2ecf20Sopenharmony_ci	priv->catas_err.map = NULL;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	if (!mlx4_is_slave(dev)) {
2828c2ecf20Sopenharmony_ci		addr = pci_resource_start(dev->persist->pdev,
2838c2ecf20Sopenharmony_ci					  priv->fw.catas_bar) +
2848c2ecf20Sopenharmony_ci					  priv->fw.catas_offset;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci		priv->catas_err.map = ioremap(addr, priv->fw.catas_size * 4);
2878c2ecf20Sopenharmony_ci		if (!priv->catas_err.map) {
2888c2ecf20Sopenharmony_ci			mlx4_warn(dev, "Failed to map internal error buffer at 0x%llx\n",
2898c2ecf20Sopenharmony_ci				  (unsigned long long)addr);
2908c2ecf20Sopenharmony_ci			return;
2918c2ecf20Sopenharmony_ci		}
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	priv->catas_err.timer.expires  =
2958c2ecf20Sopenharmony_ci		round_jiffies(jiffies + MLX4_CATAS_POLL_INTERVAL);
2968c2ecf20Sopenharmony_ci	add_timer(&priv->catas_err.timer);
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_civoid mlx4_stop_catas_poll(struct mlx4_dev *dev)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	struct mlx4_priv *priv = mlx4_priv(dev);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	del_timer_sync(&priv->catas_err.timer);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	if (priv->catas_err.map) {
3068c2ecf20Sopenharmony_ci		iounmap(priv->catas_err.map);
3078c2ecf20Sopenharmony_ci		priv->catas_err.map = NULL;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (dev->persist->interface_state & MLX4_INTERFACE_STATE_DELETION)
3118c2ecf20Sopenharmony_ci		flush_workqueue(dev->persist->catas_wq);
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ciint  mlx4_catas_init(struct mlx4_dev *dev)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	INIT_WORK(&dev->persist->catas_work, catas_reset);
3178c2ecf20Sopenharmony_ci	dev->persist->catas_wq = create_singlethread_workqueue("mlx4_health");
3188c2ecf20Sopenharmony_ci	if (!dev->persist->catas_wq)
3198c2ecf20Sopenharmony_ci		return -ENOMEM;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	return 0;
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_civoid mlx4_catas_end(struct mlx4_dev *dev)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	if (dev->persist->catas_wq) {
3278c2ecf20Sopenharmony_ci		destroy_workqueue(dev->persist->catas_wq);
3288c2ecf20Sopenharmony_ci		dev->persist->catas_wq = NULL;
3298c2ecf20Sopenharmony_ci	}
3308c2ecf20Sopenharmony_ci}
331