162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2008-2010 Cisco Systems, Inc.  All rights reserved.
462306a36Sopenharmony_ci * Copyright 2007 Nuova Systems, Inc.  All rights reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/kernel.h>
862306a36Sopenharmony_ci#include <linux/errno.h>
962306a36Sopenharmony_ci#include <linux/types.h>
1062306a36Sopenharmony_ci#include <linux/pci.h>
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/if_ether.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "vnic_resource.h"
1562306a36Sopenharmony_ci#include "vnic_devcmd.h"
1662306a36Sopenharmony_ci#include "vnic_dev.h"
1762306a36Sopenharmony_ci#include "vnic_wq.h"
1862306a36Sopenharmony_ci#include "vnic_stats.h"
1962306a36Sopenharmony_ci#include "enic.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define VNIC_MAX_RES_HDR_SIZE \
2262306a36Sopenharmony_ci	(sizeof(struct vnic_resource_header) + \
2362306a36Sopenharmony_ci	sizeof(struct vnic_resource) * RES_TYPE_MAX)
2462306a36Sopenharmony_ci#define VNIC_RES_STRIDE	128
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_civoid *vnic_dev_priv(struct vnic_dev *vdev)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	return vdev->priv;
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic int vnic_dev_discover_res(struct vnic_dev *vdev,
3262306a36Sopenharmony_ci	struct vnic_dev_bar *bar, unsigned int num_bars)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	struct vnic_resource_header __iomem *rh;
3562306a36Sopenharmony_ci	struct mgmt_barmap_hdr __iomem *mrh;
3662306a36Sopenharmony_ci	struct vnic_resource __iomem *r;
3762306a36Sopenharmony_ci	u8 type;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	if (num_bars == 0)
4062306a36Sopenharmony_ci		return -EINVAL;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	if (bar->len < VNIC_MAX_RES_HDR_SIZE) {
4362306a36Sopenharmony_ci		vdev_err(vdev, "vNIC BAR0 res hdr length error\n");
4462306a36Sopenharmony_ci		return -EINVAL;
4562306a36Sopenharmony_ci	}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	rh  = bar->vaddr;
4862306a36Sopenharmony_ci	mrh = bar->vaddr;
4962306a36Sopenharmony_ci	if (!rh) {
5062306a36Sopenharmony_ci		vdev_err(vdev, "vNIC BAR0 res hdr not mem-mapped\n");
5162306a36Sopenharmony_ci		return -EINVAL;
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	/* Check for mgmt vnic in addition to normal vnic */
5562306a36Sopenharmony_ci	if ((ioread32(&rh->magic) != VNIC_RES_MAGIC) ||
5662306a36Sopenharmony_ci		(ioread32(&rh->version) != VNIC_RES_VERSION)) {
5762306a36Sopenharmony_ci		if ((ioread32(&mrh->magic) != MGMTVNIC_MAGIC) ||
5862306a36Sopenharmony_ci			(ioread32(&mrh->version) != MGMTVNIC_VERSION)) {
5962306a36Sopenharmony_ci			vdev_err(vdev, "vNIC BAR0 res magic/version error exp (%lx/%lx) or (%lx/%lx), curr (%x/%x)\n",
6062306a36Sopenharmony_ci				 VNIC_RES_MAGIC, VNIC_RES_VERSION,
6162306a36Sopenharmony_ci				 MGMTVNIC_MAGIC, MGMTVNIC_VERSION,
6262306a36Sopenharmony_ci				 ioread32(&rh->magic), ioread32(&rh->version));
6362306a36Sopenharmony_ci			return -EINVAL;
6462306a36Sopenharmony_ci		}
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	if (ioread32(&mrh->magic) == MGMTVNIC_MAGIC)
6862306a36Sopenharmony_ci		r = (struct vnic_resource __iomem *)(mrh + 1);
6962306a36Sopenharmony_ci	else
7062306a36Sopenharmony_ci		r = (struct vnic_resource __iomem *)(rh + 1);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	while ((type = ioread8(&r->type)) != RES_TYPE_EOL) {
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci		u8 bar_num = ioread8(&r->bar);
7662306a36Sopenharmony_ci		u32 bar_offset = ioread32(&r->bar_offset);
7762306a36Sopenharmony_ci		u32 count = ioread32(&r->count);
7862306a36Sopenharmony_ci		u32 len;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		r++;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci		if (bar_num >= num_bars)
8362306a36Sopenharmony_ci			continue;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci		if (!bar[bar_num].len || !bar[bar_num].vaddr)
8662306a36Sopenharmony_ci			continue;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci		switch (type) {
8962306a36Sopenharmony_ci		case RES_TYPE_WQ:
9062306a36Sopenharmony_ci		case RES_TYPE_RQ:
9162306a36Sopenharmony_ci		case RES_TYPE_CQ:
9262306a36Sopenharmony_ci		case RES_TYPE_INTR_CTRL:
9362306a36Sopenharmony_ci			/* each count is stride bytes long */
9462306a36Sopenharmony_ci			len = count * VNIC_RES_STRIDE;
9562306a36Sopenharmony_ci			if (len + bar_offset > bar[bar_num].len) {
9662306a36Sopenharmony_ci				vdev_err(vdev, "vNIC BAR0 resource %d out-of-bounds, offset 0x%x + size 0x%x > bar len 0x%lx\n",
9762306a36Sopenharmony_ci					 type, bar_offset, len,
9862306a36Sopenharmony_ci					 bar[bar_num].len);
9962306a36Sopenharmony_ci				return -EINVAL;
10062306a36Sopenharmony_ci			}
10162306a36Sopenharmony_ci			break;
10262306a36Sopenharmony_ci		case RES_TYPE_INTR_PBA_LEGACY:
10362306a36Sopenharmony_ci		case RES_TYPE_DEVCMD:
10462306a36Sopenharmony_ci		case RES_TYPE_DEVCMD2:
10562306a36Sopenharmony_ci			len = count;
10662306a36Sopenharmony_ci			break;
10762306a36Sopenharmony_ci		default:
10862306a36Sopenharmony_ci			continue;
10962306a36Sopenharmony_ci		}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci		vdev->res[type].count = count;
11262306a36Sopenharmony_ci		vdev->res[type].vaddr = (char __iomem *)bar[bar_num].vaddr +
11362306a36Sopenharmony_ci			bar_offset;
11462306a36Sopenharmony_ci		vdev->res[type].bus_addr = bar[bar_num].bus_addr + bar_offset;
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	return 0;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ciunsigned int vnic_dev_get_res_count(struct vnic_dev *vdev,
12162306a36Sopenharmony_ci	enum vnic_res_type type)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	return vdev->res[type].count;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ciEXPORT_SYMBOL(vnic_dev_get_res_count);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_civoid __iomem *vnic_dev_get_res(struct vnic_dev *vdev, enum vnic_res_type type,
12862306a36Sopenharmony_ci	unsigned int index)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	if (!vdev->res[type].vaddr)
13162306a36Sopenharmony_ci		return NULL;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	switch (type) {
13462306a36Sopenharmony_ci	case RES_TYPE_WQ:
13562306a36Sopenharmony_ci	case RES_TYPE_RQ:
13662306a36Sopenharmony_ci	case RES_TYPE_CQ:
13762306a36Sopenharmony_ci	case RES_TYPE_INTR_CTRL:
13862306a36Sopenharmony_ci		return (char __iomem *)vdev->res[type].vaddr +
13962306a36Sopenharmony_ci			index * VNIC_RES_STRIDE;
14062306a36Sopenharmony_ci	default:
14162306a36Sopenharmony_ci		return (char __iomem *)vdev->res[type].vaddr;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ciEXPORT_SYMBOL(vnic_dev_get_res);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic unsigned int vnic_dev_desc_ring_size(struct vnic_dev_ring *ring,
14762306a36Sopenharmony_ci	unsigned int desc_count, unsigned int desc_size)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	/* The base address of the desc rings must be 512 byte aligned.
15062306a36Sopenharmony_ci	 * Descriptor count is aligned to groups of 32 descriptors.  A
15162306a36Sopenharmony_ci	 * count of 0 means the maximum 4096 descriptors.  Descriptor
15262306a36Sopenharmony_ci	 * size is aligned to 16 bytes.
15362306a36Sopenharmony_ci	 */
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	unsigned int count_align = 32;
15662306a36Sopenharmony_ci	unsigned int desc_align = 16;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	ring->base_align = 512;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	if (desc_count == 0)
16162306a36Sopenharmony_ci		desc_count = 4096;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	ring->desc_count = ALIGN(desc_count, count_align);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	ring->desc_size = ALIGN(desc_size, desc_align);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	ring->size = ring->desc_count * ring->desc_size;
16862306a36Sopenharmony_ci	ring->size_unaligned = ring->size + ring->base_align;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	return ring->size_unaligned;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_civoid vnic_dev_clear_desc_ring(struct vnic_dev_ring *ring)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	memset(ring->descs, 0, ring->size);
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ciint vnic_dev_alloc_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring,
17962306a36Sopenharmony_ci	unsigned int desc_count, unsigned int desc_size)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	vnic_dev_desc_ring_size(ring, desc_count, desc_size);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	ring->descs_unaligned = dma_alloc_coherent(&vdev->pdev->dev,
18462306a36Sopenharmony_ci						   ring->size_unaligned,
18562306a36Sopenharmony_ci						   &ring->base_addr_unaligned,
18662306a36Sopenharmony_ci						   GFP_KERNEL);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (!ring->descs_unaligned) {
18962306a36Sopenharmony_ci		vdev_err(vdev, "Failed to allocate ring (size=%d), aborting\n",
19062306a36Sopenharmony_ci			 (int)ring->size);
19162306a36Sopenharmony_ci		return -ENOMEM;
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	ring->base_addr = ALIGN(ring->base_addr_unaligned,
19562306a36Sopenharmony_ci		ring->base_align);
19662306a36Sopenharmony_ci	ring->descs = (u8 *)ring->descs_unaligned +
19762306a36Sopenharmony_ci		(ring->base_addr - ring->base_addr_unaligned);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	vnic_dev_clear_desc_ring(ring);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	ring->desc_avail = ring->desc_count - 1;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	return 0;
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_civoid vnic_dev_free_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	if (ring->descs) {
20962306a36Sopenharmony_ci		dma_free_coherent(&vdev->pdev->dev, ring->size_unaligned,
21062306a36Sopenharmony_ci				  ring->descs_unaligned,
21162306a36Sopenharmony_ci				  ring->base_addr_unaligned);
21262306a36Sopenharmony_ci		ring->descs = NULL;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic int _vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
21762306a36Sopenharmony_ci	int wait)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct vnic_devcmd __iomem *devcmd = vdev->devcmd;
22062306a36Sopenharmony_ci	unsigned int i;
22162306a36Sopenharmony_ci	int delay;
22262306a36Sopenharmony_ci	u32 status;
22362306a36Sopenharmony_ci	int err;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	status = ioread32(&devcmd->status);
22662306a36Sopenharmony_ci	if (status == 0xFFFFFFFF) {
22762306a36Sopenharmony_ci		/* PCI-e target device is gone */
22862306a36Sopenharmony_ci		return -ENODEV;
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci	if (status & STAT_BUSY) {
23162306a36Sopenharmony_ci		vdev_neterr(vdev, "Busy devcmd %d\n", _CMD_N(cmd));
23262306a36Sopenharmony_ci		return -EBUSY;
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (_CMD_DIR(cmd) & _CMD_DIR_WRITE) {
23662306a36Sopenharmony_ci		for (i = 0; i < VNIC_DEVCMD_NARGS; i++)
23762306a36Sopenharmony_ci			writeq(vdev->args[i], &devcmd->args[i]);
23862306a36Sopenharmony_ci		wmb();
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	iowrite32(cmd, &devcmd->cmd);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if ((_CMD_FLAGS(cmd) & _CMD_FLAGS_NOWAIT))
24462306a36Sopenharmony_ci		return 0;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	for (delay = 0; delay < wait; delay++) {
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci		udelay(100);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci		status = ioread32(&devcmd->status);
25162306a36Sopenharmony_ci		if (status == 0xFFFFFFFF) {
25262306a36Sopenharmony_ci			/* PCI-e target device is gone */
25362306a36Sopenharmony_ci			return -ENODEV;
25462306a36Sopenharmony_ci		}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci		if (!(status & STAT_BUSY)) {
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci			if (status & STAT_ERROR) {
25962306a36Sopenharmony_ci				err = (int)readq(&devcmd->args[0]);
26062306a36Sopenharmony_ci				if (err == ERR_EINVAL &&
26162306a36Sopenharmony_ci				    cmd == CMD_CAPABILITY)
26262306a36Sopenharmony_ci					return -err;
26362306a36Sopenharmony_ci				if (err != ERR_ECMDUNKNOWN ||
26462306a36Sopenharmony_ci				    cmd != CMD_CAPABILITY)
26562306a36Sopenharmony_ci					vdev_neterr(vdev, "Error %d devcmd %d\n",
26662306a36Sopenharmony_ci						    err, _CMD_N(cmd));
26762306a36Sopenharmony_ci				return -err;
26862306a36Sopenharmony_ci			}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci			if (_CMD_DIR(cmd) & _CMD_DIR_READ) {
27162306a36Sopenharmony_ci				rmb();
27262306a36Sopenharmony_ci				for (i = 0; i < VNIC_DEVCMD_NARGS; i++)
27362306a36Sopenharmony_ci					vdev->args[i] = readq(&devcmd->args[i]);
27462306a36Sopenharmony_ci			}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci			return 0;
27762306a36Sopenharmony_ci		}
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	vdev_neterr(vdev, "Timedout devcmd %d\n", _CMD_N(cmd));
28162306a36Sopenharmony_ci	return -ETIMEDOUT;
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic int _vnic_dev_cmd2(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
28562306a36Sopenharmony_ci			  int wait)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	struct devcmd2_controller *dc2c = vdev->devcmd2;
28862306a36Sopenharmony_ci	struct devcmd2_result *result;
28962306a36Sopenharmony_ci	u8 color;
29062306a36Sopenharmony_ci	unsigned int i;
29162306a36Sopenharmony_ci	int delay, err;
29262306a36Sopenharmony_ci	u32 fetch_index, new_posted;
29362306a36Sopenharmony_ci	u32 posted = dc2c->posted;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	fetch_index = ioread32(&dc2c->wq_ctrl->fetch_index);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	if (fetch_index == 0xFFFFFFFF)
29862306a36Sopenharmony_ci		return -ENODEV;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	new_posted = (posted + 1) % DEVCMD2_RING_SIZE;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (new_posted == fetch_index) {
30362306a36Sopenharmony_ci		vdev_neterr(vdev, "devcmd2 %d: wq is full. fetch index: %u, posted index: %u\n",
30462306a36Sopenharmony_ci			    _CMD_N(cmd), fetch_index, posted);
30562306a36Sopenharmony_ci		return -EBUSY;
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci	dc2c->cmd_ring[posted].cmd = cmd;
30862306a36Sopenharmony_ci	dc2c->cmd_ring[posted].flags = 0;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	if ((_CMD_FLAGS(cmd) & _CMD_FLAGS_NOWAIT))
31162306a36Sopenharmony_ci		dc2c->cmd_ring[posted].flags |= DEVCMD2_FNORESULT;
31262306a36Sopenharmony_ci	if (_CMD_DIR(cmd) & _CMD_DIR_WRITE)
31362306a36Sopenharmony_ci		for (i = 0; i < VNIC_DEVCMD_NARGS; i++)
31462306a36Sopenharmony_ci			dc2c->cmd_ring[posted].args[i] = vdev->args[i];
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	/* Adding write memory barrier prevents compiler and/or CPU reordering,
31762306a36Sopenharmony_ci	 * thus avoiding descriptor posting before descriptor is initialized.
31862306a36Sopenharmony_ci	 * Otherwise, hardware can read stale descriptor fields.
31962306a36Sopenharmony_ci	 */
32062306a36Sopenharmony_ci	wmb();
32162306a36Sopenharmony_ci	iowrite32(new_posted, &dc2c->wq_ctrl->posted_index);
32262306a36Sopenharmony_ci	dc2c->posted = new_posted;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	if (dc2c->cmd_ring[posted].flags & DEVCMD2_FNORESULT)
32562306a36Sopenharmony_ci		return 0;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	result = dc2c->result + dc2c->next_result;
32862306a36Sopenharmony_ci	color = dc2c->color;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	dc2c->next_result++;
33162306a36Sopenharmony_ci	if (dc2c->next_result == dc2c->result_size) {
33262306a36Sopenharmony_ci		dc2c->next_result = 0;
33362306a36Sopenharmony_ci		dc2c->color = dc2c->color ? 0 : 1;
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	for (delay = 0; delay < wait; delay++) {
33762306a36Sopenharmony_ci		if (result->color == color) {
33862306a36Sopenharmony_ci			if (result->error) {
33962306a36Sopenharmony_ci				err = result->error;
34062306a36Sopenharmony_ci				if (err != ERR_ECMDUNKNOWN ||
34162306a36Sopenharmony_ci				    cmd != CMD_CAPABILITY)
34262306a36Sopenharmony_ci					vdev_neterr(vdev, "Error %d devcmd %d\n",
34362306a36Sopenharmony_ci						    err, _CMD_N(cmd));
34462306a36Sopenharmony_ci				return -err;
34562306a36Sopenharmony_ci			}
34662306a36Sopenharmony_ci			if (_CMD_DIR(cmd) & _CMD_DIR_READ)
34762306a36Sopenharmony_ci				for (i = 0; i < VNIC_DEVCMD2_NARGS; i++)
34862306a36Sopenharmony_ci					vdev->args[i] = result->results[i];
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci			return 0;
35162306a36Sopenharmony_ci		}
35262306a36Sopenharmony_ci		udelay(100);
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	vdev_neterr(vdev, "devcmd %d timed out\n", _CMD_N(cmd));
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	return -ETIMEDOUT;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic int vnic_dev_init_devcmd1(struct vnic_dev *vdev)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	vdev->devcmd = vnic_dev_get_res(vdev, RES_TYPE_DEVCMD, 0);
36362306a36Sopenharmony_ci	if (!vdev->devcmd)
36462306a36Sopenharmony_ci		return -ENODEV;
36562306a36Sopenharmony_ci	vdev->devcmd_rtn = _vnic_dev_cmd;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	return 0;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic int vnic_dev_init_devcmd2(struct vnic_dev *vdev)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	int err;
37362306a36Sopenharmony_ci	unsigned int fetch_index;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	if (vdev->devcmd2)
37662306a36Sopenharmony_ci		return 0;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	vdev->devcmd2 = kzalloc(sizeof(*vdev->devcmd2), GFP_KERNEL);
37962306a36Sopenharmony_ci	if (!vdev->devcmd2)
38062306a36Sopenharmony_ci		return -ENOMEM;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	vdev->devcmd2->color = 1;
38362306a36Sopenharmony_ci	vdev->devcmd2->result_size = DEVCMD2_RING_SIZE;
38462306a36Sopenharmony_ci	err = enic_wq_devcmd2_alloc(vdev, &vdev->devcmd2->wq, DEVCMD2_RING_SIZE,
38562306a36Sopenharmony_ci				    DEVCMD2_DESC_SIZE);
38662306a36Sopenharmony_ci	if (err)
38762306a36Sopenharmony_ci		goto err_free_devcmd2;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	fetch_index = ioread32(&vdev->devcmd2->wq.ctrl->fetch_index);
39062306a36Sopenharmony_ci	if (fetch_index == 0xFFFFFFFF) { /* check for hardware gone  */
39162306a36Sopenharmony_ci		vdev_err(vdev, "Fatal error in devcmd2 init - hardware surprise removal\n");
39262306a36Sopenharmony_ci		err = -ENODEV;
39362306a36Sopenharmony_ci		goto err_free_wq;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	enic_wq_init_start(&vdev->devcmd2->wq, 0, fetch_index, fetch_index, 0,
39762306a36Sopenharmony_ci			   0);
39862306a36Sopenharmony_ci	vdev->devcmd2->posted = fetch_index;
39962306a36Sopenharmony_ci	vnic_wq_enable(&vdev->devcmd2->wq);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	err = vnic_dev_alloc_desc_ring(vdev, &vdev->devcmd2->results_ring,
40262306a36Sopenharmony_ci				       DEVCMD2_RING_SIZE, DEVCMD2_DESC_SIZE);
40362306a36Sopenharmony_ci	if (err)
40462306a36Sopenharmony_ci		goto err_disable_wq;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	vdev->devcmd2->result = vdev->devcmd2->results_ring.descs;
40762306a36Sopenharmony_ci	vdev->devcmd2->cmd_ring = vdev->devcmd2->wq.ring.descs;
40862306a36Sopenharmony_ci	vdev->devcmd2->wq_ctrl = vdev->devcmd2->wq.ctrl;
40962306a36Sopenharmony_ci	vdev->args[0] = (u64)vdev->devcmd2->results_ring.base_addr |
41062306a36Sopenharmony_ci			VNIC_PADDR_TARGET;
41162306a36Sopenharmony_ci	vdev->args[1] = DEVCMD2_RING_SIZE;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	err = _vnic_dev_cmd2(vdev, CMD_INITIALIZE_DEVCMD2, 1000);
41462306a36Sopenharmony_ci	if (err)
41562306a36Sopenharmony_ci		goto err_free_desc_ring;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	vdev->devcmd_rtn = _vnic_dev_cmd2;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	return 0;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cierr_free_desc_ring:
42262306a36Sopenharmony_ci	vnic_dev_free_desc_ring(vdev, &vdev->devcmd2->results_ring);
42362306a36Sopenharmony_cierr_disable_wq:
42462306a36Sopenharmony_ci	vnic_wq_disable(&vdev->devcmd2->wq);
42562306a36Sopenharmony_cierr_free_wq:
42662306a36Sopenharmony_ci	vnic_wq_free(&vdev->devcmd2->wq);
42762306a36Sopenharmony_cierr_free_devcmd2:
42862306a36Sopenharmony_ci	kfree(vdev->devcmd2);
42962306a36Sopenharmony_ci	vdev->devcmd2 = NULL;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	return err;
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic void vnic_dev_deinit_devcmd2(struct vnic_dev *vdev)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	vnic_dev_free_desc_ring(vdev, &vdev->devcmd2->results_ring);
43762306a36Sopenharmony_ci	vnic_wq_disable(&vdev->devcmd2->wq);
43862306a36Sopenharmony_ci	vnic_wq_free(&vdev->devcmd2->wq);
43962306a36Sopenharmony_ci	kfree(vdev->devcmd2);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic int vnic_dev_cmd_proxy(struct vnic_dev *vdev,
44362306a36Sopenharmony_ci	enum vnic_devcmd_cmd proxy_cmd, enum vnic_devcmd_cmd cmd,
44462306a36Sopenharmony_ci	u64 *a0, u64 *a1, int wait)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	u32 status;
44762306a36Sopenharmony_ci	int err;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	memset(vdev->args, 0, sizeof(vdev->args));
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	vdev->args[0] = vdev->proxy_index;
45262306a36Sopenharmony_ci	vdev->args[1] = cmd;
45362306a36Sopenharmony_ci	vdev->args[2] = *a0;
45462306a36Sopenharmony_ci	vdev->args[3] = *a1;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	err = vdev->devcmd_rtn(vdev, proxy_cmd, wait);
45762306a36Sopenharmony_ci	if (err)
45862306a36Sopenharmony_ci		return err;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	status = (u32)vdev->args[0];
46162306a36Sopenharmony_ci	if (status & STAT_ERROR) {
46262306a36Sopenharmony_ci		err = (int)vdev->args[1];
46362306a36Sopenharmony_ci		if (err != ERR_ECMDUNKNOWN ||
46462306a36Sopenharmony_ci		    cmd != CMD_CAPABILITY)
46562306a36Sopenharmony_ci			vdev_neterr(vdev, "Error %d proxy devcmd %d\n",
46662306a36Sopenharmony_ci				    err, _CMD_N(cmd));
46762306a36Sopenharmony_ci		return err;
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	*a0 = vdev->args[1];
47162306a36Sopenharmony_ci	*a1 = vdev->args[2];
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	return 0;
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic int vnic_dev_cmd_no_proxy(struct vnic_dev *vdev,
47762306a36Sopenharmony_ci	enum vnic_devcmd_cmd cmd, u64 *a0, u64 *a1, int wait)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	int err;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	vdev->args[0] = *a0;
48262306a36Sopenharmony_ci	vdev->args[1] = *a1;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	err = vdev->devcmd_rtn(vdev, cmd, wait);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	*a0 = vdev->args[0];
48762306a36Sopenharmony_ci	*a1 = vdev->args[1];
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	return err;
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_civoid vnic_dev_cmd_proxy_by_index_start(struct vnic_dev *vdev, u16 index)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	vdev->proxy = PROXY_BY_INDEX;
49562306a36Sopenharmony_ci	vdev->proxy_index = index;
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_civoid vnic_dev_cmd_proxy_end(struct vnic_dev *vdev)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	vdev->proxy = PROXY_NONE;
50162306a36Sopenharmony_ci	vdev->proxy_index = 0;
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ciint vnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
50562306a36Sopenharmony_ci	u64 *a0, u64 *a1, int wait)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	memset(vdev->args, 0, sizeof(vdev->args));
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	switch (vdev->proxy) {
51062306a36Sopenharmony_ci	case PROXY_BY_INDEX:
51162306a36Sopenharmony_ci		return vnic_dev_cmd_proxy(vdev, CMD_PROXY_BY_INDEX, cmd,
51262306a36Sopenharmony_ci				a0, a1, wait);
51362306a36Sopenharmony_ci	case PROXY_BY_BDF:
51462306a36Sopenharmony_ci		return vnic_dev_cmd_proxy(vdev, CMD_PROXY_BY_BDF, cmd,
51562306a36Sopenharmony_ci				a0, a1, wait);
51662306a36Sopenharmony_ci	case PROXY_NONE:
51762306a36Sopenharmony_ci	default:
51862306a36Sopenharmony_ci		return vnic_dev_cmd_no_proxy(vdev, cmd, a0, a1, wait);
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic int vnic_dev_capable(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	u64 a0 = (u32)cmd, a1 = 0;
52562306a36Sopenharmony_ci	int wait = 1000;
52662306a36Sopenharmony_ci	int err;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	err = vnic_dev_cmd(vdev, CMD_CAPABILITY, &a0, &a1, wait);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	return !(err || a0);
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ciint vnic_dev_fw_info(struct vnic_dev *vdev,
53462306a36Sopenharmony_ci	struct vnic_devcmd_fw_info **fw_info)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	u64 a0, a1 = 0;
53762306a36Sopenharmony_ci	int wait = 1000;
53862306a36Sopenharmony_ci	int err = 0;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	if (!vdev->fw_info) {
54162306a36Sopenharmony_ci		vdev->fw_info = dma_alloc_coherent(&vdev->pdev->dev,
54262306a36Sopenharmony_ci						   sizeof(struct vnic_devcmd_fw_info),
54362306a36Sopenharmony_ci						   &vdev->fw_info_pa, GFP_ATOMIC);
54462306a36Sopenharmony_ci		if (!vdev->fw_info)
54562306a36Sopenharmony_ci			return -ENOMEM;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci		a0 = vdev->fw_info_pa;
54862306a36Sopenharmony_ci		a1 = sizeof(struct vnic_devcmd_fw_info);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci		/* only get fw_info once and cache it */
55162306a36Sopenharmony_ci		if (vnic_dev_capable(vdev, CMD_MCPU_FW_INFO))
55262306a36Sopenharmony_ci			err = vnic_dev_cmd(vdev, CMD_MCPU_FW_INFO,
55362306a36Sopenharmony_ci				&a0, &a1, wait);
55462306a36Sopenharmony_ci		else
55562306a36Sopenharmony_ci			err = vnic_dev_cmd(vdev, CMD_MCPU_FW_INFO_OLD,
55662306a36Sopenharmony_ci				&a0, &a1, wait);
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	*fw_info = vdev->fw_info;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	return err;
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ciint vnic_dev_spec(struct vnic_dev *vdev, unsigned int offset, unsigned int size,
56562306a36Sopenharmony_ci	void *value)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	u64 a0, a1;
56862306a36Sopenharmony_ci	int wait = 1000;
56962306a36Sopenharmony_ci	int err;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	a0 = offset;
57262306a36Sopenharmony_ci	a1 = size;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	err = vnic_dev_cmd(vdev, CMD_DEV_SPEC, &a0, &a1, wait);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	switch (size) {
57762306a36Sopenharmony_ci	case 1: *(u8 *)value = (u8)a0; break;
57862306a36Sopenharmony_ci	case 2: *(u16 *)value = (u16)a0; break;
57962306a36Sopenharmony_ci	case 4: *(u32 *)value = (u32)a0; break;
58062306a36Sopenharmony_ci	case 8: *(u64 *)value = a0; break;
58162306a36Sopenharmony_ci	default: BUG(); break;
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	return err;
58562306a36Sopenharmony_ci}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ciint vnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats)
58862306a36Sopenharmony_ci{
58962306a36Sopenharmony_ci	u64 a0, a1;
59062306a36Sopenharmony_ci	int wait = 1000;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	if (!vdev->stats) {
59362306a36Sopenharmony_ci		vdev->stats = dma_alloc_coherent(&vdev->pdev->dev,
59462306a36Sopenharmony_ci						 sizeof(struct vnic_stats),
59562306a36Sopenharmony_ci						 &vdev->stats_pa, GFP_ATOMIC);
59662306a36Sopenharmony_ci		if (!vdev->stats)
59762306a36Sopenharmony_ci			return -ENOMEM;
59862306a36Sopenharmony_ci	}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	*stats = vdev->stats;
60162306a36Sopenharmony_ci	a0 = vdev->stats_pa;
60262306a36Sopenharmony_ci	a1 = sizeof(struct vnic_stats);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	return vnic_dev_cmd(vdev, CMD_STATS_DUMP, &a0, &a1, wait);
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ciint vnic_dev_close(struct vnic_dev *vdev)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	u64 a0 = 0, a1 = 0;
61062306a36Sopenharmony_ci	int wait = 1000;
61162306a36Sopenharmony_ci	return vnic_dev_cmd(vdev, CMD_CLOSE, &a0, &a1, wait);
61262306a36Sopenharmony_ci}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ciint vnic_dev_enable_wait(struct vnic_dev *vdev)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	u64 a0 = 0, a1 = 0;
61762306a36Sopenharmony_ci	int wait = 1000;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	if (vnic_dev_capable(vdev, CMD_ENABLE_WAIT))
62062306a36Sopenharmony_ci		return vnic_dev_cmd(vdev, CMD_ENABLE_WAIT, &a0, &a1, wait);
62162306a36Sopenharmony_ci	else
62262306a36Sopenharmony_ci		return vnic_dev_cmd(vdev, CMD_ENABLE, &a0, &a1, wait);
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ciint vnic_dev_disable(struct vnic_dev *vdev)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	u64 a0 = 0, a1 = 0;
62862306a36Sopenharmony_ci	int wait = 1000;
62962306a36Sopenharmony_ci	return vnic_dev_cmd(vdev, CMD_DISABLE, &a0, &a1, wait);
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ciint vnic_dev_open(struct vnic_dev *vdev, int arg)
63362306a36Sopenharmony_ci{
63462306a36Sopenharmony_ci	u64 a0 = (u32)arg, a1 = 0;
63562306a36Sopenharmony_ci	int wait = 1000;
63662306a36Sopenharmony_ci	return vnic_dev_cmd(vdev, CMD_OPEN, &a0, &a1, wait);
63762306a36Sopenharmony_ci}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ciint vnic_dev_open_done(struct vnic_dev *vdev, int *done)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	u64 a0 = 0, a1 = 0;
64262306a36Sopenharmony_ci	int wait = 1000;
64362306a36Sopenharmony_ci	int err;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	*done = 0;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	err = vnic_dev_cmd(vdev, CMD_OPEN_STATUS, &a0, &a1, wait);
64862306a36Sopenharmony_ci	if (err)
64962306a36Sopenharmony_ci		return err;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	*done = (a0 == 0);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	return 0;
65462306a36Sopenharmony_ci}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ciint vnic_dev_soft_reset(struct vnic_dev *vdev, int arg)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	u64 a0 = (u32)arg, a1 = 0;
65962306a36Sopenharmony_ci	int wait = 1000;
66062306a36Sopenharmony_ci	return vnic_dev_cmd(vdev, CMD_SOFT_RESET, &a0, &a1, wait);
66162306a36Sopenharmony_ci}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ciint vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done)
66462306a36Sopenharmony_ci{
66562306a36Sopenharmony_ci	u64 a0 = 0, a1 = 0;
66662306a36Sopenharmony_ci	int wait = 1000;
66762306a36Sopenharmony_ci	int err;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	*done = 0;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	err = vnic_dev_cmd(vdev, CMD_SOFT_RESET_STATUS, &a0, &a1, wait);
67262306a36Sopenharmony_ci	if (err)
67362306a36Sopenharmony_ci		return err;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	*done = (a0 == 0);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	return 0;
67862306a36Sopenharmony_ci}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ciint vnic_dev_hang_reset(struct vnic_dev *vdev, int arg)
68162306a36Sopenharmony_ci{
68262306a36Sopenharmony_ci	u64 a0 = (u32)arg, a1 = 0;
68362306a36Sopenharmony_ci	int wait = 1000;
68462306a36Sopenharmony_ci	int err;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	if (vnic_dev_capable(vdev, CMD_HANG_RESET)) {
68762306a36Sopenharmony_ci		return vnic_dev_cmd(vdev, CMD_HANG_RESET,
68862306a36Sopenharmony_ci				&a0, &a1, wait);
68962306a36Sopenharmony_ci	} else {
69062306a36Sopenharmony_ci		err = vnic_dev_soft_reset(vdev, arg);
69162306a36Sopenharmony_ci		if (err)
69262306a36Sopenharmony_ci			return err;
69362306a36Sopenharmony_ci		return vnic_dev_init(vdev, 0);
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ciint vnic_dev_hang_reset_done(struct vnic_dev *vdev, int *done)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	u64 a0 = 0, a1 = 0;
70062306a36Sopenharmony_ci	int wait = 1000;
70162306a36Sopenharmony_ci	int err;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	*done = 0;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	if (vnic_dev_capable(vdev, CMD_HANG_RESET_STATUS)) {
70662306a36Sopenharmony_ci		err = vnic_dev_cmd(vdev, CMD_HANG_RESET_STATUS,
70762306a36Sopenharmony_ci				&a0, &a1, wait);
70862306a36Sopenharmony_ci		if (err)
70962306a36Sopenharmony_ci			return err;
71062306a36Sopenharmony_ci	} else {
71162306a36Sopenharmony_ci		return vnic_dev_soft_reset_done(vdev, done);
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	*done = (a0 == 0);
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	return 0;
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ciint vnic_dev_hang_notify(struct vnic_dev *vdev)
72062306a36Sopenharmony_ci{
72162306a36Sopenharmony_ci	u64 a0, a1;
72262306a36Sopenharmony_ci	int wait = 1000;
72362306a36Sopenharmony_ci	return vnic_dev_cmd(vdev, CMD_HANG_NOTIFY, &a0, &a1, wait);
72462306a36Sopenharmony_ci}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ciint vnic_dev_get_mac_addr(struct vnic_dev *vdev, u8 *mac_addr)
72762306a36Sopenharmony_ci{
72862306a36Sopenharmony_ci	u64 a0, a1;
72962306a36Sopenharmony_ci	int wait = 1000;
73062306a36Sopenharmony_ci	int err, i;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	for (i = 0; i < ETH_ALEN; i++)
73362306a36Sopenharmony_ci		mac_addr[i] = 0;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	err = vnic_dev_cmd(vdev, CMD_GET_MAC_ADDR, &a0, &a1, wait);
73662306a36Sopenharmony_ci	if (err)
73762306a36Sopenharmony_ci		return err;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	for (i = 0; i < ETH_ALEN; i++)
74062306a36Sopenharmony_ci		mac_addr[i] = ((u8 *)&a0)[i];
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	return 0;
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ciint vnic_dev_packet_filter(struct vnic_dev *vdev, int directed, int multicast,
74662306a36Sopenharmony_ci	int broadcast, int promisc, int allmulti)
74762306a36Sopenharmony_ci{
74862306a36Sopenharmony_ci	u64 a0, a1 = 0;
74962306a36Sopenharmony_ci	int wait = 1000;
75062306a36Sopenharmony_ci	int err;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	a0 = (directed ? CMD_PFILTER_DIRECTED : 0) |
75362306a36Sopenharmony_ci	     (multicast ? CMD_PFILTER_MULTICAST : 0) |
75462306a36Sopenharmony_ci	     (broadcast ? CMD_PFILTER_BROADCAST : 0) |
75562306a36Sopenharmony_ci	     (promisc ? CMD_PFILTER_PROMISCUOUS : 0) |
75662306a36Sopenharmony_ci	     (allmulti ? CMD_PFILTER_ALL_MULTICAST : 0);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	err = vnic_dev_cmd(vdev, CMD_PACKET_FILTER, &a0, &a1, wait);
75962306a36Sopenharmony_ci	if (err)
76062306a36Sopenharmony_ci		vdev_neterr(vdev, "Can't set packet filter\n");
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	return err;
76362306a36Sopenharmony_ci}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ciint vnic_dev_add_addr(struct vnic_dev *vdev, const u8 *addr)
76662306a36Sopenharmony_ci{
76762306a36Sopenharmony_ci	u64 a0 = 0, a1 = 0;
76862306a36Sopenharmony_ci	int wait = 1000;
76962306a36Sopenharmony_ci	int err;
77062306a36Sopenharmony_ci	int i;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	for (i = 0; i < ETH_ALEN; i++)
77362306a36Sopenharmony_ci		((u8 *)&a0)[i] = addr[i];
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	err = vnic_dev_cmd(vdev, CMD_ADDR_ADD, &a0, &a1, wait);
77662306a36Sopenharmony_ci	if (err)
77762306a36Sopenharmony_ci		vdev_neterr(vdev, "Can't add addr [%pM], %d\n", addr, err);
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	return err;
78062306a36Sopenharmony_ci}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ciint vnic_dev_del_addr(struct vnic_dev *vdev, const u8 *addr)
78362306a36Sopenharmony_ci{
78462306a36Sopenharmony_ci	u64 a0 = 0, a1 = 0;
78562306a36Sopenharmony_ci	int wait = 1000;
78662306a36Sopenharmony_ci	int err;
78762306a36Sopenharmony_ci	int i;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	for (i = 0; i < ETH_ALEN; i++)
79062306a36Sopenharmony_ci		((u8 *)&a0)[i] = addr[i];
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	err = vnic_dev_cmd(vdev, CMD_ADDR_DEL, &a0, &a1, wait);
79362306a36Sopenharmony_ci	if (err)
79462306a36Sopenharmony_ci		vdev_neterr(vdev, "Can't del addr [%pM], %d\n", addr, err);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	return err;
79762306a36Sopenharmony_ci}
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ciint vnic_dev_set_ig_vlan_rewrite_mode(struct vnic_dev *vdev,
80062306a36Sopenharmony_ci	u8 ig_vlan_rewrite_mode)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	u64 a0 = ig_vlan_rewrite_mode, a1 = 0;
80362306a36Sopenharmony_ci	int wait = 1000;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	if (vnic_dev_capable(vdev, CMD_IG_VLAN_REWRITE_MODE))
80662306a36Sopenharmony_ci		return vnic_dev_cmd(vdev, CMD_IG_VLAN_REWRITE_MODE,
80762306a36Sopenharmony_ci				&a0, &a1, wait);
80862306a36Sopenharmony_ci	else
80962306a36Sopenharmony_ci		return 0;
81062306a36Sopenharmony_ci}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_cistatic int vnic_dev_notify_setcmd(struct vnic_dev *vdev,
81362306a36Sopenharmony_ci	void *notify_addr, dma_addr_t notify_pa, u16 intr)
81462306a36Sopenharmony_ci{
81562306a36Sopenharmony_ci	u64 a0, a1;
81662306a36Sopenharmony_ci	int wait = 1000;
81762306a36Sopenharmony_ci	int r;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	memset(notify_addr, 0, sizeof(struct vnic_devcmd_notify));
82062306a36Sopenharmony_ci	vdev->notify = notify_addr;
82162306a36Sopenharmony_ci	vdev->notify_pa = notify_pa;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	a0 = (u64)notify_pa;
82462306a36Sopenharmony_ci	a1 = ((u64)intr << 32) & 0x0000ffff00000000ULL;
82562306a36Sopenharmony_ci	a1 += sizeof(struct vnic_devcmd_notify);
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	r = vnic_dev_cmd(vdev, CMD_NOTIFY, &a0, &a1, wait);
82862306a36Sopenharmony_ci	vdev->notify_sz = (r == 0) ? (u32)a1 : 0;
82962306a36Sopenharmony_ci	return r;
83062306a36Sopenharmony_ci}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ciint vnic_dev_notify_set(struct vnic_dev *vdev, u16 intr)
83362306a36Sopenharmony_ci{
83462306a36Sopenharmony_ci	void *notify_addr;
83562306a36Sopenharmony_ci	dma_addr_t notify_pa;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	if (vdev->notify || vdev->notify_pa) {
83862306a36Sopenharmony_ci		vdev_neterr(vdev, "notify block %p still allocated\n",
83962306a36Sopenharmony_ci			    vdev->notify);
84062306a36Sopenharmony_ci		return -EINVAL;
84162306a36Sopenharmony_ci	}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	notify_addr = dma_alloc_coherent(&vdev->pdev->dev,
84462306a36Sopenharmony_ci					 sizeof(struct vnic_devcmd_notify),
84562306a36Sopenharmony_ci					 &notify_pa, GFP_ATOMIC);
84662306a36Sopenharmony_ci	if (!notify_addr)
84762306a36Sopenharmony_ci		return -ENOMEM;
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	return vnic_dev_notify_setcmd(vdev, notify_addr, notify_pa, intr);
85062306a36Sopenharmony_ci}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_cistatic int vnic_dev_notify_unsetcmd(struct vnic_dev *vdev)
85362306a36Sopenharmony_ci{
85462306a36Sopenharmony_ci	u64 a0, a1;
85562306a36Sopenharmony_ci	int wait = 1000;
85662306a36Sopenharmony_ci	int err;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	a0 = 0;  /* paddr = 0 to unset notify buffer */
85962306a36Sopenharmony_ci	a1 = 0x0000ffff00000000ULL; /* intr num = -1 to unreg for intr */
86062306a36Sopenharmony_ci	a1 += sizeof(struct vnic_devcmd_notify);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	err = vnic_dev_cmd(vdev, CMD_NOTIFY, &a0, &a1, wait);
86362306a36Sopenharmony_ci	vdev->notify = NULL;
86462306a36Sopenharmony_ci	vdev->notify_pa = 0;
86562306a36Sopenharmony_ci	vdev->notify_sz = 0;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	return err;
86862306a36Sopenharmony_ci}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ciint vnic_dev_notify_unset(struct vnic_dev *vdev)
87162306a36Sopenharmony_ci{
87262306a36Sopenharmony_ci	if (vdev->notify) {
87362306a36Sopenharmony_ci		dma_free_coherent(&vdev->pdev->dev,
87462306a36Sopenharmony_ci				  sizeof(struct vnic_devcmd_notify),
87562306a36Sopenharmony_ci				  vdev->notify, vdev->notify_pa);
87662306a36Sopenharmony_ci	}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	return vnic_dev_notify_unsetcmd(vdev);
87962306a36Sopenharmony_ci}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_cistatic int vnic_dev_notify_ready(struct vnic_dev *vdev)
88262306a36Sopenharmony_ci{
88362306a36Sopenharmony_ci	u32 *words;
88462306a36Sopenharmony_ci	unsigned int nwords = vdev->notify_sz / 4;
88562306a36Sopenharmony_ci	unsigned int i;
88662306a36Sopenharmony_ci	u32 csum;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	if (!vdev->notify || !vdev->notify_sz)
88962306a36Sopenharmony_ci		return 0;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	do {
89262306a36Sopenharmony_ci		csum = 0;
89362306a36Sopenharmony_ci		memcpy(&vdev->notify_copy, vdev->notify, vdev->notify_sz);
89462306a36Sopenharmony_ci		words = (u32 *)&vdev->notify_copy;
89562306a36Sopenharmony_ci		for (i = 1; i < nwords; i++)
89662306a36Sopenharmony_ci			csum += words[i];
89762306a36Sopenharmony_ci	} while (csum != words[0]);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	return 1;
90062306a36Sopenharmony_ci}
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ciint vnic_dev_init(struct vnic_dev *vdev, int arg)
90362306a36Sopenharmony_ci{
90462306a36Sopenharmony_ci	u64 a0 = (u32)arg, a1 = 0;
90562306a36Sopenharmony_ci	int wait = 1000;
90662306a36Sopenharmony_ci	int r = 0;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	if (vnic_dev_capable(vdev, CMD_INIT))
90962306a36Sopenharmony_ci		r = vnic_dev_cmd(vdev, CMD_INIT, &a0, &a1, wait);
91062306a36Sopenharmony_ci	else {
91162306a36Sopenharmony_ci		vnic_dev_cmd(vdev, CMD_INIT_v1, &a0, &a1, wait);
91262306a36Sopenharmony_ci		if (a0 & CMD_INITF_DEFAULT_MAC) {
91362306a36Sopenharmony_ci			/* Emulate these for old CMD_INIT_v1 which
91462306a36Sopenharmony_ci			 * didn't pass a0 so no CMD_INITF_*.
91562306a36Sopenharmony_ci			 */
91662306a36Sopenharmony_ci			vnic_dev_cmd(vdev, CMD_GET_MAC_ADDR, &a0, &a1, wait);
91762306a36Sopenharmony_ci			vnic_dev_cmd(vdev, CMD_ADDR_ADD, &a0, &a1, wait);
91862306a36Sopenharmony_ci		}
91962306a36Sopenharmony_ci	}
92062306a36Sopenharmony_ci	return r;
92162306a36Sopenharmony_ci}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ciint vnic_dev_deinit(struct vnic_dev *vdev)
92462306a36Sopenharmony_ci{
92562306a36Sopenharmony_ci	u64 a0 = 0, a1 = 0;
92662306a36Sopenharmony_ci	int wait = 1000;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	return vnic_dev_cmd(vdev, CMD_DEINIT, &a0, &a1, wait);
92962306a36Sopenharmony_ci}
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_civoid vnic_dev_intr_coal_timer_info_default(struct vnic_dev *vdev)
93262306a36Sopenharmony_ci{
93362306a36Sopenharmony_ci	/* Default: hardware intr coal timer is in units of 1.5 usecs */
93462306a36Sopenharmony_ci	vdev->intr_coal_timer_info.mul = 2;
93562306a36Sopenharmony_ci	vdev->intr_coal_timer_info.div = 3;
93662306a36Sopenharmony_ci	vdev->intr_coal_timer_info.max_usec =
93762306a36Sopenharmony_ci		vnic_dev_intr_coal_timer_hw_to_usec(vdev, 0xffff);
93862306a36Sopenharmony_ci}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ciint vnic_dev_intr_coal_timer_info(struct vnic_dev *vdev)
94162306a36Sopenharmony_ci{
94262306a36Sopenharmony_ci	int wait = 1000;
94362306a36Sopenharmony_ci	int err;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	memset(vdev->args, 0, sizeof(vdev->args));
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	if (vnic_dev_capable(vdev, CMD_INTR_COAL_CONVERT))
94862306a36Sopenharmony_ci		err = vdev->devcmd_rtn(vdev, CMD_INTR_COAL_CONVERT, wait);
94962306a36Sopenharmony_ci	else
95062306a36Sopenharmony_ci		err = ERR_ECMDUNKNOWN;
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	/* Use defaults when firmware doesn't support the devcmd at all or
95362306a36Sopenharmony_ci	 * supports it for only specific hardware
95462306a36Sopenharmony_ci	 */
95562306a36Sopenharmony_ci	if ((err == ERR_ECMDUNKNOWN) ||
95662306a36Sopenharmony_ci		(!err && !(vdev->args[0] && vdev->args[1] && vdev->args[2]))) {
95762306a36Sopenharmony_ci		vdev_netwarn(vdev, "Using default conversion factor for interrupt coalesce timer\n");
95862306a36Sopenharmony_ci		vnic_dev_intr_coal_timer_info_default(vdev);
95962306a36Sopenharmony_ci		return 0;
96062306a36Sopenharmony_ci	}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	if (!err) {
96362306a36Sopenharmony_ci		vdev->intr_coal_timer_info.mul = (u32) vdev->args[0];
96462306a36Sopenharmony_ci		vdev->intr_coal_timer_info.div = (u32) vdev->args[1];
96562306a36Sopenharmony_ci		vdev->intr_coal_timer_info.max_usec = (u32) vdev->args[2];
96662306a36Sopenharmony_ci	}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	return err;
96962306a36Sopenharmony_ci}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ciint vnic_dev_link_status(struct vnic_dev *vdev)
97262306a36Sopenharmony_ci{
97362306a36Sopenharmony_ci	if (!vnic_dev_notify_ready(vdev))
97462306a36Sopenharmony_ci		return 0;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	return vdev->notify_copy.link_state;
97762306a36Sopenharmony_ci}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ciu32 vnic_dev_port_speed(struct vnic_dev *vdev)
98062306a36Sopenharmony_ci{
98162306a36Sopenharmony_ci	if (!vnic_dev_notify_ready(vdev))
98262306a36Sopenharmony_ci		return 0;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	return vdev->notify_copy.port_speed;
98562306a36Sopenharmony_ci}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ciu32 vnic_dev_msg_lvl(struct vnic_dev *vdev)
98862306a36Sopenharmony_ci{
98962306a36Sopenharmony_ci	if (!vnic_dev_notify_ready(vdev))
99062306a36Sopenharmony_ci		return 0;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	return vdev->notify_copy.msglvl;
99362306a36Sopenharmony_ci}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ciu32 vnic_dev_mtu(struct vnic_dev *vdev)
99662306a36Sopenharmony_ci{
99762306a36Sopenharmony_ci	if (!vnic_dev_notify_ready(vdev))
99862306a36Sopenharmony_ci		return 0;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	return vdev->notify_copy.mtu;
100162306a36Sopenharmony_ci}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_civoid vnic_dev_set_intr_mode(struct vnic_dev *vdev,
100462306a36Sopenharmony_ci	enum vnic_dev_intr_mode intr_mode)
100562306a36Sopenharmony_ci{
100662306a36Sopenharmony_ci	vdev->intr_mode = intr_mode;
100762306a36Sopenharmony_ci}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_cienum vnic_dev_intr_mode vnic_dev_get_intr_mode(
101062306a36Sopenharmony_ci	struct vnic_dev *vdev)
101162306a36Sopenharmony_ci{
101262306a36Sopenharmony_ci	return vdev->intr_mode;
101362306a36Sopenharmony_ci}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ciu32 vnic_dev_intr_coal_timer_usec_to_hw(struct vnic_dev *vdev, u32 usec)
101662306a36Sopenharmony_ci{
101762306a36Sopenharmony_ci	return (usec * vdev->intr_coal_timer_info.mul) /
101862306a36Sopenharmony_ci		vdev->intr_coal_timer_info.div;
101962306a36Sopenharmony_ci}
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ciu32 vnic_dev_intr_coal_timer_hw_to_usec(struct vnic_dev *vdev, u32 hw_cycles)
102262306a36Sopenharmony_ci{
102362306a36Sopenharmony_ci	return (hw_cycles * vdev->intr_coal_timer_info.div) /
102462306a36Sopenharmony_ci		vdev->intr_coal_timer_info.mul;
102562306a36Sopenharmony_ci}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ciu32 vnic_dev_get_intr_coal_timer_max(struct vnic_dev *vdev)
102862306a36Sopenharmony_ci{
102962306a36Sopenharmony_ci	return vdev->intr_coal_timer_info.max_usec;
103062306a36Sopenharmony_ci}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_civoid vnic_dev_unregister(struct vnic_dev *vdev)
103362306a36Sopenharmony_ci{
103462306a36Sopenharmony_ci	if (vdev) {
103562306a36Sopenharmony_ci		if (vdev->notify)
103662306a36Sopenharmony_ci			dma_free_coherent(&vdev->pdev->dev,
103762306a36Sopenharmony_ci					  sizeof(struct vnic_devcmd_notify),
103862306a36Sopenharmony_ci					  vdev->notify, vdev->notify_pa);
103962306a36Sopenharmony_ci		if (vdev->stats)
104062306a36Sopenharmony_ci			dma_free_coherent(&vdev->pdev->dev,
104162306a36Sopenharmony_ci					  sizeof(struct vnic_stats),
104262306a36Sopenharmony_ci					  vdev->stats, vdev->stats_pa);
104362306a36Sopenharmony_ci		if (vdev->fw_info)
104462306a36Sopenharmony_ci			dma_free_coherent(&vdev->pdev->dev,
104562306a36Sopenharmony_ci					  sizeof(struct vnic_devcmd_fw_info),
104662306a36Sopenharmony_ci					  vdev->fw_info, vdev->fw_info_pa);
104762306a36Sopenharmony_ci		if (vdev->devcmd2)
104862306a36Sopenharmony_ci			vnic_dev_deinit_devcmd2(vdev);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci		kfree(vdev);
105162306a36Sopenharmony_ci	}
105262306a36Sopenharmony_ci}
105362306a36Sopenharmony_ciEXPORT_SYMBOL(vnic_dev_unregister);
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_cistruct vnic_dev *vnic_dev_register(struct vnic_dev *vdev,
105662306a36Sopenharmony_ci	void *priv, struct pci_dev *pdev, struct vnic_dev_bar *bar,
105762306a36Sopenharmony_ci	unsigned int num_bars)
105862306a36Sopenharmony_ci{
105962306a36Sopenharmony_ci	if (!vdev) {
106062306a36Sopenharmony_ci		vdev = kzalloc(sizeof(struct vnic_dev), GFP_KERNEL);
106162306a36Sopenharmony_ci		if (!vdev)
106262306a36Sopenharmony_ci			return NULL;
106362306a36Sopenharmony_ci	}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	vdev->priv = priv;
106662306a36Sopenharmony_ci	vdev->pdev = pdev;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	if (vnic_dev_discover_res(vdev, bar, num_bars))
106962306a36Sopenharmony_ci		goto err_out;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	return vdev;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_cierr_out:
107462306a36Sopenharmony_ci	vnic_dev_unregister(vdev);
107562306a36Sopenharmony_ci	return NULL;
107662306a36Sopenharmony_ci}
107762306a36Sopenharmony_ciEXPORT_SYMBOL(vnic_dev_register);
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_cistruct pci_dev *vnic_dev_get_pdev(struct vnic_dev *vdev)
108062306a36Sopenharmony_ci{
108162306a36Sopenharmony_ci	return vdev->pdev;
108262306a36Sopenharmony_ci}
108362306a36Sopenharmony_ciEXPORT_SYMBOL(vnic_dev_get_pdev);
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ciint vnic_devcmd_init(struct vnic_dev *vdev)
108662306a36Sopenharmony_ci{
108762306a36Sopenharmony_ci	void __iomem *res;
108862306a36Sopenharmony_ci	int err;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	res = vnic_dev_get_res(vdev, RES_TYPE_DEVCMD2, 0);
109162306a36Sopenharmony_ci	if (res) {
109262306a36Sopenharmony_ci		err = vnic_dev_init_devcmd2(vdev);
109362306a36Sopenharmony_ci		if (err)
109462306a36Sopenharmony_ci			vdev_warn(vdev, "DEVCMD2 init failed: %d, Using DEVCMD1\n",
109562306a36Sopenharmony_ci				  err);
109662306a36Sopenharmony_ci		else
109762306a36Sopenharmony_ci			return 0;
109862306a36Sopenharmony_ci	} else {
109962306a36Sopenharmony_ci		vdev_warn(vdev, "DEVCMD2 resource not found (old firmware?) Using DEVCMD1\n");
110062306a36Sopenharmony_ci	}
110162306a36Sopenharmony_ci	err = vnic_dev_init_devcmd1(vdev);
110262306a36Sopenharmony_ci	if (err)
110362306a36Sopenharmony_ci		vdev_err(vdev, "DEVCMD1 initialization failed: %d\n", err);
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	return err;
110662306a36Sopenharmony_ci}
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ciint vnic_dev_init_prov2(struct vnic_dev *vdev, u8 *buf, u32 len)
110962306a36Sopenharmony_ci{
111062306a36Sopenharmony_ci	u64 a0, a1 = len;
111162306a36Sopenharmony_ci	int wait = 1000;
111262306a36Sopenharmony_ci	dma_addr_t prov_pa;
111362306a36Sopenharmony_ci	void *prov_buf;
111462306a36Sopenharmony_ci	int ret;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	prov_buf = dma_alloc_coherent(&vdev->pdev->dev, len, &prov_pa, GFP_ATOMIC);
111762306a36Sopenharmony_ci	if (!prov_buf)
111862306a36Sopenharmony_ci		return -ENOMEM;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	memcpy(prov_buf, buf, len);
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	a0 = prov_pa;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	ret = vnic_dev_cmd(vdev, CMD_INIT_PROV_INFO2, &a0, &a1, wait);
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	dma_free_coherent(&vdev->pdev->dev, len, prov_buf, prov_pa);
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	return ret;
112962306a36Sopenharmony_ci}
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ciint vnic_dev_enable2(struct vnic_dev *vdev, int active)
113262306a36Sopenharmony_ci{
113362306a36Sopenharmony_ci	u64 a0, a1 = 0;
113462306a36Sopenharmony_ci	int wait = 1000;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	a0 = (active ? CMD_ENABLE2_ACTIVE : 0);
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	return vnic_dev_cmd(vdev, CMD_ENABLE2, &a0, &a1, wait);
113962306a36Sopenharmony_ci}
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_cistatic int vnic_dev_cmd_status(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
114262306a36Sopenharmony_ci	int *status)
114362306a36Sopenharmony_ci{
114462306a36Sopenharmony_ci	u64 a0 = cmd, a1 = 0;
114562306a36Sopenharmony_ci	int wait = 1000;
114662306a36Sopenharmony_ci	int ret;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	ret = vnic_dev_cmd(vdev, CMD_STATUS, &a0, &a1, wait);
114962306a36Sopenharmony_ci	if (!ret)
115062306a36Sopenharmony_ci		*status = (int)a0;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	return ret;
115362306a36Sopenharmony_ci}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ciint vnic_dev_enable2_done(struct vnic_dev *vdev, int *status)
115662306a36Sopenharmony_ci{
115762306a36Sopenharmony_ci	return vnic_dev_cmd_status(vdev, CMD_ENABLE2, status);
115862306a36Sopenharmony_ci}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ciint vnic_dev_deinit_done(struct vnic_dev *vdev, int *status)
116162306a36Sopenharmony_ci{
116262306a36Sopenharmony_ci	return vnic_dev_cmd_status(vdev, CMD_DEINIT, status);
116362306a36Sopenharmony_ci}
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ciint vnic_dev_set_mac_addr(struct vnic_dev *vdev, u8 *mac_addr)
116662306a36Sopenharmony_ci{
116762306a36Sopenharmony_ci	u64 a0, a1;
116862306a36Sopenharmony_ci	int wait = 1000;
116962306a36Sopenharmony_ci	int i;
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	for (i = 0; i < ETH_ALEN; i++)
117262306a36Sopenharmony_ci		((u8 *)&a0)[i] = mac_addr[i];
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	return vnic_dev_cmd(vdev, CMD_SET_MAC_ADDR, &a0, &a1, wait);
117562306a36Sopenharmony_ci}
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci/* vnic_dev_classifier: Add/Delete classifier entries
117862306a36Sopenharmony_ci * @vdev: vdev of the device
117962306a36Sopenharmony_ci * @cmd: CLSF_ADD for Add filter
118062306a36Sopenharmony_ci *	 CLSF_DEL for Delete filter
118162306a36Sopenharmony_ci * @entry: In case of ADD filter, the caller passes the RQ number in this
118262306a36Sopenharmony_ci *	   variable.
118362306a36Sopenharmony_ci *
118462306a36Sopenharmony_ci *	   This function stores the filter_id returned by the firmware in the
118562306a36Sopenharmony_ci *	   same variable before return;
118662306a36Sopenharmony_ci *
118762306a36Sopenharmony_ci *	   In case of DEL filter, the caller passes the RQ number. Return
118862306a36Sopenharmony_ci *	   value is irrelevant.
118962306a36Sopenharmony_ci * @data: filter data
119062306a36Sopenharmony_ci */
119162306a36Sopenharmony_ciint vnic_dev_classifier(struct vnic_dev *vdev, u8 cmd, u16 *entry,
119262306a36Sopenharmony_ci			struct filter *data)
119362306a36Sopenharmony_ci{
119462306a36Sopenharmony_ci	u64 a0, a1;
119562306a36Sopenharmony_ci	int wait = 1000;
119662306a36Sopenharmony_ci	dma_addr_t tlv_pa;
119762306a36Sopenharmony_ci	int ret = -EINVAL;
119862306a36Sopenharmony_ci	struct filter_tlv *tlv, *tlv_va;
119962306a36Sopenharmony_ci	struct filter_action *action;
120062306a36Sopenharmony_ci	u64 tlv_size;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	if (cmd == CLSF_ADD) {
120362306a36Sopenharmony_ci		tlv_size = sizeof(struct filter) +
120462306a36Sopenharmony_ci			   sizeof(struct filter_action) +
120562306a36Sopenharmony_ci			   2 * sizeof(struct filter_tlv);
120662306a36Sopenharmony_ci		tlv_va = dma_alloc_coherent(&vdev->pdev->dev, tlv_size,
120762306a36Sopenharmony_ci					    &tlv_pa, GFP_ATOMIC);
120862306a36Sopenharmony_ci		if (!tlv_va)
120962306a36Sopenharmony_ci			return -ENOMEM;
121062306a36Sopenharmony_ci		tlv = tlv_va;
121162306a36Sopenharmony_ci		a0 = tlv_pa;
121262306a36Sopenharmony_ci		a1 = tlv_size;
121362306a36Sopenharmony_ci		memset(tlv, 0, tlv_size);
121462306a36Sopenharmony_ci		tlv->type = CLSF_TLV_FILTER;
121562306a36Sopenharmony_ci		tlv->length = sizeof(struct filter);
121662306a36Sopenharmony_ci		*(struct filter *)&tlv->val = *data;
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci		tlv = (struct filter_tlv *)((char *)tlv +
121962306a36Sopenharmony_ci					    sizeof(struct filter_tlv) +
122062306a36Sopenharmony_ci					    sizeof(struct filter));
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci		tlv->type = CLSF_TLV_ACTION;
122362306a36Sopenharmony_ci		tlv->length = sizeof(struct filter_action);
122462306a36Sopenharmony_ci		action = (struct filter_action *)&tlv->val;
122562306a36Sopenharmony_ci		action->type = FILTER_ACTION_RQ_STEERING;
122662306a36Sopenharmony_ci		action->u.rq_idx = *entry;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci		ret = vnic_dev_cmd(vdev, CMD_ADD_FILTER, &a0, &a1, wait);
122962306a36Sopenharmony_ci		*entry = (u16)a0;
123062306a36Sopenharmony_ci		dma_free_coherent(&vdev->pdev->dev, tlv_size, tlv_va, tlv_pa);
123162306a36Sopenharmony_ci	} else if (cmd == CLSF_DEL) {
123262306a36Sopenharmony_ci		a0 = *entry;
123362306a36Sopenharmony_ci		ret = vnic_dev_cmd(vdev, CMD_DEL_FILTER, &a0, &a1, wait);
123462306a36Sopenharmony_ci	}
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	return ret;
123762306a36Sopenharmony_ci}
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ciint vnic_dev_overlay_offload_ctrl(struct vnic_dev *vdev, u8 overlay, u8 config)
124062306a36Sopenharmony_ci{
124162306a36Sopenharmony_ci	u64 a0 = overlay;
124262306a36Sopenharmony_ci	u64 a1 = config;
124362306a36Sopenharmony_ci	int wait = 1000;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	return vnic_dev_cmd(vdev, CMD_OVERLAY_OFFLOAD_CTRL, &a0, &a1, wait);
124662306a36Sopenharmony_ci}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ciint vnic_dev_overlay_offload_cfg(struct vnic_dev *vdev, u8 overlay,
124962306a36Sopenharmony_ci				 u16 vxlan_udp_port_number)
125062306a36Sopenharmony_ci{
125162306a36Sopenharmony_ci	u64 a1 = vxlan_udp_port_number;
125262306a36Sopenharmony_ci	u64 a0 = overlay;
125362306a36Sopenharmony_ci	int wait = 1000;
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	return vnic_dev_cmd(vdev, CMD_OVERLAY_OFFLOAD_CFG, &a0, &a1, wait);
125662306a36Sopenharmony_ci}
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ciint vnic_dev_get_supported_feature_ver(struct vnic_dev *vdev, u8 feature,
125962306a36Sopenharmony_ci				       u64 *supported_versions, u64 *a1)
126062306a36Sopenharmony_ci{
126162306a36Sopenharmony_ci	u64 a0 = feature;
126262306a36Sopenharmony_ci	int wait = 1000;
126362306a36Sopenharmony_ci	int ret;
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	ret = vnic_dev_cmd(vdev, CMD_GET_SUPP_FEATURE_VER, &a0, a1, wait);
126662306a36Sopenharmony_ci	if (!ret)
126762306a36Sopenharmony_ci		*supported_versions = a0;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	return ret;
127062306a36Sopenharmony_ci}
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ciint vnic_dev_capable_rss_hash_type(struct vnic_dev *vdev, u8 *rss_hash_type)
127362306a36Sopenharmony_ci{
127462306a36Sopenharmony_ci	u64 a0 = CMD_NIC_CFG, a1 = 0;
127562306a36Sopenharmony_ci	int wait = 1000;
127662306a36Sopenharmony_ci	int err;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	err = vnic_dev_cmd(vdev, CMD_CAPABILITY, &a0, &a1, wait);
127962306a36Sopenharmony_ci	/* rss_hash_type is valid only when a0 is 1. Adapter which does not
128062306a36Sopenharmony_ci	 * support CMD_CAPABILITY for rss_hash_type has a0 = 0
128162306a36Sopenharmony_ci	 */
128262306a36Sopenharmony_ci	if (err || (a0 != 1))
128362306a36Sopenharmony_ci		return -EOPNOTSUPP;
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	a1 = (a1 >> NIC_CFG_RSS_HASH_TYPE_SHIFT) &
128662306a36Sopenharmony_ci	     NIC_CFG_RSS_HASH_TYPE_MASK_FIELD;
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	*rss_hash_type = (u8)a1;
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	return 0;
129162306a36Sopenharmony_ci}
1292