18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/printk.h>
58c2ecf20Sopenharmony_ci#include <linux/dynamic_debug.h>
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
88c2ecf20Sopenharmony_ci#include <linux/utsname.h>
98c2ecf20Sopenharmony_ci#include <generated/utsrelease.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "ionic.h"
128c2ecf20Sopenharmony_ci#include "ionic_bus.h"
138c2ecf20Sopenharmony_ci#include "ionic_lif.h"
148c2ecf20Sopenharmony_ci#include "ionic_debugfs.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(IONIC_DRV_DESCRIPTION);
178c2ecf20Sopenharmony_ciMODULE_AUTHOR("Pensando Systems, Inc");
188c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic const char *ionic_error_to_str(enum ionic_status_code code)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	switch (code) {
238c2ecf20Sopenharmony_ci	case IONIC_RC_SUCCESS:
248c2ecf20Sopenharmony_ci		return "IONIC_RC_SUCCESS";
258c2ecf20Sopenharmony_ci	case IONIC_RC_EVERSION:
268c2ecf20Sopenharmony_ci		return "IONIC_RC_EVERSION";
278c2ecf20Sopenharmony_ci	case IONIC_RC_EOPCODE:
288c2ecf20Sopenharmony_ci		return "IONIC_RC_EOPCODE";
298c2ecf20Sopenharmony_ci	case IONIC_RC_EIO:
308c2ecf20Sopenharmony_ci		return "IONIC_RC_EIO";
318c2ecf20Sopenharmony_ci	case IONIC_RC_EPERM:
328c2ecf20Sopenharmony_ci		return "IONIC_RC_EPERM";
338c2ecf20Sopenharmony_ci	case IONIC_RC_EQID:
348c2ecf20Sopenharmony_ci		return "IONIC_RC_EQID";
358c2ecf20Sopenharmony_ci	case IONIC_RC_EQTYPE:
368c2ecf20Sopenharmony_ci		return "IONIC_RC_EQTYPE";
378c2ecf20Sopenharmony_ci	case IONIC_RC_ENOENT:
388c2ecf20Sopenharmony_ci		return "IONIC_RC_ENOENT";
398c2ecf20Sopenharmony_ci	case IONIC_RC_EINTR:
408c2ecf20Sopenharmony_ci		return "IONIC_RC_EINTR";
418c2ecf20Sopenharmony_ci	case IONIC_RC_EAGAIN:
428c2ecf20Sopenharmony_ci		return "IONIC_RC_EAGAIN";
438c2ecf20Sopenharmony_ci	case IONIC_RC_ENOMEM:
448c2ecf20Sopenharmony_ci		return "IONIC_RC_ENOMEM";
458c2ecf20Sopenharmony_ci	case IONIC_RC_EFAULT:
468c2ecf20Sopenharmony_ci		return "IONIC_RC_EFAULT";
478c2ecf20Sopenharmony_ci	case IONIC_RC_EBUSY:
488c2ecf20Sopenharmony_ci		return "IONIC_RC_EBUSY";
498c2ecf20Sopenharmony_ci	case IONIC_RC_EEXIST:
508c2ecf20Sopenharmony_ci		return "IONIC_RC_EEXIST";
518c2ecf20Sopenharmony_ci	case IONIC_RC_EINVAL:
528c2ecf20Sopenharmony_ci		return "IONIC_RC_EINVAL";
538c2ecf20Sopenharmony_ci	case IONIC_RC_ENOSPC:
548c2ecf20Sopenharmony_ci		return "IONIC_RC_ENOSPC";
558c2ecf20Sopenharmony_ci	case IONIC_RC_ERANGE:
568c2ecf20Sopenharmony_ci		return "IONIC_RC_ERANGE";
578c2ecf20Sopenharmony_ci	case IONIC_RC_BAD_ADDR:
588c2ecf20Sopenharmony_ci		return "IONIC_RC_BAD_ADDR";
598c2ecf20Sopenharmony_ci	case IONIC_RC_DEV_CMD:
608c2ecf20Sopenharmony_ci		return "IONIC_RC_DEV_CMD";
618c2ecf20Sopenharmony_ci	case IONIC_RC_ENOSUPP:
628c2ecf20Sopenharmony_ci		return "IONIC_RC_ENOSUPP";
638c2ecf20Sopenharmony_ci	case IONIC_RC_ERROR:
648c2ecf20Sopenharmony_ci		return "IONIC_RC_ERROR";
658c2ecf20Sopenharmony_ci	case IONIC_RC_ERDMA:
668c2ecf20Sopenharmony_ci		return "IONIC_RC_ERDMA";
678c2ecf20Sopenharmony_ci	case IONIC_RC_EBAD_FW:
688c2ecf20Sopenharmony_ci		return "IONIC_RC_EBAD_FW";
698c2ecf20Sopenharmony_ci	default:
708c2ecf20Sopenharmony_ci		return "IONIC_RC_UNKNOWN";
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic int ionic_error_to_errno(enum ionic_status_code code)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	switch (code) {
778c2ecf20Sopenharmony_ci	case IONIC_RC_SUCCESS:
788c2ecf20Sopenharmony_ci		return 0;
798c2ecf20Sopenharmony_ci	case IONIC_RC_EVERSION:
808c2ecf20Sopenharmony_ci	case IONIC_RC_EQTYPE:
818c2ecf20Sopenharmony_ci	case IONIC_RC_EQID:
828c2ecf20Sopenharmony_ci	case IONIC_RC_EINVAL:
838c2ecf20Sopenharmony_ci	case IONIC_RC_ENOSUPP:
848c2ecf20Sopenharmony_ci		return -EINVAL;
858c2ecf20Sopenharmony_ci	case IONIC_RC_EPERM:
868c2ecf20Sopenharmony_ci		return -EPERM;
878c2ecf20Sopenharmony_ci	case IONIC_RC_ENOENT:
888c2ecf20Sopenharmony_ci		return -ENOENT;
898c2ecf20Sopenharmony_ci	case IONIC_RC_EAGAIN:
908c2ecf20Sopenharmony_ci		return -EAGAIN;
918c2ecf20Sopenharmony_ci	case IONIC_RC_ENOMEM:
928c2ecf20Sopenharmony_ci		return -ENOMEM;
938c2ecf20Sopenharmony_ci	case IONIC_RC_EFAULT:
948c2ecf20Sopenharmony_ci		return -EFAULT;
958c2ecf20Sopenharmony_ci	case IONIC_RC_EBUSY:
968c2ecf20Sopenharmony_ci		return -EBUSY;
978c2ecf20Sopenharmony_ci	case IONIC_RC_EEXIST:
988c2ecf20Sopenharmony_ci		return -EEXIST;
998c2ecf20Sopenharmony_ci	case IONIC_RC_ENOSPC:
1008c2ecf20Sopenharmony_ci		return -ENOSPC;
1018c2ecf20Sopenharmony_ci	case IONIC_RC_ERANGE:
1028c2ecf20Sopenharmony_ci		return -ERANGE;
1038c2ecf20Sopenharmony_ci	case IONIC_RC_BAD_ADDR:
1048c2ecf20Sopenharmony_ci		return -EFAULT;
1058c2ecf20Sopenharmony_ci	case IONIC_RC_EOPCODE:
1068c2ecf20Sopenharmony_ci	case IONIC_RC_EINTR:
1078c2ecf20Sopenharmony_ci	case IONIC_RC_DEV_CMD:
1088c2ecf20Sopenharmony_ci	case IONIC_RC_ERROR:
1098c2ecf20Sopenharmony_ci	case IONIC_RC_ERDMA:
1108c2ecf20Sopenharmony_ci	case IONIC_RC_EIO:
1118c2ecf20Sopenharmony_ci	default:
1128c2ecf20Sopenharmony_ci		return -EIO;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic const char *ionic_opcode_to_str(enum ionic_cmd_opcode opcode)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	switch (opcode) {
1198c2ecf20Sopenharmony_ci	case IONIC_CMD_NOP:
1208c2ecf20Sopenharmony_ci		return "IONIC_CMD_NOP";
1218c2ecf20Sopenharmony_ci	case IONIC_CMD_INIT:
1228c2ecf20Sopenharmony_ci		return "IONIC_CMD_INIT";
1238c2ecf20Sopenharmony_ci	case IONIC_CMD_RESET:
1248c2ecf20Sopenharmony_ci		return "IONIC_CMD_RESET";
1258c2ecf20Sopenharmony_ci	case IONIC_CMD_IDENTIFY:
1268c2ecf20Sopenharmony_ci		return "IONIC_CMD_IDENTIFY";
1278c2ecf20Sopenharmony_ci	case IONIC_CMD_GETATTR:
1288c2ecf20Sopenharmony_ci		return "IONIC_CMD_GETATTR";
1298c2ecf20Sopenharmony_ci	case IONIC_CMD_SETATTR:
1308c2ecf20Sopenharmony_ci		return "IONIC_CMD_SETATTR";
1318c2ecf20Sopenharmony_ci	case IONIC_CMD_PORT_IDENTIFY:
1328c2ecf20Sopenharmony_ci		return "IONIC_CMD_PORT_IDENTIFY";
1338c2ecf20Sopenharmony_ci	case IONIC_CMD_PORT_INIT:
1348c2ecf20Sopenharmony_ci		return "IONIC_CMD_PORT_INIT";
1358c2ecf20Sopenharmony_ci	case IONIC_CMD_PORT_RESET:
1368c2ecf20Sopenharmony_ci		return "IONIC_CMD_PORT_RESET";
1378c2ecf20Sopenharmony_ci	case IONIC_CMD_PORT_GETATTR:
1388c2ecf20Sopenharmony_ci		return "IONIC_CMD_PORT_GETATTR";
1398c2ecf20Sopenharmony_ci	case IONIC_CMD_PORT_SETATTR:
1408c2ecf20Sopenharmony_ci		return "IONIC_CMD_PORT_SETATTR";
1418c2ecf20Sopenharmony_ci	case IONIC_CMD_LIF_INIT:
1428c2ecf20Sopenharmony_ci		return "IONIC_CMD_LIF_INIT";
1438c2ecf20Sopenharmony_ci	case IONIC_CMD_LIF_RESET:
1448c2ecf20Sopenharmony_ci		return "IONIC_CMD_LIF_RESET";
1458c2ecf20Sopenharmony_ci	case IONIC_CMD_LIF_IDENTIFY:
1468c2ecf20Sopenharmony_ci		return "IONIC_CMD_LIF_IDENTIFY";
1478c2ecf20Sopenharmony_ci	case IONIC_CMD_LIF_SETATTR:
1488c2ecf20Sopenharmony_ci		return "IONIC_CMD_LIF_SETATTR";
1498c2ecf20Sopenharmony_ci	case IONIC_CMD_LIF_GETATTR:
1508c2ecf20Sopenharmony_ci		return "IONIC_CMD_LIF_GETATTR";
1518c2ecf20Sopenharmony_ci	case IONIC_CMD_RX_MODE_SET:
1528c2ecf20Sopenharmony_ci		return "IONIC_CMD_RX_MODE_SET";
1538c2ecf20Sopenharmony_ci	case IONIC_CMD_RX_FILTER_ADD:
1548c2ecf20Sopenharmony_ci		return "IONIC_CMD_RX_FILTER_ADD";
1558c2ecf20Sopenharmony_ci	case IONIC_CMD_RX_FILTER_DEL:
1568c2ecf20Sopenharmony_ci		return "IONIC_CMD_RX_FILTER_DEL";
1578c2ecf20Sopenharmony_ci	case IONIC_CMD_Q_IDENTIFY:
1588c2ecf20Sopenharmony_ci		return "IONIC_CMD_Q_IDENTIFY";
1598c2ecf20Sopenharmony_ci	case IONIC_CMD_Q_INIT:
1608c2ecf20Sopenharmony_ci		return "IONIC_CMD_Q_INIT";
1618c2ecf20Sopenharmony_ci	case IONIC_CMD_Q_CONTROL:
1628c2ecf20Sopenharmony_ci		return "IONIC_CMD_Q_CONTROL";
1638c2ecf20Sopenharmony_ci	case IONIC_CMD_RDMA_RESET_LIF:
1648c2ecf20Sopenharmony_ci		return "IONIC_CMD_RDMA_RESET_LIF";
1658c2ecf20Sopenharmony_ci	case IONIC_CMD_RDMA_CREATE_EQ:
1668c2ecf20Sopenharmony_ci		return "IONIC_CMD_RDMA_CREATE_EQ";
1678c2ecf20Sopenharmony_ci	case IONIC_CMD_RDMA_CREATE_CQ:
1688c2ecf20Sopenharmony_ci		return "IONIC_CMD_RDMA_CREATE_CQ";
1698c2ecf20Sopenharmony_ci	case IONIC_CMD_RDMA_CREATE_ADMINQ:
1708c2ecf20Sopenharmony_ci		return "IONIC_CMD_RDMA_CREATE_ADMINQ";
1718c2ecf20Sopenharmony_ci	case IONIC_CMD_FW_DOWNLOAD:
1728c2ecf20Sopenharmony_ci		return "IONIC_CMD_FW_DOWNLOAD";
1738c2ecf20Sopenharmony_ci	case IONIC_CMD_FW_CONTROL:
1748c2ecf20Sopenharmony_ci		return "IONIC_CMD_FW_CONTROL";
1758c2ecf20Sopenharmony_ci	case IONIC_CMD_FW_DOWNLOAD_V1:
1768c2ecf20Sopenharmony_ci		return "IONIC_CMD_FW_DOWNLOAD_V1";
1778c2ecf20Sopenharmony_ci	case IONIC_CMD_FW_CONTROL_V1:
1788c2ecf20Sopenharmony_ci		return "IONIC_CMD_FW_CONTROL_V1";
1798c2ecf20Sopenharmony_ci	case IONIC_CMD_VF_GETATTR:
1808c2ecf20Sopenharmony_ci		return "IONIC_CMD_VF_GETATTR";
1818c2ecf20Sopenharmony_ci	case IONIC_CMD_VF_SETATTR:
1828c2ecf20Sopenharmony_ci		return "IONIC_CMD_VF_SETATTR";
1838c2ecf20Sopenharmony_ci	default:
1848c2ecf20Sopenharmony_ci		return "DEVCMD_UNKNOWN";
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic void ionic_adminq_flush(struct ionic_lif *lif)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	struct ionic_queue *q = &lif->adminqcq->q;
1918c2ecf20Sopenharmony_ci	struct ionic_desc_info *desc_info;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	spin_lock(&lif->adminq_lock);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	while (q->tail_idx != q->head_idx) {
1968c2ecf20Sopenharmony_ci		desc_info = &q->info[q->tail_idx];
1978c2ecf20Sopenharmony_ci		memset(desc_info->desc, 0, sizeof(union ionic_adminq_cmd));
1988c2ecf20Sopenharmony_ci		desc_info->cb = NULL;
1998c2ecf20Sopenharmony_ci		desc_info->cb_arg = NULL;
2008c2ecf20Sopenharmony_ci		q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1);
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci	spin_unlock(&lif->adminq_lock);
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic int ionic_adminq_check_err(struct ionic_lif *lif,
2068c2ecf20Sopenharmony_ci				  struct ionic_admin_ctx *ctx,
2078c2ecf20Sopenharmony_ci				  bool timeout)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	struct net_device *netdev = lif->netdev;
2108c2ecf20Sopenharmony_ci	const char *opcode_str;
2118c2ecf20Sopenharmony_ci	const char *status_str;
2128c2ecf20Sopenharmony_ci	int err = 0;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if (ctx->comp.comp.status || timeout) {
2158c2ecf20Sopenharmony_ci		opcode_str = ionic_opcode_to_str(ctx->cmd.cmd.opcode);
2168c2ecf20Sopenharmony_ci		status_str = ionic_error_to_str(ctx->comp.comp.status);
2178c2ecf20Sopenharmony_ci		err = timeout ? -ETIMEDOUT :
2188c2ecf20Sopenharmony_ci				ionic_error_to_errno(ctx->comp.comp.status);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci		netdev_err(netdev, "%s (%d) failed: %s (%d)\n",
2218c2ecf20Sopenharmony_ci			   opcode_str, ctx->cmd.cmd.opcode,
2228c2ecf20Sopenharmony_ci			   timeout ? "TIMEOUT" : status_str, err);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci		if (timeout)
2258c2ecf20Sopenharmony_ci			ionic_adminq_flush(lif);
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	return err;
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic void ionic_adminq_cb(struct ionic_queue *q,
2328c2ecf20Sopenharmony_ci			    struct ionic_desc_info *desc_info,
2338c2ecf20Sopenharmony_ci			    struct ionic_cq_info *cq_info, void *cb_arg)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	struct ionic_admin_ctx *ctx = cb_arg;
2368c2ecf20Sopenharmony_ci	struct ionic_admin_comp *comp;
2378c2ecf20Sopenharmony_ci	struct device *dev;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	if (!ctx)
2408c2ecf20Sopenharmony_ci		return;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	comp = cq_info->cq_desc;
2438c2ecf20Sopenharmony_ci	dev = &q->lif->netdev->dev;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	memcpy(&ctx->comp, comp, sizeof(*comp));
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	dev_dbg(dev, "comp admin queue command:\n");
2488c2ecf20Sopenharmony_ci	dynamic_hex_dump("comp ", DUMP_PREFIX_OFFSET, 16, 1,
2498c2ecf20Sopenharmony_ci			 &ctx->comp, sizeof(ctx->comp), true);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	complete_all(&ctx->work);
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic int ionic_adminq_post(struct ionic_lif *lif, struct ionic_admin_ctx *ctx)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	struct ionic_desc_info *desc_info;
2578c2ecf20Sopenharmony_ci	struct ionic_queue *q;
2588c2ecf20Sopenharmony_ci	int err = 0;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	if (!lif->adminqcq)
2618c2ecf20Sopenharmony_ci		return -EIO;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	q = &lif->adminqcq->q;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	spin_lock(&lif->adminq_lock);
2668c2ecf20Sopenharmony_ci	if (!ionic_q_has_space(q, 1)) {
2678c2ecf20Sopenharmony_ci		err = -ENOSPC;
2688c2ecf20Sopenharmony_ci		goto err_out;
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	err = ionic_heartbeat_check(lif->ionic);
2728c2ecf20Sopenharmony_ci	if (err)
2738c2ecf20Sopenharmony_ci		goto err_out;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	desc_info = &q->info[q->head_idx];
2768c2ecf20Sopenharmony_ci	memcpy(desc_info->desc, &ctx->cmd, sizeof(ctx->cmd));
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	dev_dbg(&lif->netdev->dev, "post admin queue command:\n");
2798c2ecf20Sopenharmony_ci	dynamic_hex_dump("cmd ", DUMP_PREFIX_OFFSET, 16, 1,
2808c2ecf20Sopenharmony_ci			 &ctx->cmd, sizeof(ctx->cmd), true);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	ionic_q_post(q, true, ionic_adminq_cb, ctx);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cierr_out:
2858c2ecf20Sopenharmony_ci	spin_unlock(&lif->adminq_lock);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	return err;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ciint ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	struct net_device *netdev = lif->netdev;
2938c2ecf20Sopenharmony_ci	unsigned long remaining;
2948c2ecf20Sopenharmony_ci	const char *name;
2958c2ecf20Sopenharmony_ci	int err;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	err = ionic_adminq_post(lif, ctx);
2988c2ecf20Sopenharmony_ci	if (err) {
2998c2ecf20Sopenharmony_ci		if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state)) {
3008c2ecf20Sopenharmony_ci			name = ionic_opcode_to_str(ctx->cmd.cmd.opcode);
3018c2ecf20Sopenharmony_ci			netdev_err(netdev, "Posting of %s (%d) failed: %d\n",
3028c2ecf20Sopenharmony_ci				   name, ctx->cmd.cmd.opcode, err);
3038c2ecf20Sopenharmony_ci		}
3048c2ecf20Sopenharmony_ci		return err;
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	remaining = wait_for_completion_timeout(&ctx->work,
3088c2ecf20Sopenharmony_ci						HZ * (ulong)DEVCMD_TIMEOUT);
3098c2ecf20Sopenharmony_ci	return ionic_adminq_check_err(lif, ctx, (remaining == 0));
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic void ionic_dev_cmd_clean(struct ionic *ionic)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	struct ionic_dev *idev = &ionic->idev;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	iowrite32(0, &idev->dev_cmd_regs->doorbell);
3178c2ecf20Sopenharmony_ci	memset_io(&idev->dev_cmd_regs->cmd, 0, sizeof(idev->dev_cmd_regs->cmd));
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ciint ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	struct ionic_dev *idev = &ionic->idev;
3238c2ecf20Sopenharmony_ci	unsigned long start_time;
3248c2ecf20Sopenharmony_ci	unsigned long max_wait;
3258c2ecf20Sopenharmony_ci	unsigned long duration;
3268c2ecf20Sopenharmony_ci	int opcode;
3278c2ecf20Sopenharmony_ci	int hb = 0;
3288c2ecf20Sopenharmony_ci	int done;
3298c2ecf20Sopenharmony_ci	int err;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	/* Wait for dev cmd to complete, retrying if we get EAGAIN,
3328c2ecf20Sopenharmony_ci	 * but don't wait any longer than max_seconds.
3338c2ecf20Sopenharmony_ci	 */
3348c2ecf20Sopenharmony_ci	max_wait = jiffies + (max_seconds * HZ);
3358c2ecf20Sopenharmony_citry_again:
3368c2ecf20Sopenharmony_ci	opcode = idev->opcode;
3378c2ecf20Sopenharmony_ci	start_time = jiffies;
3388c2ecf20Sopenharmony_ci	do {
3398c2ecf20Sopenharmony_ci		done = ionic_dev_cmd_done(idev);
3408c2ecf20Sopenharmony_ci		if (done)
3418c2ecf20Sopenharmony_ci			break;
3428c2ecf20Sopenharmony_ci		usleep_range(100, 200);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci		/* Don't check the heartbeat on FW_CONTROL commands as they are
3458c2ecf20Sopenharmony_ci		 * notorious for interrupting the firmware's heartbeat update.
3468c2ecf20Sopenharmony_ci		 */
3478c2ecf20Sopenharmony_ci		if (opcode != IONIC_CMD_FW_CONTROL)
3488c2ecf20Sopenharmony_ci			hb = ionic_heartbeat_check(ionic);
3498c2ecf20Sopenharmony_ci	} while (!done && !hb && time_before(jiffies, max_wait));
3508c2ecf20Sopenharmony_ci	duration = jiffies - start_time;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	dev_dbg(ionic->dev, "DEVCMD %s (%d) done=%d took %ld secs (%ld jiffies)\n",
3538c2ecf20Sopenharmony_ci		ionic_opcode_to_str(opcode), opcode,
3548c2ecf20Sopenharmony_ci		done, duration / HZ, duration);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	if (!done && hb) {
3578c2ecf20Sopenharmony_ci		/* It is possible (but unlikely) that FW was busy and missed a
3588c2ecf20Sopenharmony_ci		 * heartbeat check but is still alive and will process this
3598c2ecf20Sopenharmony_ci		 * request, so don't clean the dev_cmd in this case.
3608c2ecf20Sopenharmony_ci		 */
3618c2ecf20Sopenharmony_ci		dev_warn(ionic->dev, "DEVCMD %s (%d) failed - FW halted\n",
3628c2ecf20Sopenharmony_ci			 ionic_opcode_to_str(opcode), opcode);
3638c2ecf20Sopenharmony_ci		return -ENXIO;
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	if (!done && !time_before(jiffies, max_wait)) {
3678c2ecf20Sopenharmony_ci		ionic_dev_cmd_clean(ionic);
3688c2ecf20Sopenharmony_ci		dev_warn(ionic->dev, "DEVCMD %s (%d) timeout after %ld secs\n",
3698c2ecf20Sopenharmony_ci			 ionic_opcode_to_str(opcode), opcode, max_seconds);
3708c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	err = ionic_dev_cmd_status(&ionic->idev);
3748c2ecf20Sopenharmony_ci	if (err) {
3758c2ecf20Sopenharmony_ci		if (err == IONIC_RC_EAGAIN &&
3768c2ecf20Sopenharmony_ci		    time_before(jiffies, (max_wait - HZ))) {
3778c2ecf20Sopenharmony_ci			dev_dbg(ionic->dev, "DEV_CMD %s (%d), %s (%d) retrying...\n",
3788c2ecf20Sopenharmony_ci				ionic_opcode_to_str(opcode), opcode,
3798c2ecf20Sopenharmony_ci				ionic_error_to_str(err), err);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci			iowrite32(0, &idev->dev_cmd_regs->done);
3828c2ecf20Sopenharmony_ci			msleep(1000);
3838c2ecf20Sopenharmony_ci			iowrite32(1, &idev->dev_cmd_regs->doorbell);
3848c2ecf20Sopenharmony_ci			goto try_again;
3858c2ecf20Sopenharmony_ci		}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci		if (!(opcode == IONIC_CMD_FW_CONTROL && err == IONIC_RC_EAGAIN))
3888c2ecf20Sopenharmony_ci			dev_err(ionic->dev, "DEV_CMD %s (%d) error, %s (%d) failed\n",
3898c2ecf20Sopenharmony_ci				ionic_opcode_to_str(opcode), opcode,
3908c2ecf20Sopenharmony_ci				ionic_error_to_str(err), err);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci		return ionic_error_to_errno(err);
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	ionic_dev_cmd_clean(ionic);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	return 0;
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ciint ionic_setup(struct ionic *ionic)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	int err;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	err = ionic_dev_setup(ionic);
4058c2ecf20Sopenharmony_ci	if (err)
4068c2ecf20Sopenharmony_ci		return err;
4078c2ecf20Sopenharmony_ci	ionic_reset(ionic);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	return 0;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ciint ionic_identify(struct ionic *ionic)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	struct ionic_identity *ident = &ionic->ident;
4158c2ecf20Sopenharmony_ci	struct ionic_dev *idev = &ionic->idev;
4168c2ecf20Sopenharmony_ci	size_t sz;
4178c2ecf20Sopenharmony_ci	int err;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	memset(ident, 0, sizeof(*ident));
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	ident->drv.os_type = cpu_to_le32(IONIC_OS_TYPE_LINUX);
4228c2ecf20Sopenharmony_ci	strncpy(ident->drv.driver_ver_str, UTS_RELEASE,
4238c2ecf20Sopenharmony_ci		sizeof(ident->drv.driver_ver_str) - 1);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	mutex_lock(&ionic->dev_cmd_lock);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	sz = min(sizeof(ident->drv), sizeof(idev->dev_cmd_regs->data));
4288c2ecf20Sopenharmony_ci	memcpy_toio(&idev->dev_cmd_regs->data, &ident->drv, sz);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	ionic_dev_cmd_identify(idev, IONIC_IDENTITY_VERSION_1);
4318c2ecf20Sopenharmony_ci	err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
4328c2ecf20Sopenharmony_ci	if (!err) {
4338c2ecf20Sopenharmony_ci		sz = min(sizeof(ident->dev), sizeof(idev->dev_cmd_regs->data));
4348c2ecf20Sopenharmony_ci		memcpy_fromio(&ident->dev, &idev->dev_cmd_regs->data, sz);
4358c2ecf20Sopenharmony_ci	}
4368c2ecf20Sopenharmony_ci	mutex_unlock(&ionic->dev_cmd_lock);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	if (err) {
4398c2ecf20Sopenharmony_ci		dev_err(ionic->dev, "Cannot identify ionic: %dn", err);
4408c2ecf20Sopenharmony_ci		goto err_out;
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	err = ionic_lif_identify(ionic, IONIC_LIF_TYPE_CLASSIC,
4448c2ecf20Sopenharmony_ci				 &ionic->ident.lif);
4458c2ecf20Sopenharmony_ci	if (err) {
4468c2ecf20Sopenharmony_ci		dev_err(ionic->dev, "Cannot identify LIFs: %d\n", err);
4478c2ecf20Sopenharmony_ci		goto err_out;
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	return 0;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_cierr_out:
4538c2ecf20Sopenharmony_ci	return err;
4548c2ecf20Sopenharmony_ci}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ciint ionic_init(struct ionic *ionic)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	struct ionic_dev *idev = &ionic->idev;
4598c2ecf20Sopenharmony_ci	int err;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	mutex_lock(&ionic->dev_cmd_lock);
4628c2ecf20Sopenharmony_ci	ionic_dev_cmd_init(idev);
4638c2ecf20Sopenharmony_ci	err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
4648c2ecf20Sopenharmony_ci	mutex_unlock(&ionic->dev_cmd_lock);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	return err;
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ciint ionic_reset(struct ionic *ionic)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	struct ionic_dev *idev = &ionic->idev;
4728c2ecf20Sopenharmony_ci	int err;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	mutex_lock(&ionic->dev_cmd_lock);
4758c2ecf20Sopenharmony_ci	ionic_dev_cmd_reset(idev);
4768c2ecf20Sopenharmony_ci	err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
4778c2ecf20Sopenharmony_ci	mutex_unlock(&ionic->dev_cmd_lock);
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	return err;
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ciint ionic_port_identify(struct ionic *ionic)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	struct ionic_identity *ident = &ionic->ident;
4858c2ecf20Sopenharmony_ci	struct ionic_dev *idev = &ionic->idev;
4868c2ecf20Sopenharmony_ci	size_t sz;
4878c2ecf20Sopenharmony_ci	int err;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	mutex_lock(&ionic->dev_cmd_lock);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	ionic_dev_cmd_port_identify(idev);
4928c2ecf20Sopenharmony_ci	err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
4938c2ecf20Sopenharmony_ci	if (!err) {
4948c2ecf20Sopenharmony_ci		sz = min(sizeof(ident->port), sizeof(idev->dev_cmd_regs->data));
4958c2ecf20Sopenharmony_ci		memcpy_fromio(&ident->port, &idev->dev_cmd_regs->data, sz);
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	mutex_unlock(&ionic->dev_cmd_lock);
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	return err;
5018c2ecf20Sopenharmony_ci}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ciint ionic_port_init(struct ionic *ionic)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	struct ionic_identity *ident = &ionic->ident;
5068c2ecf20Sopenharmony_ci	struct ionic_dev *idev = &ionic->idev;
5078c2ecf20Sopenharmony_ci	size_t sz;
5088c2ecf20Sopenharmony_ci	int err;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	if (!idev->port_info) {
5118c2ecf20Sopenharmony_ci		idev->port_info_sz = ALIGN(sizeof(*idev->port_info), PAGE_SIZE);
5128c2ecf20Sopenharmony_ci		idev->port_info = dma_alloc_coherent(ionic->dev,
5138c2ecf20Sopenharmony_ci						     idev->port_info_sz,
5148c2ecf20Sopenharmony_ci						     &idev->port_info_pa,
5158c2ecf20Sopenharmony_ci						     GFP_KERNEL);
5168c2ecf20Sopenharmony_ci		if (!idev->port_info) {
5178c2ecf20Sopenharmony_ci			dev_err(ionic->dev, "Failed to allocate port info\n");
5188c2ecf20Sopenharmony_ci			return -ENOMEM;
5198c2ecf20Sopenharmony_ci		}
5208c2ecf20Sopenharmony_ci	}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	sz = min(sizeof(ident->port.config), sizeof(idev->dev_cmd_regs->data));
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	mutex_lock(&ionic->dev_cmd_lock);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	memcpy_toio(&idev->dev_cmd_regs->data, &ident->port.config, sz);
5278c2ecf20Sopenharmony_ci	ionic_dev_cmd_port_init(idev);
5288c2ecf20Sopenharmony_ci	err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	ionic_dev_cmd_port_state(&ionic->idev, IONIC_PORT_ADMIN_STATE_UP);
5318c2ecf20Sopenharmony_ci	(void)ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	mutex_unlock(&ionic->dev_cmd_lock);
5348c2ecf20Sopenharmony_ci	if (err) {
5358c2ecf20Sopenharmony_ci		dev_err(ionic->dev, "Failed to init port\n");
5368c2ecf20Sopenharmony_ci		dma_free_coherent(ionic->dev, idev->port_info_sz,
5378c2ecf20Sopenharmony_ci				  idev->port_info, idev->port_info_pa);
5388c2ecf20Sopenharmony_ci		idev->port_info = NULL;
5398c2ecf20Sopenharmony_ci		idev->port_info_pa = 0;
5408c2ecf20Sopenharmony_ci	}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	return err;
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ciint ionic_port_reset(struct ionic *ionic)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	struct ionic_dev *idev = &ionic->idev;
5488c2ecf20Sopenharmony_ci	int err;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	if (!idev->port_info)
5518c2ecf20Sopenharmony_ci		return 0;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	mutex_lock(&ionic->dev_cmd_lock);
5548c2ecf20Sopenharmony_ci	ionic_dev_cmd_port_reset(idev);
5558c2ecf20Sopenharmony_ci	err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
5568c2ecf20Sopenharmony_ci	mutex_unlock(&ionic->dev_cmd_lock);
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	dma_free_coherent(ionic->dev, idev->port_info_sz,
5598c2ecf20Sopenharmony_ci			  idev->port_info, idev->port_info_pa);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	idev->port_info = NULL;
5628c2ecf20Sopenharmony_ci	idev->port_info_pa = 0;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	if (err)
5658c2ecf20Sopenharmony_ci		dev_err(ionic->dev, "Failed to reset port\n");
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	return err;
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_cistatic int __init ionic_init_module(void)
5718c2ecf20Sopenharmony_ci{
5728c2ecf20Sopenharmony_ci	int ret;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	ionic_debugfs_create();
5758c2ecf20Sopenharmony_ci	ret = ionic_bus_register_driver();
5768c2ecf20Sopenharmony_ci	if (ret)
5778c2ecf20Sopenharmony_ci		ionic_debugfs_destroy();
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	return ret;
5808c2ecf20Sopenharmony_ci}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_cistatic void __exit ionic_cleanup_module(void)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci	ionic_bus_unregister_driver();
5858c2ecf20Sopenharmony_ci	ionic_debugfs_destroy();
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	pr_info("%s removed\n", IONIC_DRV_NAME);
5888c2ecf20Sopenharmony_ci}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_cimodule_init(ionic_init_module);
5918c2ecf20Sopenharmony_cimodule_exit(ionic_cleanup_module);
592