162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci// Copyright 2014 Cisco Systems, Inc.  All rights reserved.
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/errno.h>
562306a36Sopenharmony_ci#include <linux/types.h>
662306a36Sopenharmony_ci#include <linux/pci.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "wq_enet_desc.h"
962306a36Sopenharmony_ci#include "cq_enet_desc.h"
1062306a36Sopenharmony_ci#include "vnic_resource.h"
1162306a36Sopenharmony_ci#include "vnic_dev.h"
1262306a36Sopenharmony_ci#include "vnic_wq.h"
1362306a36Sopenharmony_ci#include "vnic_cq.h"
1462306a36Sopenharmony_ci#include "vnic_intr.h"
1562306a36Sopenharmony_ci#include "vnic_stats.h"
1662306a36Sopenharmony_ci#include "snic.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ciint
1962306a36Sopenharmony_cisnic_get_vnic_config(struct snic *snic)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	struct vnic_snic_config *c = &snic->config;
2262306a36Sopenharmony_ci	int ret;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define GET_CONFIG(m) \
2562306a36Sopenharmony_ci	do { \
2662306a36Sopenharmony_ci		ret = svnic_dev_spec(snic->vdev, \
2762306a36Sopenharmony_ci				     offsetof(struct vnic_snic_config, m), \
2862306a36Sopenharmony_ci				     sizeof(c->m), \
2962306a36Sopenharmony_ci				     &c->m); \
3062306a36Sopenharmony_ci		if (ret) { \
3162306a36Sopenharmony_ci			SNIC_HOST_ERR(snic->shost, \
3262306a36Sopenharmony_ci				      "Error getting %s, %d\n", #m, ret); \
3362306a36Sopenharmony_ci			return ret; \
3462306a36Sopenharmony_ci		} \
3562306a36Sopenharmony_ci	} while (0)
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	GET_CONFIG(wq_enet_desc_count);
3862306a36Sopenharmony_ci	GET_CONFIG(maxdatafieldsize);
3962306a36Sopenharmony_ci	GET_CONFIG(intr_timer);
4062306a36Sopenharmony_ci	GET_CONFIG(intr_timer_type);
4162306a36Sopenharmony_ci	GET_CONFIG(flags);
4262306a36Sopenharmony_ci	GET_CONFIG(io_throttle_count);
4362306a36Sopenharmony_ci	GET_CONFIG(port_down_timeout);
4462306a36Sopenharmony_ci	GET_CONFIG(port_down_io_retries);
4562306a36Sopenharmony_ci	GET_CONFIG(luns_per_tgt);
4662306a36Sopenharmony_ci	GET_CONFIG(xpt_type);
4762306a36Sopenharmony_ci	GET_CONFIG(hid);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	c->wq_enet_desc_count = min_t(u32,
5062306a36Sopenharmony_ci				      VNIC_SNIC_WQ_DESCS_MAX,
5162306a36Sopenharmony_ci				      max_t(u32,
5262306a36Sopenharmony_ci					    VNIC_SNIC_WQ_DESCS_MIN,
5362306a36Sopenharmony_ci					    c->wq_enet_desc_count));
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	c->wq_enet_desc_count = ALIGN(c->wq_enet_desc_count, 16);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	c->maxdatafieldsize = min_t(u32,
5862306a36Sopenharmony_ci				    VNIC_SNIC_MAXDATAFIELDSIZE_MAX,
5962306a36Sopenharmony_ci				    max_t(u32,
6062306a36Sopenharmony_ci					  VNIC_SNIC_MAXDATAFIELDSIZE_MIN,
6162306a36Sopenharmony_ci					  c->maxdatafieldsize));
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	c->io_throttle_count = min_t(u32,
6462306a36Sopenharmony_ci				     VNIC_SNIC_IO_THROTTLE_COUNT_MAX,
6562306a36Sopenharmony_ci				     max_t(u32,
6662306a36Sopenharmony_ci					   VNIC_SNIC_IO_THROTTLE_COUNT_MIN,
6762306a36Sopenharmony_ci					   c->io_throttle_count));
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	c->port_down_timeout = min_t(u32,
7062306a36Sopenharmony_ci				     VNIC_SNIC_PORT_DOWN_TIMEOUT_MAX,
7162306a36Sopenharmony_ci				     c->port_down_timeout);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	c->port_down_io_retries = min_t(u32,
7462306a36Sopenharmony_ci				     VNIC_SNIC_PORT_DOWN_IO_RETRIES_MAX,
7562306a36Sopenharmony_ci				     c->port_down_io_retries);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	c->luns_per_tgt = min_t(u32,
7862306a36Sopenharmony_ci				VNIC_SNIC_LUNS_PER_TARGET_MAX,
7962306a36Sopenharmony_ci				max_t(u32,
8062306a36Sopenharmony_ci				      VNIC_SNIC_LUNS_PER_TARGET_MIN,
8162306a36Sopenharmony_ci				      c->luns_per_tgt));
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	c->intr_timer = min_t(u32, VNIC_INTR_TIMER_MAX, c->intr_timer);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	SNIC_INFO("vNIC resources wq %d\n", c->wq_enet_desc_count);
8662306a36Sopenharmony_ci	SNIC_INFO("vNIC mtu %d intr timer %d\n",
8762306a36Sopenharmony_ci		  c->maxdatafieldsize,
8862306a36Sopenharmony_ci		  c->intr_timer);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	SNIC_INFO("vNIC flags 0x%x luns per tgt %d\n",
9162306a36Sopenharmony_ci		  c->flags,
9262306a36Sopenharmony_ci		  c->luns_per_tgt);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	SNIC_INFO("vNIC io throttle count %d\n", c->io_throttle_count);
9562306a36Sopenharmony_ci	SNIC_INFO("vNIC port down timeout %d port down io retries %d\n",
9662306a36Sopenharmony_ci		  c->port_down_timeout,
9762306a36Sopenharmony_ci		  c->port_down_io_retries);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	SNIC_INFO("vNIC back end type = %d\n", c->xpt_type);
10062306a36Sopenharmony_ci	SNIC_INFO("vNIC hid = %d\n", c->hid);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return 0;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_civoid
10662306a36Sopenharmony_cisnic_get_res_counts(struct snic *snic)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	snic->wq_count = svnic_dev_get_res_count(snic->vdev, RES_TYPE_WQ);
10962306a36Sopenharmony_ci	SNIC_BUG_ON(snic->wq_count == 0);
11062306a36Sopenharmony_ci	snic->cq_count = svnic_dev_get_res_count(snic->vdev, RES_TYPE_CQ);
11162306a36Sopenharmony_ci	SNIC_BUG_ON(snic->cq_count == 0);
11262306a36Sopenharmony_ci	snic->intr_count = svnic_dev_get_res_count(snic->vdev,
11362306a36Sopenharmony_ci						  RES_TYPE_INTR_CTRL);
11462306a36Sopenharmony_ci	SNIC_BUG_ON(snic->intr_count == 0);
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_civoid
11862306a36Sopenharmony_cisnic_free_vnic_res(struct snic *snic)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	unsigned int i;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	for (i = 0; i < snic->wq_count; i++)
12362306a36Sopenharmony_ci		svnic_wq_free(&snic->wq[i]);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	for (i = 0; i < snic->cq_count; i++)
12662306a36Sopenharmony_ci		svnic_cq_free(&snic->cq[i]);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	for (i = 0; i < snic->intr_count; i++)
12962306a36Sopenharmony_ci		svnic_intr_free(&snic->intr[i]);
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ciint
13362306a36Sopenharmony_cisnic_alloc_vnic_res(struct snic *snic)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	enum vnic_dev_intr_mode intr_mode;
13662306a36Sopenharmony_ci	unsigned int mask_on_assertion;
13762306a36Sopenharmony_ci	unsigned int intr_offset;
13862306a36Sopenharmony_ci	unsigned int err_intr_enable;
13962306a36Sopenharmony_ci	unsigned int err_intr_offset;
14062306a36Sopenharmony_ci	unsigned int i;
14162306a36Sopenharmony_ci	int ret;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	intr_mode = svnic_dev_get_intr_mode(snic->vdev);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	SNIC_INFO("vNIC interrupt mode: %s\n",
14662306a36Sopenharmony_ci		  ((intr_mode == VNIC_DEV_INTR_MODE_INTX) ?
14762306a36Sopenharmony_ci		   "Legacy PCI INTx" :
14862306a36Sopenharmony_ci		   ((intr_mode == VNIC_DEV_INTR_MODE_MSI) ?
14962306a36Sopenharmony_ci		    "MSI" :
15062306a36Sopenharmony_ci		    ((intr_mode == VNIC_DEV_INTR_MODE_MSIX) ?
15162306a36Sopenharmony_ci		     "MSI-X" : "Unknown"))));
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	/* only MSI-X is supported */
15462306a36Sopenharmony_ci	SNIC_BUG_ON(intr_mode != VNIC_DEV_INTR_MODE_MSIX);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	SNIC_INFO("wq %d cq %d intr %d\n", snic->wq_count,
15762306a36Sopenharmony_ci		  snic->cq_count,
15862306a36Sopenharmony_ci		  snic->intr_count);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/* Allocate WQs used for SCSI IOs */
16262306a36Sopenharmony_ci	for (i = 0; i < snic->wq_count; i++) {
16362306a36Sopenharmony_ci		ret = svnic_wq_alloc(snic->vdev,
16462306a36Sopenharmony_ci				     &snic->wq[i],
16562306a36Sopenharmony_ci				     i,
16662306a36Sopenharmony_ci				     snic->config.wq_enet_desc_count,
16762306a36Sopenharmony_ci				     sizeof(struct wq_enet_desc));
16862306a36Sopenharmony_ci		if (ret)
16962306a36Sopenharmony_ci			goto error_cleanup;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* CQ for each WQ */
17362306a36Sopenharmony_ci	for (i = 0; i < snic->wq_count; i++) {
17462306a36Sopenharmony_ci		ret = svnic_cq_alloc(snic->vdev,
17562306a36Sopenharmony_ci				     &snic->cq[i],
17662306a36Sopenharmony_ci				     i,
17762306a36Sopenharmony_ci				     snic->config.wq_enet_desc_count,
17862306a36Sopenharmony_ci				     sizeof(struct cq_enet_wq_desc));
17962306a36Sopenharmony_ci		if (ret)
18062306a36Sopenharmony_ci			goto error_cleanup;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	SNIC_BUG_ON(snic->cq_count != 2 * snic->wq_count);
18462306a36Sopenharmony_ci	/* CQ for FW TO host */
18562306a36Sopenharmony_ci	for (i = snic->wq_count; i < snic->cq_count; i++) {
18662306a36Sopenharmony_ci		ret = svnic_cq_alloc(snic->vdev,
18762306a36Sopenharmony_ci				     &snic->cq[i],
18862306a36Sopenharmony_ci				     i,
18962306a36Sopenharmony_ci				     (snic->config.wq_enet_desc_count * 3),
19062306a36Sopenharmony_ci				     sizeof(struct snic_fw_req));
19162306a36Sopenharmony_ci		if (ret)
19262306a36Sopenharmony_ci			goto error_cleanup;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	for (i = 0; i < snic->intr_count; i++) {
19662306a36Sopenharmony_ci		ret = svnic_intr_alloc(snic->vdev, &snic->intr[i], i);
19762306a36Sopenharmony_ci		if (ret)
19862306a36Sopenharmony_ci			goto error_cleanup;
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	/*
20262306a36Sopenharmony_ci	 * Init WQ Resources.
20362306a36Sopenharmony_ci	 * WQ[0 to n] points to CQ[0 to n-1]
20462306a36Sopenharmony_ci	 * firmware to host comm points to CQ[n to m+1]
20562306a36Sopenharmony_ci	 */
20662306a36Sopenharmony_ci	err_intr_enable = 1;
20762306a36Sopenharmony_ci	err_intr_offset = snic->err_intr_offset;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	for (i = 0; i < snic->wq_count; i++) {
21062306a36Sopenharmony_ci		svnic_wq_init(&snic->wq[i],
21162306a36Sopenharmony_ci			      i,
21262306a36Sopenharmony_ci			      err_intr_enable,
21362306a36Sopenharmony_ci			      err_intr_offset);
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	for (i = 0; i < snic->cq_count; i++) {
21762306a36Sopenharmony_ci		intr_offset = i;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		svnic_cq_init(&snic->cq[i],
22062306a36Sopenharmony_ci			      0 /* flow_control_enable */,
22162306a36Sopenharmony_ci			      1 /* color_enable */,
22262306a36Sopenharmony_ci			      0 /* cq_head */,
22362306a36Sopenharmony_ci			      0 /* cq_tail */,
22462306a36Sopenharmony_ci			      1 /* cq_tail_color */,
22562306a36Sopenharmony_ci			      1 /* interrupt_enable */,
22662306a36Sopenharmony_ci			      1 /* cq_entry_enable */,
22762306a36Sopenharmony_ci			      0 /* cq_message_enable */,
22862306a36Sopenharmony_ci			      intr_offset,
22962306a36Sopenharmony_ci			      0 /* cq_message_addr */);
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	/*
23362306a36Sopenharmony_ci	 * Init INTR resources
23462306a36Sopenharmony_ci	 * Assumption : snic is always in MSI-X mode
23562306a36Sopenharmony_ci	 */
23662306a36Sopenharmony_ci	SNIC_BUG_ON(intr_mode != VNIC_DEV_INTR_MODE_MSIX);
23762306a36Sopenharmony_ci	mask_on_assertion = 1;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	for (i = 0; i < snic->intr_count; i++) {
24062306a36Sopenharmony_ci		svnic_intr_init(&snic->intr[i],
24162306a36Sopenharmony_ci				snic->config.intr_timer,
24262306a36Sopenharmony_ci				snic->config.intr_timer_type,
24362306a36Sopenharmony_ci				mask_on_assertion);
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/* init the stats memory by making the first call here */
24762306a36Sopenharmony_ci	ret = svnic_dev_stats_dump(snic->vdev, &snic->stats);
24862306a36Sopenharmony_ci	if (ret) {
24962306a36Sopenharmony_ci		SNIC_HOST_ERR(snic->shost,
25062306a36Sopenharmony_ci			      "svnic_dev_stats_dump failed - x%x\n",
25162306a36Sopenharmony_ci			      ret);
25262306a36Sopenharmony_ci		goto error_cleanup;
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	/* Clear LIF stats */
25662306a36Sopenharmony_ci	svnic_dev_stats_clear(snic->vdev);
25762306a36Sopenharmony_ci	ret = 0;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	return ret;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cierror_cleanup:
26262306a36Sopenharmony_ci	snic_free_vnic_res(snic);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	return ret;
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_civoid
26862306a36Sopenharmony_cisnic_log_q_error(struct snic *snic)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	unsigned int i;
27162306a36Sopenharmony_ci	u32 err_status;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	for (i = 0; i < snic->wq_count; i++) {
27462306a36Sopenharmony_ci		err_status = ioread32(&snic->wq[i].ctrl->error_status);
27562306a36Sopenharmony_ci		if (err_status)
27662306a36Sopenharmony_ci			SNIC_HOST_ERR(snic->shost,
27762306a36Sopenharmony_ci				      "WQ[%d] error status %d\n",
27862306a36Sopenharmony_ci				      i,
27962306a36Sopenharmony_ci				      err_status);
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci} /* end of snic_log_q_error */
282