18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * This program is free software; you may redistribute it and/or modify
58c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by
68c2ecf20Sopenharmony_ci * the Free Software Foundation; version 2 of the License.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
98c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
108c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
118c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
128c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
138c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
148c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
158c2ecf20Sopenharmony_ci * SOFTWARE.
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/errno.h>
198c2ecf20Sopenharmony_ci#include <linux/types.h>
208c2ecf20Sopenharmony_ci#include <linux/pci.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "wq_enet_desc.h"
238c2ecf20Sopenharmony_ci#include "cq_enet_desc.h"
248c2ecf20Sopenharmony_ci#include "vnic_resource.h"
258c2ecf20Sopenharmony_ci#include "vnic_dev.h"
268c2ecf20Sopenharmony_ci#include "vnic_wq.h"
278c2ecf20Sopenharmony_ci#include "vnic_cq.h"
288c2ecf20Sopenharmony_ci#include "vnic_intr.h"
298c2ecf20Sopenharmony_ci#include "vnic_stats.h"
308c2ecf20Sopenharmony_ci#include "snic.h"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ciint
338c2ecf20Sopenharmony_cisnic_get_vnic_config(struct snic *snic)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	struct vnic_snic_config *c = &snic->config;
368c2ecf20Sopenharmony_ci	int ret;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define GET_CONFIG(m) \
398c2ecf20Sopenharmony_ci	do { \
408c2ecf20Sopenharmony_ci		ret = svnic_dev_spec(snic->vdev, \
418c2ecf20Sopenharmony_ci				     offsetof(struct vnic_snic_config, m), \
428c2ecf20Sopenharmony_ci				     sizeof(c->m), \
438c2ecf20Sopenharmony_ci				     &c->m); \
448c2ecf20Sopenharmony_ci		if (ret) { \
458c2ecf20Sopenharmony_ci			SNIC_HOST_ERR(snic->shost, \
468c2ecf20Sopenharmony_ci				      "Error getting %s, %d\n", #m, ret); \
478c2ecf20Sopenharmony_ci			return ret; \
488c2ecf20Sopenharmony_ci		} \
498c2ecf20Sopenharmony_ci	} while (0)
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	GET_CONFIG(wq_enet_desc_count);
528c2ecf20Sopenharmony_ci	GET_CONFIG(maxdatafieldsize);
538c2ecf20Sopenharmony_ci	GET_CONFIG(intr_timer);
548c2ecf20Sopenharmony_ci	GET_CONFIG(intr_timer_type);
558c2ecf20Sopenharmony_ci	GET_CONFIG(flags);
568c2ecf20Sopenharmony_ci	GET_CONFIG(io_throttle_count);
578c2ecf20Sopenharmony_ci	GET_CONFIG(port_down_timeout);
588c2ecf20Sopenharmony_ci	GET_CONFIG(port_down_io_retries);
598c2ecf20Sopenharmony_ci	GET_CONFIG(luns_per_tgt);
608c2ecf20Sopenharmony_ci	GET_CONFIG(xpt_type);
618c2ecf20Sopenharmony_ci	GET_CONFIG(hid);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	c->wq_enet_desc_count = min_t(u32,
648c2ecf20Sopenharmony_ci				      VNIC_SNIC_WQ_DESCS_MAX,
658c2ecf20Sopenharmony_ci				      max_t(u32,
668c2ecf20Sopenharmony_ci					    VNIC_SNIC_WQ_DESCS_MIN,
678c2ecf20Sopenharmony_ci					    c->wq_enet_desc_count));
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	c->wq_enet_desc_count = ALIGN(c->wq_enet_desc_count, 16);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	c->maxdatafieldsize = min_t(u32,
728c2ecf20Sopenharmony_ci				    VNIC_SNIC_MAXDATAFIELDSIZE_MAX,
738c2ecf20Sopenharmony_ci				    max_t(u32,
748c2ecf20Sopenharmony_ci					  VNIC_SNIC_MAXDATAFIELDSIZE_MIN,
758c2ecf20Sopenharmony_ci					  c->maxdatafieldsize));
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	c->io_throttle_count = min_t(u32,
788c2ecf20Sopenharmony_ci				     VNIC_SNIC_IO_THROTTLE_COUNT_MAX,
798c2ecf20Sopenharmony_ci				     max_t(u32,
808c2ecf20Sopenharmony_ci					   VNIC_SNIC_IO_THROTTLE_COUNT_MIN,
818c2ecf20Sopenharmony_ci					   c->io_throttle_count));
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	c->port_down_timeout = min_t(u32,
848c2ecf20Sopenharmony_ci				     VNIC_SNIC_PORT_DOWN_TIMEOUT_MAX,
858c2ecf20Sopenharmony_ci				     c->port_down_timeout);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	c->port_down_io_retries = min_t(u32,
888c2ecf20Sopenharmony_ci				     VNIC_SNIC_PORT_DOWN_IO_RETRIES_MAX,
898c2ecf20Sopenharmony_ci				     c->port_down_io_retries);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	c->luns_per_tgt = min_t(u32,
928c2ecf20Sopenharmony_ci				VNIC_SNIC_LUNS_PER_TARGET_MAX,
938c2ecf20Sopenharmony_ci				max_t(u32,
948c2ecf20Sopenharmony_ci				      VNIC_SNIC_LUNS_PER_TARGET_MIN,
958c2ecf20Sopenharmony_ci				      c->luns_per_tgt));
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	c->intr_timer = min_t(u32, VNIC_INTR_TIMER_MAX, c->intr_timer);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	SNIC_INFO("vNIC resources wq %d\n", c->wq_enet_desc_count);
1008c2ecf20Sopenharmony_ci	SNIC_INFO("vNIC mtu %d intr timer %d\n",
1018c2ecf20Sopenharmony_ci		  c->maxdatafieldsize,
1028c2ecf20Sopenharmony_ci		  c->intr_timer);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	SNIC_INFO("vNIC flags 0x%x luns per tgt %d\n",
1058c2ecf20Sopenharmony_ci		  c->flags,
1068c2ecf20Sopenharmony_ci		  c->luns_per_tgt);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	SNIC_INFO("vNIC io throttle count %d\n", c->io_throttle_count);
1098c2ecf20Sopenharmony_ci	SNIC_INFO("vNIC port down timeout %d port down io retries %d\n",
1108c2ecf20Sopenharmony_ci		  c->port_down_timeout,
1118c2ecf20Sopenharmony_ci		  c->port_down_io_retries);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	SNIC_INFO("vNIC back end type = %d\n", c->xpt_type);
1148c2ecf20Sopenharmony_ci	SNIC_INFO("vNIC hid = %d\n", c->hid);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	return 0;
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_civoid
1208c2ecf20Sopenharmony_cisnic_get_res_counts(struct snic *snic)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	snic->wq_count = svnic_dev_get_res_count(snic->vdev, RES_TYPE_WQ);
1238c2ecf20Sopenharmony_ci	SNIC_BUG_ON(snic->wq_count == 0);
1248c2ecf20Sopenharmony_ci	snic->cq_count = svnic_dev_get_res_count(snic->vdev, RES_TYPE_CQ);
1258c2ecf20Sopenharmony_ci	SNIC_BUG_ON(snic->cq_count == 0);
1268c2ecf20Sopenharmony_ci	snic->intr_count = svnic_dev_get_res_count(snic->vdev,
1278c2ecf20Sopenharmony_ci						  RES_TYPE_INTR_CTRL);
1288c2ecf20Sopenharmony_ci	SNIC_BUG_ON(snic->intr_count == 0);
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_civoid
1328c2ecf20Sopenharmony_cisnic_free_vnic_res(struct snic *snic)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	unsigned int i;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	for (i = 0; i < snic->wq_count; i++)
1378c2ecf20Sopenharmony_ci		svnic_wq_free(&snic->wq[i]);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	for (i = 0; i < snic->cq_count; i++)
1408c2ecf20Sopenharmony_ci		svnic_cq_free(&snic->cq[i]);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	for (i = 0; i < snic->intr_count; i++)
1438c2ecf20Sopenharmony_ci		svnic_intr_free(&snic->intr[i]);
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ciint
1478c2ecf20Sopenharmony_cisnic_alloc_vnic_res(struct snic *snic)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	enum vnic_dev_intr_mode intr_mode;
1508c2ecf20Sopenharmony_ci	unsigned int mask_on_assertion;
1518c2ecf20Sopenharmony_ci	unsigned int intr_offset;
1528c2ecf20Sopenharmony_ci	unsigned int err_intr_enable;
1538c2ecf20Sopenharmony_ci	unsigned int err_intr_offset;
1548c2ecf20Sopenharmony_ci	unsigned int i;
1558c2ecf20Sopenharmony_ci	int ret;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	intr_mode = svnic_dev_get_intr_mode(snic->vdev);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	SNIC_INFO("vNIC interrupt mode: %s\n",
1608c2ecf20Sopenharmony_ci		  ((intr_mode == VNIC_DEV_INTR_MODE_INTX) ?
1618c2ecf20Sopenharmony_ci		   "Legacy PCI INTx" :
1628c2ecf20Sopenharmony_ci		   ((intr_mode == VNIC_DEV_INTR_MODE_MSI) ?
1638c2ecf20Sopenharmony_ci		    "MSI" :
1648c2ecf20Sopenharmony_ci		    ((intr_mode == VNIC_DEV_INTR_MODE_MSIX) ?
1658c2ecf20Sopenharmony_ci		     "MSI-X" : "Unknown"))));
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	/* only MSI-X is supported */
1688c2ecf20Sopenharmony_ci	SNIC_BUG_ON(intr_mode != VNIC_DEV_INTR_MODE_MSIX);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	SNIC_INFO("wq %d cq %d intr %d\n", snic->wq_count,
1718c2ecf20Sopenharmony_ci		  snic->cq_count,
1728c2ecf20Sopenharmony_ci		  snic->intr_count);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/* Allocate WQs used for SCSI IOs */
1768c2ecf20Sopenharmony_ci	for (i = 0; i < snic->wq_count; i++) {
1778c2ecf20Sopenharmony_ci		ret = svnic_wq_alloc(snic->vdev,
1788c2ecf20Sopenharmony_ci				     &snic->wq[i],
1798c2ecf20Sopenharmony_ci				     i,
1808c2ecf20Sopenharmony_ci				     snic->config.wq_enet_desc_count,
1818c2ecf20Sopenharmony_ci				     sizeof(struct wq_enet_desc));
1828c2ecf20Sopenharmony_ci		if (ret)
1838c2ecf20Sopenharmony_ci			goto error_cleanup;
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	/* CQ for each WQ */
1878c2ecf20Sopenharmony_ci	for (i = 0; i < snic->wq_count; i++) {
1888c2ecf20Sopenharmony_ci		ret = svnic_cq_alloc(snic->vdev,
1898c2ecf20Sopenharmony_ci				     &snic->cq[i],
1908c2ecf20Sopenharmony_ci				     i,
1918c2ecf20Sopenharmony_ci				     snic->config.wq_enet_desc_count,
1928c2ecf20Sopenharmony_ci				     sizeof(struct cq_enet_wq_desc));
1938c2ecf20Sopenharmony_ci		if (ret)
1948c2ecf20Sopenharmony_ci			goto error_cleanup;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	SNIC_BUG_ON(snic->cq_count != 2 * snic->wq_count);
1988c2ecf20Sopenharmony_ci	/* CQ for FW TO host */
1998c2ecf20Sopenharmony_ci	for (i = snic->wq_count; i < snic->cq_count; i++) {
2008c2ecf20Sopenharmony_ci		ret = svnic_cq_alloc(snic->vdev,
2018c2ecf20Sopenharmony_ci				     &snic->cq[i],
2028c2ecf20Sopenharmony_ci				     i,
2038c2ecf20Sopenharmony_ci				     (snic->config.wq_enet_desc_count * 3),
2048c2ecf20Sopenharmony_ci				     sizeof(struct snic_fw_req));
2058c2ecf20Sopenharmony_ci		if (ret)
2068c2ecf20Sopenharmony_ci			goto error_cleanup;
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	for (i = 0; i < snic->intr_count; i++) {
2108c2ecf20Sopenharmony_ci		ret = svnic_intr_alloc(snic->vdev, &snic->intr[i], i);
2118c2ecf20Sopenharmony_ci		if (ret)
2128c2ecf20Sopenharmony_ci			goto error_cleanup;
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	/*
2168c2ecf20Sopenharmony_ci	 * Init WQ Resources.
2178c2ecf20Sopenharmony_ci	 * WQ[0 to n] points to CQ[0 to n-1]
2188c2ecf20Sopenharmony_ci	 * firmware to host comm points to CQ[n to m+1]
2198c2ecf20Sopenharmony_ci	 */
2208c2ecf20Sopenharmony_ci	err_intr_enable = 1;
2218c2ecf20Sopenharmony_ci	err_intr_offset = snic->err_intr_offset;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	for (i = 0; i < snic->wq_count; i++) {
2248c2ecf20Sopenharmony_ci		svnic_wq_init(&snic->wq[i],
2258c2ecf20Sopenharmony_ci			      i,
2268c2ecf20Sopenharmony_ci			      err_intr_enable,
2278c2ecf20Sopenharmony_ci			      err_intr_offset);
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	for (i = 0; i < snic->cq_count; i++) {
2318c2ecf20Sopenharmony_ci		intr_offset = i;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci		svnic_cq_init(&snic->cq[i],
2348c2ecf20Sopenharmony_ci			      0 /* flow_control_enable */,
2358c2ecf20Sopenharmony_ci			      1 /* color_enable */,
2368c2ecf20Sopenharmony_ci			      0 /* cq_head */,
2378c2ecf20Sopenharmony_ci			      0 /* cq_tail */,
2388c2ecf20Sopenharmony_ci			      1 /* cq_tail_color */,
2398c2ecf20Sopenharmony_ci			      1 /* interrupt_enable */,
2408c2ecf20Sopenharmony_ci			      1 /* cq_entry_enable */,
2418c2ecf20Sopenharmony_ci			      0 /* cq_message_enable */,
2428c2ecf20Sopenharmony_ci			      intr_offset,
2438c2ecf20Sopenharmony_ci			      0 /* cq_message_addr */);
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	/*
2478c2ecf20Sopenharmony_ci	 * Init INTR resources
2488c2ecf20Sopenharmony_ci	 * Assumption : snic is always in MSI-X mode
2498c2ecf20Sopenharmony_ci	 */
2508c2ecf20Sopenharmony_ci	SNIC_BUG_ON(intr_mode != VNIC_DEV_INTR_MODE_MSIX);
2518c2ecf20Sopenharmony_ci	mask_on_assertion = 1;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	for (i = 0; i < snic->intr_count; i++) {
2548c2ecf20Sopenharmony_ci		svnic_intr_init(&snic->intr[i],
2558c2ecf20Sopenharmony_ci				snic->config.intr_timer,
2568c2ecf20Sopenharmony_ci				snic->config.intr_timer_type,
2578c2ecf20Sopenharmony_ci				mask_on_assertion);
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	/* init the stats memory by making the first call here */
2618c2ecf20Sopenharmony_ci	ret = svnic_dev_stats_dump(snic->vdev, &snic->stats);
2628c2ecf20Sopenharmony_ci	if (ret) {
2638c2ecf20Sopenharmony_ci		SNIC_HOST_ERR(snic->shost,
2648c2ecf20Sopenharmony_ci			      "svnic_dev_stats_dump failed - x%x\n",
2658c2ecf20Sopenharmony_ci			      ret);
2668c2ecf20Sopenharmony_ci		goto error_cleanup;
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	/* Clear LIF stats */
2708c2ecf20Sopenharmony_ci	svnic_dev_stats_clear(snic->vdev);
2718c2ecf20Sopenharmony_ci	ret = 0;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return ret;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cierror_cleanup:
2768c2ecf20Sopenharmony_ci	snic_free_vnic_res(snic);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	return ret;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_civoid
2828c2ecf20Sopenharmony_cisnic_log_q_error(struct snic *snic)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	unsigned int i;
2858c2ecf20Sopenharmony_ci	u32 err_status;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	for (i = 0; i < snic->wq_count; i++) {
2888c2ecf20Sopenharmony_ci		err_status = ioread32(&snic->wq[i].ctrl->error_status);
2898c2ecf20Sopenharmony_ci		if (err_status)
2908c2ecf20Sopenharmony_ci			SNIC_HOST_ERR(snic->shost,
2918c2ecf20Sopenharmony_ci				      "WQ[%d] error status %d\n",
2928c2ecf20Sopenharmony_ci				      i,
2938c2ecf20Sopenharmony_ci				      err_status);
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci} /* end of snic_log_q_error */
296