18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  QLogic FCoE Offload Driver
48c2ecf20Sopenharmony_ci *  Copyright (c) 2016-2018 Cavium Inc.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci#include <linux/init.h>
78c2ecf20Sopenharmony_ci#include <linux/kernel.h>
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/pci.h>
108c2ecf20Sopenharmony_ci#include <linux/device.h>
118c2ecf20Sopenharmony_ci#include <linux/highmem.h>
128c2ecf20Sopenharmony_ci#include <linux/crc32.h>
138c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
148c2ecf20Sopenharmony_ci#include <linux/list.h>
158c2ecf20Sopenharmony_ci#include <linux/kthread.h>
168c2ecf20Sopenharmony_ci#include <linux/phylink.h>
178c2ecf20Sopenharmony_ci#include <scsi/libfc.h>
188c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
198c2ecf20Sopenharmony_ci#include <scsi/fc_frame.h>
208c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
218c2ecf20Sopenharmony_ci#include <linux/if_vlan.h>
228c2ecf20Sopenharmony_ci#include <linux/cpu.h>
238c2ecf20Sopenharmony_ci#include "qedf.h"
248c2ecf20Sopenharmony_ci#include "qedf_dbg.h"
258c2ecf20Sopenharmony_ci#include <uapi/linux/pci_regs.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ciconst struct qed_fcoe_ops *qed_ops;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic int qedf_probe(struct pci_dev *pdev, const struct pci_device_id *id);
308c2ecf20Sopenharmony_cistatic void qedf_remove(struct pci_dev *pdev);
318c2ecf20Sopenharmony_cistatic void qedf_shutdown(struct pci_dev *pdev);
328c2ecf20Sopenharmony_cistatic void qedf_schedule_recovery_handler(void *dev);
338c2ecf20Sopenharmony_cistatic void qedf_recovery_handler(struct work_struct *work);
348c2ecf20Sopenharmony_cistatic int qedf_suspend(struct pci_dev *pdev, pm_message_t state);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/*
378c2ecf20Sopenharmony_ci * Driver module parameters.
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_cistatic unsigned int qedf_dev_loss_tmo = 60;
408c2ecf20Sopenharmony_cimodule_param_named(dev_loss_tmo, qedf_dev_loss_tmo, int, S_IRUGO);
418c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dev_loss_tmo,  " dev_loss_tmo setting for attached "
428c2ecf20Sopenharmony_ci	"remote ports (default 60)");
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ciuint qedf_debug = QEDF_LOG_INFO;
458c2ecf20Sopenharmony_cimodule_param_named(debug, qedf_debug, uint, S_IRUGO|S_IWUSR);
468c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, " Debug mask. Pass '1' to enable default debugging"
478c2ecf20Sopenharmony_ci	" mask");
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic uint qedf_fipvlan_retries = 60;
508c2ecf20Sopenharmony_cimodule_param_named(fipvlan_retries, qedf_fipvlan_retries, int, S_IRUGO);
518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fipvlan_retries, " Number of FIP VLAN requests to attempt "
528c2ecf20Sopenharmony_ci	"before giving up (default 60)");
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic uint qedf_fallback_vlan = QEDF_FALLBACK_VLAN;
558c2ecf20Sopenharmony_cimodule_param_named(fallback_vlan, qedf_fallback_vlan, int, S_IRUGO);
568c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fallback_vlan, " VLAN ID to try if fip vlan request fails "
578c2ecf20Sopenharmony_ci	"(default 1002).");
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic int qedf_default_prio = -1;
608c2ecf20Sopenharmony_cimodule_param_named(default_prio, qedf_default_prio, int, S_IRUGO);
618c2ecf20Sopenharmony_ciMODULE_PARM_DESC(default_prio, " Override 802.1q priority for FIP and FCoE"
628c2ecf20Sopenharmony_ci	" traffic (value between 0 and 7, default 3).");
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ciuint qedf_dump_frames;
658c2ecf20Sopenharmony_cimodule_param_named(dump_frames, qedf_dump_frames, int, S_IRUGO | S_IWUSR);
668c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dump_frames, " Print the skb data of FIP and FCoE frames "
678c2ecf20Sopenharmony_ci	"(default off)");
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic uint qedf_queue_depth;
708c2ecf20Sopenharmony_cimodule_param_named(queue_depth, qedf_queue_depth, int, S_IRUGO);
718c2ecf20Sopenharmony_ciMODULE_PARM_DESC(queue_depth, " Sets the queue depth for all LUNs discovered "
728c2ecf20Sopenharmony_ci	"by the qedf driver. Default is 0 (use OS default).");
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ciuint qedf_io_tracing;
758c2ecf20Sopenharmony_cimodule_param_named(io_tracing, qedf_io_tracing, int, S_IRUGO | S_IWUSR);
768c2ecf20Sopenharmony_ciMODULE_PARM_DESC(io_tracing, " Enable logging of SCSI requests/completions "
778c2ecf20Sopenharmony_ci	"into trace buffer. (default off).");
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic uint qedf_max_lun = MAX_FIBRE_LUNS;
808c2ecf20Sopenharmony_cimodule_param_named(max_lun, qedf_max_lun, int, S_IRUGO);
818c2ecf20Sopenharmony_ciMODULE_PARM_DESC(max_lun, " Sets the maximum luns per target that the driver "
828c2ecf20Sopenharmony_ci	"supports. (default 0xffffffff)");
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ciuint qedf_link_down_tmo;
858c2ecf20Sopenharmony_cimodule_param_named(link_down_tmo, qedf_link_down_tmo, int, S_IRUGO);
868c2ecf20Sopenharmony_ciMODULE_PARM_DESC(link_down_tmo, " Delays informing the fcoe transport that the "
878c2ecf20Sopenharmony_ci	"link is down by N seconds.");
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cibool qedf_retry_delay;
908c2ecf20Sopenharmony_cimodule_param_named(retry_delay, qedf_retry_delay, bool, S_IRUGO | S_IWUSR);
918c2ecf20Sopenharmony_ciMODULE_PARM_DESC(retry_delay, " Enable/disable handling of FCP_RSP IU retry "
928c2ecf20Sopenharmony_ci	"delay handling (default off).");
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic bool qedf_dcbx_no_wait;
958c2ecf20Sopenharmony_cimodule_param_named(dcbx_no_wait, qedf_dcbx_no_wait, bool, S_IRUGO | S_IWUSR);
968c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dcbx_no_wait, " Do not wait for DCBX convergence to start "
978c2ecf20Sopenharmony_ci	"sending FIP VLAN requests on link up (Default: off).");
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic uint qedf_dp_module;
1008c2ecf20Sopenharmony_cimodule_param_named(dp_module, qedf_dp_module, uint, S_IRUGO);
1018c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dp_module, " bit flags control for verbose printk passed "
1028c2ecf20Sopenharmony_ci	"qed module during probe.");
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic uint qedf_dp_level = QED_LEVEL_NOTICE;
1058c2ecf20Sopenharmony_cimodule_param_named(dp_level, qedf_dp_level, uint, S_IRUGO);
1068c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dp_level, " printk verbosity control passed to qed module  "
1078c2ecf20Sopenharmony_ci	"during probe (0-3: 0 more verbose).");
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic bool qedf_enable_recovery = true;
1108c2ecf20Sopenharmony_cimodule_param_named(enable_recovery, qedf_enable_recovery,
1118c2ecf20Sopenharmony_ci		bool, S_IRUGO | S_IWUSR);
1128c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable_recovery, "Enable/disable recovery on driver/firmware "
1138c2ecf20Sopenharmony_ci		"interface level errors 0 = Disabled, 1 = Enabled (Default: 1).");
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistruct workqueue_struct *qedf_io_wq;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic struct fcoe_percpu_s qedf_global;
1188c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(qedf_global_lock);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic struct kmem_cache *qedf_io_work_cache;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_civoid qedf_set_vlan_id(struct qedf_ctx *qedf, int vlan_id)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	int vlan_id_tmp = 0;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	vlan_id_tmp = vlan_id  | (qedf->prio << VLAN_PRIO_SHIFT);
1278c2ecf20Sopenharmony_ci	qedf->vlan_id = vlan_id_tmp;
1288c2ecf20Sopenharmony_ci	QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
1298c2ecf20Sopenharmony_ci		  "Setting vlan_id=0x%04x prio=%d.\n",
1308c2ecf20Sopenharmony_ci		  vlan_id_tmp, qedf->prio);
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci/* Returns true if we have a valid vlan, false otherwise */
1348c2ecf20Sopenharmony_cistatic bool qedf_initiate_fipvlan_req(struct qedf_ctx *qedf)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	while (qedf->fipvlan_retries--) {
1388c2ecf20Sopenharmony_ci		/* This is to catch if link goes down during fipvlan retries */
1398c2ecf20Sopenharmony_ci		if (atomic_read(&qedf->link_state) == QEDF_LINK_DOWN) {
1408c2ecf20Sopenharmony_ci			QEDF_ERR(&qedf->dbg_ctx, "Link not up.\n");
1418c2ecf20Sopenharmony_ci			return false;
1428c2ecf20Sopenharmony_ci		}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci		if (test_bit(QEDF_UNLOADING, &qedf->flags)) {
1458c2ecf20Sopenharmony_ci			QEDF_ERR(&qedf->dbg_ctx, "Driver unloading.\n");
1468c2ecf20Sopenharmony_ci			return false;
1478c2ecf20Sopenharmony_ci		}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci		if (qedf->vlan_id > 0) {
1508c2ecf20Sopenharmony_ci			QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
1518c2ecf20Sopenharmony_ci				  "vlan = 0x%x already set, calling ctlr_link_up.\n",
1528c2ecf20Sopenharmony_ci				  qedf->vlan_id);
1538c2ecf20Sopenharmony_ci			if (atomic_read(&qedf->link_state) == QEDF_LINK_UP)
1548c2ecf20Sopenharmony_ci				fcoe_ctlr_link_up(&qedf->ctlr);
1558c2ecf20Sopenharmony_ci			return true;
1568c2ecf20Sopenharmony_ci		}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
1598c2ecf20Sopenharmony_ci			   "Retry %d.\n", qedf->fipvlan_retries);
1608c2ecf20Sopenharmony_ci		init_completion(&qedf->fipvlan_compl);
1618c2ecf20Sopenharmony_ci		qedf_fcoe_send_vlan_req(qedf);
1628c2ecf20Sopenharmony_ci		wait_for_completion_timeout(&qedf->fipvlan_compl, 1 * HZ);
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	return false;
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic void qedf_handle_link_update(struct work_struct *work)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf =
1718c2ecf20Sopenharmony_ci	    container_of(work, struct qedf_ctx, link_update.work);
1728c2ecf20Sopenharmony_ci	int rc;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, "Entered. link_state=%d.\n",
1758c2ecf20Sopenharmony_ci		  atomic_read(&qedf->link_state));
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (atomic_read(&qedf->link_state) == QEDF_LINK_UP) {
1788c2ecf20Sopenharmony_ci		rc = qedf_initiate_fipvlan_req(qedf);
1798c2ecf20Sopenharmony_ci		if (rc)
1808c2ecf20Sopenharmony_ci			return;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci		if (atomic_read(&qedf->link_state) != QEDF_LINK_UP) {
1838c2ecf20Sopenharmony_ci			QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
1848c2ecf20Sopenharmony_ci				  "Link is down, resetting vlan_id.\n");
1858c2ecf20Sopenharmony_ci			qedf->vlan_id = 0;
1868c2ecf20Sopenharmony_ci			return;
1878c2ecf20Sopenharmony_ci		}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci		/*
1908c2ecf20Sopenharmony_ci		 * If we get here then we never received a repsonse to our
1918c2ecf20Sopenharmony_ci		 * fip vlan request so set the vlan_id to the default and
1928c2ecf20Sopenharmony_ci		 * tell FCoE that the link is up
1938c2ecf20Sopenharmony_ci		 */
1948c2ecf20Sopenharmony_ci		QEDF_WARN(&(qedf->dbg_ctx), "Did not receive FIP VLAN "
1958c2ecf20Sopenharmony_ci			   "response, falling back to default VLAN %d.\n",
1968c2ecf20Sopenharmony_ci			   qedf_fallback_vlan);
1978c2ecf20Sopenharmony_ci		qedf_set_vlan_id(qedf, qedf_fallback_vlan);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci		/*
2008c2ecf20Sopenharmony_ci		 * Zero out data_src_addr so we'll update it with the new
2018c2ecf20Sopenharmony_ci		 * lport port_id
2028c2ecf20Sopenharmony_ci		 */
2038c2ecf20Sopenharmony_ci		eth_zero_addr(qedf->data_src_addr);
2048c2ecf20Sopenharmony_ci		fcoe_ctlr_link_up(&qedf->ctlr);
2058c2ecf20Sopenharmony_ci	} else if (atomic_read(&qedf->link_state) == QEDF_LINK_DOWN) {
2068c2ecf20Sopenharmony_ci		/*
2078c2ecf20Sopenharmony_ci		 * If we hit here and link_down_tmo_valid is still 1 it means
2088c2ecf20Sopenharmony_ci		 * that link_down_tmo timed out so set it to 0 to make sure any
2098c2ecf20Sopenharmony_ci		 * other readers have accurate state.
2108c2ecf20Sopenharmony_ci		 */
2118c2ecf20Sopenharmony_ci		atomic_set(&qedf->link_down_tmo_valid, 0);
2128c2ecf20Sopenharmony_ci		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
2138c2ecf20Sopenharmony_ci		    "Calling fcoe_ctlr_link_down().\n");
2148c2ecf20Sopenharmony_ci		fcoe_ctlr_link_down(&qedf->ctlr);
2158c2ecf20Sopenharmony_ci		if (qedf_wait_for_upload(qedf) == false)
2168c2ecf20Sopenharmony_ci			QEDF_ERR(&qedf->dbg_ctx,
2178c2ecf20Sopenharmony_ci				 "Could not upload all sessions.\n");
2188c2ecf20Sopenharmony_ci		/* Reset the number of FIP VLAN retries */
2198c2ecf20Sopenharmony_ci		qedf->fipvlan_retries = qedf_fipvlan_retries;
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci#define	QEDF_FCOE_MAC_METHOD_GRANGED_MAC		1
2248c2ecf20Sopenharmony_ci#define QEDF_FCOE_MAC_METHOD_FCF_MAP			2
2258c2ecf20Sopenharmony_ci#define QEDF_FCOE_MAC_METHOD_FCOE_SET_MAC		3
2268c2ecf20Sopenharmony_cistatic void qedf_set_data_src_addr(struct qedf_ctx *qedf, struct fc_frame *fp)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	u8 *granted_mac;
2298c2ecf20Sopenharmony_ci	struct fc_frame_header *fh = fc_frame_header_get(fp);
2308c2ecf20Sopenharmony_ci	u8 fc_map[3];
2318c2ecf20Sopenharmony_ci	int method = 0;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/* Get granted MAC address from FIP FLOGI payload */
2348c2ecf20Sopenharmony_ci	granted_mac = fr_cb(fp)->granted_mac;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	/*
2378c2ecf20Sopenharmony_ci	 * We set the source MAC for FCoE traffic based on the Granted MAC
2388c2ecf20Sopenharmony_ci	 * address from the switch.
2398c2ecf20Sopenharmony_ci	 *
2408c2ecf20Sopenharmony_ci	 * If granted_mac is non-zero, we used that.
2418c2ecf20Sopenharmony_ci	 * If the granted_mac is zeroed out, created the FCoE MAC based on
2428c2ecf20Sopenharmony_ci	 * the sel_fcf->fc_map and the d_id fo the FLOGI frame.
2438c2ecf20Sopenharmony_ci	 * If sel_fcf->fc_map is 0 then we use the default FCF-MAC plus the
2448c2ecf20Sopenharmony_ci	 * d_id of the FLOGI frame.
2458c2ecf20Sopenharmony_ci	 */
2468c2ecf20Sopenharmony_ci	if (!is_zero_ether_addr(granted_mac)) {
2478c2ecf20Sopenharmony_ci		ether_addr_copy(qedf->data_src_addr, granted_mac);
2488c2ecf20Sopenharmony_ci		method = QEDF_FCOE_MAC_METHOD_GRANGED_MAC;
2498c2ecf20Sopenharmony_ci	} else if (qedf->ctlr.sel_fcf->fc_map != 0) {
2508c2ecf20Sopenharmony_ci		hton24(fc_map, qedf->ctlr.sel_fcf->fc_map);
2518c2ecf20Sopenharmony_ci		qedf->data_src_addr[0] = fc_map[0];
2528c2ecf20Sopenharmony_ci		qedf->data_src_addr[1] = fc_map[1];
2538c2ecf20Sopenharmony_ci		qedf->data_src_addr[2] = fc_map[2];
2548c2ecf20Sopenharmony_ci		qedf->data_src_addr[3] = fh->fh_d_id[0];
2558c2ecf20Sopenharmony_ci		qedf->data_src_addr[4] = fh->fh_d_id[1];
2568c2ecf20Sopenharmony_ci		qedf->data_src_addr[5] = fh->fh_d_id[2];
2578c2ecf20Sopenharmony_ci		method = QEDF_FCOE_MAC_METHOD_FCF_MAP;
2588c2ecf20Sopenharmony_ci	} else {
2598c2ecf20Sopenharmony_ci		fc_fcoe_set_mac(qedf->data_src_addr, fh->fh_d_id);
2608c2ecf20Sopenharmony_ci		method = QEDF_FCOE_MAC_METHOD_FCOE_SET_MAC;
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
2648c2ecf20Sopenharmony_ci	    "QEDF data_src_mac=%pM method=%d.\n", qedf->data_src_addr, method);
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic void qedf_flogi_resp(struct fc_seq *seq, struct fc_frame *fp,
2688c2ecf20Sopenharmony_ci	void *arg)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	struct fc_exch *exch = fc_seq_exch(seq);
2718c2ecf20Sopenharmony_ci	struct fc_lport *lport = exch->lp;
2728c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf = lport_priv(lport);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (!qedf) {
2758c2ecf20Sopenharmony_ci		QEDF_ERR(NULL, "qedf is NULL.\n");
2768c2ecf20Sopenharmony_ci		return;
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	/*
2808c2ecf20Sopenharmony_ci	 * If ERR_PTR is set then don't try to stat anything as it will cause
2818c2ecf20Sopenharmony_ci	 * a crash when we access fp.
2828c2ecf20Sopenharmony_ci	 */
2838c2ecf20Sopenharmony_ci	if (IS_ERR(fp)) {
2848c2ecf20Sopenharmony_ci		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS,
2858c2ecf20Sopenharmony_ci		    "fp has IS_ERR() set.\n");
2868c2ecf20Sopenharmony_ci		goto skip_stat;
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	/* Log stats for FLOGI reject */
2908c2ecf20Sopenharmony_ci	if (fc_frame_payload_op(fp) == ELS_LS_RJT)
2918c2ecf20Sopenharmony_ci		qedf->flogi_failed++;
2928c2ecf20Sopenharmony_ci	else if (fc_frame_payload_op(fp) == ELS_LS_ACC) {
2938c2ecf20Sopenharmony_ci		/* Set the source MAC we will use for FCoE traffic */
2948c2ecf20Sopenharmony_ci		qedf_set_data_src_addr(qedf, fp);
2958c2ecf20Sopenharmony_ci		qedf->flogi_pending = 0;
2968c2ecf20Sopenharmony_ci	}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	/* Complete flogi_compl so we can proceed to sending ADISCs */
2998c2ecf20Sopenharmony_ci	complete(&qedf->flogi_compl);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ciskip_stat:
3028c2ecf20Sopenharmony_ci	/* Report response to libfc */
3038c2ecf20Sopenharmony_ci	fc_lport_flogi_resp(seq, fp, lport);
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic struct fc_seq *qedf_elsct_send(struct fc_lport *lport, u32 did,
3078c2ecf20Sopenharmony_ci	struct fc_frame *fp, unsigned int op,
3088c2ecf20Sopenharmony_ci	void (*resp)(struct fc_seq *,
3098c2ecf20Sopenharmony_ci	struct fc_frame *,
3108c2ecf20Sopenharmony_ci	void *),
3118c2ecf20Sopenharmony_ci	void *arg, u32 timeout)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf = lport_priv(lport);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	/*
3168c2ecf20Sopenharmony_ci	 * Intercept FLOGI for statistic purposes. Note we use the resp
3178c2ecf20Sopenharmony_ci	 * callback to tell if this is really a flogi.
3188c2ecf20Sopenharmony_ci	 */
3198c2ecf20Sopenharmony_ci	if (resp == fc_lport_flogi_resp) {
3208c2ecf20Sopenharmony_ci		qedf->flogi_cnt++;
3218c2ecf20Sopenharmony_ci		if (qedf->flogi_pending >= QEDF_FLOGI_RETRY_CNT) {
3228c2ecf20Sopenharmony_ci			schedule_delayed_work(&qedf->stag_work, 2);
3238c2ecf20Sopenharmony_ci			return NULL;
3248c2ecf20Sopenharmony_ci		}
3258c2ecf20Sopenharmony_ci		qedf->flogi_pending++;
3268c2ecf20Sopenharmony_ci		return fc_elsct_send(lport, did, fp, op, qedf_flogi_resp,
3278c2ecf20Sopenharmony_ci		    arg, timeout);
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	return fc_elsct_send(lport, did, fp, op, resp, arg, timeout);
3318c2ecf20Sopenharmony_ci}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ciint qedf_send_flogi(struct qedf_ctx *qedf)
3348c2ecf20Sopenharmony_ci{
3358c2ecf20Sopenharmony_ci	struct fc_lport *lport;
3368c2ecf20Sopenharmony_ci	struct fc_frame *fp;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	lport = qedf->lport;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	if (!lport->tt.elsct_send) {
3418c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx, "tt.elsct_send not set.\n");
3428c2ecf20Sopenharmony_ci		return -EINVAL;
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
3468c2ecf20Sopenharmony_ci	if (!fp) {
3478c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "fc_frame_alloc failed.\n");
3488c2ecf20Sopenharmony_ci		return -ENOMEM;
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS,
3528c2ecf20Sopenharmony_ci	    "Sending FLOGI to reestablish session with switch.\n");
3538c2ecf20Sopenharmony_ci	lport->tt.elsct_send(lport, FC_FID_FLOGI, fp,
3548c2ecf20Sopenharmony_ci	    ELS_FLOGI, qedf_flogi_resp, lport, lport->r_a_tov);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	init_completion(&qedf->flogi_compl);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	return 0;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci/*
3628c2ecf20Sopenharmony_ci * This function is called if link_down_tmo is in use.  If we get a link up and
3638c2ecf20Sopenharmony_ci * link_down_tmo has not expired then use just FLOGI/ADISC to recover our
3648c2ecf20Sopenharmony_ci * sessions with targets.  Otherwise, just call fcoe_ctlr_link_up().
3658c2ecf20Sopenharmony_ci */
3668c2ecf20Sopenharmony_cistatic void qedf_link_recovery(struct work_struct *work)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf =
3698c2ecf20Sopenharmony_ci	    container_of(work, struct qedf_ctx, link_recovery.work);
3708c2ecf20Sopenharmony_ci	struct fc_lport *lport = qedf->lport;
3718c2ecf20Sopenharmony_ci	struct fc_rport_priv *rdata;
3728c2ecf20Sopenharmony_ci	bool rc;
3738c2ecf20Sopenharmony_ci	int retries = 30;
3748c2ecf20Sopenharmony_ci	int rval, i;
3758c2ecf20Sopenharmony_ci	struct list_head rdata_login_list;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&rdata_login_list);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
3808c2ecf20Sopenharmony_ci	    "Link down tmo did not expire.\n");
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	/*
3838c2ecf20Sopenharmony_ci	 * Essentially reset the fcoe_ctlr here without affecting the state
3848c2ecf20Sopenharmony_ci	 * of the libfc structs.
3858c2ecf20Sopenharmony_ci	 */
3868c2ecf20Sopenharmony_ci	qedf->ctlr.state = FIP_ST_LINK_WAIT;
3878c2ecf20Sopenharmony_ci	fcoe_ctlr_link_down(&qedf->ctlr);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	/*
3908c2ecf20Sopenharmony_ci	 * Bring the link up before we send the fipvlan request so libfcoe
3918c2ecf20Sopenharmony_ci	 * can select a new fcf in parallel
3928c2ecf20Sopenharmony_ci	 */
3938c2ecf20Sopenharmony_ci	fcoe_ctlr_link_up(&qedf->ctlr);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	/* Since the link when down and up to verify which vlan we're on */
3968c2ecf20Sopenharmony_ci	qedf->fipvlan_retries = qedf_fipvlan_retries;
3978c2ecf20Sopenharmony_ci	rc = qedf_initiate_fipvlan_req(qedf);
3988c2ecf20Sopenharmony_ci	/* If getting the VLAN fails, set the VLAN to the fallback one */
3998c2ecf20Sopenharmony_ci	if (!rc)
4008c2ecf20Sopenharmony_ci		qedf_set_vlan_id(qedf, qedf_fallback_vlan);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	/*
4038c2ecf20Sopenharmony_ci	 * We need to wait for an FCF to be selected due to the
4048c2ecf20Sopenharmony_ci	 * fcoe_ctlr_link_up other the FLOGI will be rejected.
4058c2ecf20Sopenharmony_ci	 */
4068c2ecf20Sopenharmony_ci	while (retries > 0) {
4078c2ecf20Sopenharmony_ci		if (qedf->ctlr.sel_fcf) {
4088c2ecf20Sopenharmony_ci			QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
4098c2ecf20Sopenharmony_ci			    "FCF reselected, proceeding with FLOGI.\n");
4108c2ecf20Sopenharmony_ci			break;
4118c2ecf20Sopenharmony_ci		}
4128c2ecf20Sopenharmony_ci		msleep(500);
4138c2ecf20Sopenharmony_ci		retries--;
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	if (retries < 1) {
4178c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "Exhausted retries waiting for "
4188c2ecf20Sopenharmony_ci		    "FCF selection.\n");
4198c2ecf20Sopenharmony_ci		return;
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	rval = qedf_send_flogi(qedf);
4238c2ecf20Sopenharmony_ci	if (rval)
4248c2ecf20Sopenharmony_ci		return;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	/* Wait for FLOGI completion before proceeding with sending ADISCs */
4278c2ecf20Sopenharmony_ci	i = wait_for_completion_timeout(&qedf->flogi_compl,
4288c2ecf20Sopenharmony_ci	    qedf->lport->r_a_tov);
4298c2ecf20Sopenharmony_ci	if (i == 0) {
4308c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "FLOGI timed out.\n");
4318c2ecf20Sopenharmony_ci		return;
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	/*
4358c2ecf20Sopenharmony_ci	 * Call lport->tt.rport_login which will cause libfc to send an
4368c2ecf20Sopenharmony_ci	 * ADISC since the rport is in state ready.
4378c2ecf20Sopenharmony_ci	 */
4388c2ecf20Sopenharmony_ci	mutex_lock(&lport->disc.disc_mutex);
4398c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(rdata, &lport->disc.rports, peers) {
4408c2ecf20Sopenharmony_ci		if (kref_get_unless_zero(&rdata->kref)) {
4418c2ecf20Sopenharmony_ci			fc_rport_login(rdata);
4428c2ecf20Sopenharmony_ci			kref_put(&rdata->kref, fc_rport_destroy);
4438c2ecf20Sopenharmony_ci		}
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci	mutex_unlock(&lport->disc.disc_mutex);
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cistatic void qedf_update_link_speed(struct qedf_ctx *qedf,
4498c2ecf20Sopenharmony_ci	struct qed_link_output *link)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	__ETHTOOL_DECLARE_LINK_MODE_MASK(sup_caps);
4528c2ecf20Sopenharmony_ci	struct fc_lport *lport = qedf->lport;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	lport->link_speed = FC_PORTSPEED_UNKNOWN;
4558c2ecf20Sopenharmony_ci	lport->link_supported_speeds = FC_PORTSPEED_UNKNOWN;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	/* Set fc_host link speed */
4588c2ecf20Sopenharmony_ci	switch (link->speed) {
4598c2ecf20Sopenharmony_ci	case 10000:
4608c2ecf20Sopenharmony_ci		lport->link_speed = FC_PORTSPEED_10GBIT;
4618c2ecf20Sopenharmony_ci		break;
4628c2ecf20Sopenharmony_ci	case 25000:
4638c2ecf20Sopenharmony_ci		lport->link_speed = FC_PORTSPEED_25GBIT;
4648c2ecf20Sopenharmony_ci		break;
4658c2ecf20Sopenharmony_ci	case 40000:
4668c2ecf20Sopenharmony_ci		lport->link_speed = FC_PORTSPEED_40GBIT;
4678c2ecf20Sopenharmony_ci		break;
4688c2ecf20Sopenharmony_ci	case 50000:
4698c2ecf20Sopenharmony_ci		lport->link_speed = FC_PORTSPEED_50GBIT;
4708c2ecf20Sopenharmony_ci		break;
4718c2ecf20Sopenharmony_ci	case 100000:
4728c2ecf20Sopenharmony_ci		lport->link_speed = FC_PORTSPEED_100GBIT;
4738c2ecf20Sopenharmony_ci		break;
4748c2ecf20Sopenharmony_ci	case 20000:
4758c2ecf20Sopenharmony_ci		lport->link_speed = FC_PORTSPEED_20GBIT;
4768c2ecf20Sopenharmony_ci		break;
4778c2ecf20Sopenharmony_ci	default:
4788c2ecf20Sopenharmony_ci		lport->link_speed = FC_PORTSPEED_UNKNOWN;
4798c2ecf20Sopenharmony_ci		break;
4808c2ecf20Sopenharmony_ci	}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	/*
4838c2ecf20Sopenharmony_ci	 * Set supported link speed by querying the supported
4848c2ecf20Sopenharmony_ci	 * capabilities of the link.
4858c2ecf20Sopenharmony_ci	 */
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	phylink_zero(sup_caps);
4888c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 10000baseT_Full);
4898c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 10000baseKX4_Full);
4908c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 10000baseR_FEC);
4918c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 10000baseCR_Full);
4928c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 10000baseSR_Full);
4938c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 10000baseLR_Full);
4948c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 10000baseLRM_Full);
4958c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 10000baseKR_Full);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	if (linkmode_intersects(link->supported_caps, sup_caps))
4988c2ecf20Sopenharmony_ci		lport->link_supported_speeds |= FC_PORTSPEED_10GBIT;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	phylink_zero(sup_caps);
5018c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 25000baseKR_Full);
5028c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 25000baseCR_Full);
5038c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 25000baseSR_Full);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	if (linkmode_intersects(link->supported_caps, sup_caps))
5068c2ecf20Sopenharmony_ci		lport->link_supported_speeds |= FC_PORTSPEED_25GBIT;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	phylink_zero(sup_caps);
5098c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 40000baseLR4_Full);
5108c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 40000baseKR4_Full);
5118c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 40000baseCR4_Full);
5128c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 40000baseSR4_Full);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	if (linkmode_intersects(link->supported_caps, sup_caps))
5158c2ecf20Sopenharmony_ci		lport->link_supported_speeds |= FC_PORTSPEED_40GBIT;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	phylink_zero(sup_caps);
5188c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 50000baseKR2_Full);
5198c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 50000baseCR2_Full);
5208c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 50000baseSR2_Full);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	if (linkmode_intersects(link->supported_caps, sup_caps))
5238c2ecf20Sopenharmony_ci		lport->link_supported_speeds |= FC_PORTSPEED_50GBIT;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	phylink_zero(sup_caps);
5268c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 100000baseKR4_Full);
5278c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 100000baseSR4_Full);
5288c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 100000baseCR4_Full);
5298c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 100000baseLR4_ER4_Full);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	if (linkmode_intersects(link->supported_caps, sup_caps))
5328c2ecf20Sopenharmony_ci		lport->link_supported_speeds |= FC_PORTSPEED_100GBIT;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	phylink_zero(sup_caps);
5358c2ecf20Sopenharmony_ci	phylink_set(sup_caps, 20000baseKR2_Full);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	if (linkmode_intersects(link->supported_caps, sup_caps))
5388c2ecf20Sopenharmony_ci		lport->link_supported_speeds |= FC_PORTSPEED_20GBIT;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	if (lport->host && lport->host->shost_data)
5418c2ecf20Sopenharmony_ci		fc_host_supported_speeds(lport->host) =
5428c2ecf20Sopenharmony_ci			lport->link_supported_speeds;
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_cistatic void qedf_bw_update(void *dev)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf = (struct qedf_ctx *)dev;
5488c2ecf20Sopenharmony_ci	struct qed_link_output link;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	/* Get the latest status of the link */
5518c2ecf20Sopenharmony_ci	qed_ops->common->get_link(qedf->cdev, &link);
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	if (test_bit(QEDF_UNLOADING, &qedf->flags)) {
5548c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx,
5558c2ecf20Sopenharmony_ci			 "Ignore link update, driver getting unload.\n");
5568c2ecf20Sopenharmony_ci		return;
5578c2ecf20Sopenharmony_ci	}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	if (link.link_up) {
5608c2ecf20Sopenharmony_ci		if (atomic_read(&qedf->link_state) == QEDF_LINK_UP)
5618c2ecf20Sopenharmony_ci			qedf_update_link_speed(qedf, &link);
5628c2ecf20Sopenharmony_ci		else
5638c2ecf20Sopenharmony_ci			QEDF_ERR(&qedf->dbg_ctx,
5648c2ecf20Sopenharmony_ci				 "Ignore bw update, link is down.\n");
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	} else {
5678c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx, "link_up is not set.\n");
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_cistatic void qedf_link_update(void *dev, struct qed_link_output *link)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf = (struct qedf_ctx *)dev;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	/*
5768c2ecf20Sopenharmony_ci	 * Prevent race where we're removing the module and we get link update
5778c2ecf20Sopenharmony_ci	 * for qed.
5788c2ecf20Sopenharmony_ci	 */
5798c2ecf20Sopenharmony_ci	if (test_bit(QEDF_UNLOADING, &qedf->flags)) {
5808c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx,
5818c2ecf20Sopenharmony_ci			 "Ignore link update, driver getting unload.\n");
5828c2ecf20Sopenharmony_ci		return;
5838c2ecf20Sopenharmony_ci	}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	if (link->link_up) {
5868c2ecf20Sopenharmony_ci		if (atomic_read(&qedf->link_state) == QEDF_LINK_UP) {
5878c2ecf20Sopenharmony_ci			QEDF_INFO((&qedf->dbg_ctx), QEDF_LOG_DISC,
5888c2ecf20Sopenharmony_ci			    "Ignoring link up event as link is already up.\n");
5898c2ecf20Sopenharmony_ci			return;
5908c2ecf20Sopenharmony_ci		}
5918c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "LINK UP (%d GB/s).\n",
5928c2ecf20Sopenharmony_ci		    link->speed / 1000);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci		/* Cancel any pending link down work */
5958c2ecf20Sopenharmony_ci		cancel_delayed_work(&qedf->link_update);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci		atomic_set(&qedf->link_state, QEDF_LINK_UP);
5988c2ecf20Sopenharmony_ci		qedf_update_link_speed(qedf, link);
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci		if (atomic_read(&qedf->dcbx) == QEDF_DCBX_DONE ||
6018c2ecf20Sopenharmony_ci		    qedf_dcbx_no_wait) {
6028c2ecf20Sopenharmony_ci			QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
6038c2ecf20Sopenharmony_ci			     "DCBx done.\n");
6048c2ecf20Sopenharmony_ci			if (atomic_read(&qedf->link_down_tmo_valid) > 0)
6058c2ecf20Sopenharmony_ci				queue_delayed_work(qedf->link_update_wq,
6068c2ecf20Sopenharmony_ci				    &qedf->link_recovery, 0);
6078c2ecf20Sopenharmony_ci			else
6088c2ecf20Sopenharmony_ci				queue_delayed_work(qedf->link_update_wq,
6098c2ecf20Sopenharmony_ci				    &qedf->link_update, 0);
6108c2ecf20Sopenharmony_ci			atomic_set(&qedf->link_down_tmo_valid, 0);
6118c2ecf20Sopenharmony_ci		}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	} else {
6148c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "LINK DOWN.\n");
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci		atomic_set(&qedf->link_state, QEDF_LINK_DOWN);
6178c2ecf20Sopenharmony_ci		atomic_set(&qedf->dcbx, QEDF_DCBX_PENDING);
6188c2ecf20Sopenharmony_ci		/*
6198c2ecf20Sopenharmony_ci		 * Flag that we're waiting for the link to come back up before
6208c2ecf20Sopenharmony_ci		 * informing the fcoe layer of the event.
6218c2ecf20Sopenharmony_ci		 */
6228c2ecf20Sopenharmony_ci		if (qedf_link_down_tmo > 0) {
6238c2ecf20Sopenharmony_ci			QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
6248c2ecf20Sopenharmony_ci			    "Starting link down tmo.\n");
6258c2ecf20Sopenharmony_ci			atomic_set(&qedf->link_down_tmo_valid, 1);
6268c2ecf20Sopenharmony_ci		}
6278c2ecf20Sopenharmony_ci		qedf->vlan_id = 0;
6288c2ecf20Sopenharmony_ci		qedf_update_link_speed(qedf, link);
6298c2ecf20Sopenharmony_ci		queue_delayed_work(qedf->link_update_wq, &qedf->link_update,
6308c2ecf20Sopenharmony_ci		    qedf_link_down_tmo * HZ);
6318c2ecf20Sopenharmony_ci	}
6328c2ecf20Sopenharmony_ci}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_cistatic void qedf_dcbx_handler(void *dev, struct qed_dcbx_get *get, u32 mib_type)
6368c2ecf20Sopenharmony_ci{
6378c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf = (struct qedf_ctx *)dev;
6388c2ecf20Sopenharmony_ci	u8 tmp_prio;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	QEDF_ERR(&(qedf->dbg_ctx), "DCBx event valid=%d enabled=%d fcoe "
6418c2ecf20Sopenharmony_ci	    "prio=%d.\n", get->operational.valid, get->operational.enabled,
6428c2ecf20Sopenharmony_ci	    get->operational.app_prio.fcoe);
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	if (get->operational.enabled && get->operational.valid) {
6458c2ecf20Sopenharmony_ci		/* If DCBX was already negotiated on link up then just exit */
6468c2ecf20Sopenharmony_ci		if (atomic_read(&qedf->dcbx) == QEDF_DCBX_DONE) {
6478c2ecf20Sopenharmony_ci			QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
6488c2ecf20Sopenharmony_ci			    "DCBX already set on link up.\n");
6498c2ecf20Sopenharmony_ci			return;
6508c2ecf20Sopenharmony_ci		}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci		atomic_set(&qedf->dcbx, QEDF_DCBX_DONE);
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci		/*
6558c2ecf20Sopenharmony_ci		 * Set the 8021q priority in the following manner:
6568c2ecf20Sopenharmony_ci		 *
6578c2ecf20Sopenharmony_ci		 * 1. If a modparam is set use that
6588c2ecf20Sopenharmony_ci		 * 2. If the value is not between 0..7 use the default
6598c2ecf20Sopenharmony_ci		 * 3. Use the priority we get from the DCBX app tag
6608c2ecf20Sopenharmony_ci		 */
6618c2ecf20Sopenharmony_ci		tmp_prio = get->operational.app_prio.fcoe;
6628c2ecf20Sopenharmony_ci		if (qedf_default_prio > -1)
6638c2ecf20Sopenharmony_ci			qedf->prio = qedf_default_prio;
6648c2ecf20Sopenharmony_ci		else if (tmp_prio > 7) {
6658c2ecf20Sopenharmony_ci			QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
6668c2ecf20Sopenharmony_ci			    "FIP/FCoE prio %d out of range, setting to %d.\n",
6678c2ecf20Sopenharmony_ci			    tmp_prio, QEDF_DEFAULT_PRIO);
6688c2ecf20Sopenharmony_ci			qedf->prio = QEDF_DEFAULT_PRIO;
6698c2ecf20Sopenharmony_ci		} else
6708c2ecf20Sopenharmony_ci			qedf->prio = tmp_prio;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci		if (atomic_read(&qedf->link_state) == QEDF_LINK_UP &&
6738c2ecf20Sopenharmony_ci		    !qedf_dcbx_no_wait) {
6748c2ecf20Sopenharmony_ci			if (atomic_read(&qedf->link_down_tmo_valid) > 0)
6758c2ecf20Sopenharmony_ci				queue_delayed_work(qedf->link_update_wq,
6768c2ecf20Sopenharmony_ci				    &qedf->link_recovery, 0);
6778c2ecf20Sopenharmony_ci			else
6788c2ecf20Sopenharmony_ci				queue_delayed_work(qedf->link_update_wq,
6798c2ecf20Sopenharmony_ci				    &qedf->link_update, 0);
6808c2ecf20Sopenharmony_ci			atomic_set(&qedf->link_down_tmo_valid, 0);
6818c2ecf20Sopenharmony_ci		}
6828c2ecf20Sopenharmony_ci	}
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_cistatic u32 qedf_get_login_failures(void *cookie)
6878c2ecf20Sopenharmony_ci{
6888c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	qedf = (struct qedf_ctx *)cookie;
6918c2ecf20Sopenharmony_ci	return qedf->flogi_failed;
6928c2ecf20Sopenharmony_ci}
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_cistatic struct qed_fcoe_cb_ops qedf_cb_ops = {
6958c2ecf20Sopenharmony_ci	{
6968c2ecf20Sopenharmony_ci		.link_update = qedf_link_update,
6978c2ecf20Sopenharmony_ci		.bw_update = qedf_bw_update,
6988c2ecf20Sopenharmony_ci		.schedule_recovery_handler = qedf_schedule_recovery_handler,
6998c2ecf20Sopenharmony_ci		.dcbx_aen = qedf_dcbx_handler,
7008c2ecf20Sopenharmony_ci		.get_generic_tlv_data = qedf_get_generic_tlv_data,
7018c2ecf20Sopenharmony_ci		.get_protocol_tlv_data = qedf_get_protocol_tlv_data,
7028c2ecf20Sopenharmony_ci		.schedule_hw_err_handler = qedf_schedule_hw_err_handler,
7038c2ecf20Sopenharmony_ci	}
7048c2ecf20Sopenharmony_ci};
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci/*
7078c2ecf20Sopenharmony_ci * Various transport templates.
7088c2ecf20Sopenharmony_ci */
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_cistatic struct scsi_transport_template *qedf_fc_transport_template;
7118c2ecf20Sopenharmony_cistatic struct scsi_transport_template *qedf_fc_vport_transport_template;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci/*
7148c2ecf20Sopenharmony_ci * SCSI EH handlers
7158c2ecf20Sopenharmony_ci */
7168c2ecf20Sopenharmony_cistatic int qedf_eh_abort(struct scsi_cmnd *sc_cmd)
7178c2ecf20Sopenharmony_ci{
7188c2ecf20Sopenharmony_ci	struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
7198c2ecf20Sopenharmony_ci	struct fc_lport *lport;
7208c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf;
7218c2ecf20Sopenharmony_ci	struct qedf_ioreq *io_req;
7228c2ecf20Sopenharmony_ci	struct fc_rport_libfc_priv *rp = rport->dd_data;
7238c2ecf20Sopenharmony_ci	struct fc_rport_priv *rdata;
7248c2ecf20Sopenharmony_ci	struct qedf_rport *fcport = NULL;
7258c2ecf20Sopenharmony_ci	int rc = FAILED;
7268c2ecf20Sopenharmony_ci	int wait_count = 100;
7278c2ecf20Sopenharmony_ci	int refcount = 0;
7288c2ecf20Sopenharmony_ci	int rval;
7298c2ecf20Sopenharmony_ci	int got_ref = 0;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	lport = shost_priv(sc_cmd->device->host);
7328c2ecf20Sopenharmony_ci	qedf = (struct qedf_ctx *)lport_priv(lport);
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	/* rport and tgt are allocated together, so tgt should be non-NULL */
7358c2ecf20Sopenharmony_ci	fcport = (struct qedf_rport *)&rp[1];
7368c2ecf20Sopenharmony_ci	rdata = fcport->rdata;
7378c2ecf20Sopenharmony_ci	if (!rdata || !kref_get_unless_zero(&rdata->kref)) {
7388c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx, "stale rport, sc_cmd=%p\n", sc_cmd);
7398c2ecf20Sopenharmony_ci		rc = SUCCESS;
7408c2ecf20Sopenharmony_ci		goto out;
7418c2ecf20Sopenharmony_ci	}
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	io_req = (struct qedf_ioreq *)sc_cmd->SCp.ptr;
7458c2ecf20Sopenharmony_ci	if (!io_req) {
7468c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx,
7478c2ecf20Sopenharmony_ci			 "sc_cmd not queued with lld, sc_cmd=%p op=0x%02x, port_id=%06x\n",
7488c2ecf20Sopenharmony_ci			 sc_cmd, sc_cmd->cmnd[0],
7498c2ecf20Sopenharmony_ci			 rdata->ids.port_id);
7508c2ecf20Sopenharmony_ci		rc = SUCCESS;
7518c2ecf20Sopenharmony_ci		goto drop_rdata_kref;
7528c2ecf20Sopenharmony_ci	}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	rval = kref_get_unless_zero(&io_req->refcount);	/* ID: 005 */
7558c2ecf20Sopenharmony_ci	if (rval)
7568c2ecf20Sopenharmony_ci		got_ref = 1;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	/* If we got a valid io_req, confirm it belongs to this sc_cmd. */
7598c2ecf20Sopenharmony_ci	if (!rval || io_req->sc_cmd != sc_cmd) {
7608c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx,
7618c2ecf20Sopenharmony_ci			 "Freed/Incorrect io_req, io_req->sc_cmd=%p, sc_cmd=%p, port_id=%06x, bailing out.\n",
7628c2ecf20Sopenharmony_ci			 io_req->sc_cmd, sc_cmd, rdata->ids.port_id);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci		goto drop_rdata_kref;
7658c2ecf20Sopenharmony_ci	}
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	if (fc_remote_port_chkready(rport)) {
7688c2ecf20Sopenharmony_ci		refcount = kref_read(&io_req->refcount);
7698c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx,
7708c2ecf20Sopenharmony_ci			 "rport not ready, io_req=%p, xid=0x%x sc_cmd=%p op=0x%02x, refcount=%d, port_id=%06x\n",
7718c2ecf20Sopenharmony_ci			 io_req, io_req->xid, sc_cmd, sc_cmd->cmnd[0],
7728c2ecf20Sopenharmony_ci			 refcount, rdata->ids.port_id);
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci		goto drop_rdata_kref;
7758c2ecf20Sopenharmony_ci	}
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	rc = fc_block_scsi_eh(sc_cmd);
7788c2ecf20Sopenharmony_ci	if (rc)
7798c2ecf20Sopenharmony_ci		goto drop_rdata_kref;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	if (test_bit(QEDF_RPORT_UPLOADING_CONNECTION, &fcport->flags)) {
7828c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx,
7838c2ecf20Sopenharmony_ci			 "Connection uploading, xid=0x%x., port_id=%06x\n",
7848c2ecf20Sopenharmony_ci			 io_req->xid, rdata->ids.port_id);
7858c2ecf20Sopenharmony_ci		while (io_req->sc_cmd && (wait_count != 0)) {
7868c2ecf20Sopenharmony_ci			msleep(100);
7878c2ecf20Sopenharmony_ci			wait_count--;
7888c2ecf20Sopenharmony_ci		}
7898c2ecf20Sopenharmony_ci		if (wait_count) {
7908c2ecf20Sopenharmony_ci			QEDF_ERR(&qedf->dbg_ctx, "ABTS succeeded\n");
7918c2ecf20Sopenharmony_ci			rc = SUCCESS;
7928c2ecf20Sopenharmony_ci		} else {
7938c2ecf20Sopenharmony_ci			QEDF_ERR(&qedf->dbg_ctx, "ABTS failed\n");
7948c2ecf20Sopenharmony_ci			rc = FAILED;
7958c2ecf20Sopenharmony_ci		}
7968c2ecf20Sopenharmony_ci		goto drop_rdata_kref;
7978c2ecf20Sopenharmony_ci	}
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	if (lport->state != LPORT_ST_READY || !(lport->link_up)) {
8008c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx, "link not ready.\n");
8018c2ecf20Sopenharmony_ci		goto drop_rdata_kref;
8028c2ecf20Sopenharmony_ci	}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	QEDF_ERR(&qedf->dbg_ctx,
8058c2ecf20Sopenharmony_ci		 "Aborting io_req=%p sc_cmd=%p xid=0x%x fp_idx=%d, port_id=%06x.\n",
8068c2ecf20Sopenharmony_ci		 io_req, sc_cmd, io_req->xid, io_req->fp_idx,
8078c2ecf20Sopenharmony_ci		 rdata->ids.port_id);
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	if (qedf->stop_io_on_error) {
8108c2ecf20Sopenharmony_ci		qedf_stop_all_io(qedf);
8118c2ecf20Sopenharmony_ci		rc = SUCCESS;
8128c2ecf20Sopenharmony_ci		goto drop_rdata_kref;
8138c2ecf20Sopenharmony_ci	}
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	init_completion(&io_req->abts_done);
8168c2ecf20Sopenharmony_ci	rval = qedf_initiate_abts(io_req, true);
8178c2ecf20Sopenharmony_ci	if (rval) {
8188c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "Failed to queue ABTS.\n");
8198c2ecf20Sopenharmony_ci		/*
8208c2ecf20Sopenharmony_ci		 * If we fail to queue the ABTS then return this command to
8218c2ecf20Sopenharmony_ci		 * the SCSI layer as it will own and free the xid
8228c2ecf20Sopenharmony_ci		 */
8238c2ecf20Sopenharmony_ci		rc = SUCCESS;
8248c2ecf20Sopenharmony_ci		qedf_scsi_done(qedf, io_req, DID_ERROR);
8258c2ecf20Sopenharmony_ci		goto drop_rdata_kref;
8268c2ecf20Sopenharmony_ci	}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	wait_for_completion(&io_req->abts_done);
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	if (io_req->event == QEDF_IOREQ_EV_ABORT_SUCCESS ||
8318c2ecf20Sopenharmony_ci	    io_req->event == QEDF_IOREQ_EV_ABORT_FAILED ||
8328c2ecf20Sopenharmony_ci	    io_req->event == QEDF_IOREQ_EV_CLEANUP_SUCCESS) {
8338c2ecf20Sopenharmony_ci		/*
8348c2ecf20Sopenharmony_ci		 * If we get a reponse to the abort this is success from
8358c2ecf20Sopenharmony_ci		 * the perspective that all references to the command have
8368c2ecf20Sopenharmony_ci		 * been removed from the driver and firmware
8378c2ecf20Sopenharmony_ci		 */
8388c2ecf20Sopenharmony_ci		rc = SUCCESS;
8398c2ecf20Sopenharmony_ci	} else {
8408c2ecf20Sopenharmony_ci		/* If the abort and cleanup failed then return a failure */
8418c2ecf20Sopenharmony_ci		rc = FAILED;
8428c2ecf20Sopenharmony_ci	}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	if (rc == SUCCESS)
8458c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "ABTS succeeded, xid=0x%x.\n",
8468c2ecf20Sopenharmony_ci			  io_req->xid);
8478c2ecf20Sopenharmony_ci	else
8488c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "ABTS failed, xid=0x%x.\n",
8498c2ecf20Sopenharmony_ci			  io_req->xid);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_cidrop_rdata_kref:
8528c2ecf20Sopenharmony_ci	kref_put(&rdata->kref, fc_rport_destroy);
8538c2ecf20Sopenharmony_ciout:
8548c2ecf20Sopenharmony_ci	if (got_ref)
8558c2ecf20Sopenharmony_ci		kref_put(&io_req->refcount, qedf_release_cmd);
8568c2ecf20Sopenharmony_ci	return rc;
8578c2ecf20Sopenharmony_ci}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_cistatic int qedf_eh_target_reset(struct scsi_cmnd *sc_cmd)
8608c2ecf20Sopenharmony_ci{
8618c2ecf20Sopenharmony_ci	QEDF_ERR(NULL, "%d:0:%d:%lld: TARGET RESET Issued...",
8628c2ecf20Sopenharmony_ci		 sc_cmd->device->host->host_no, sc_cmd->device->id,
8638c2ecf20Sopenharmony_ci		 sc_cmd->device->lun);
8648c2ecf20Sopenharmony_ci	return qedf_initiate_tmf(sc_cmd, FCP_TMF_TGT_RESET);
8658c2ecf20Sopenharmony_ci}
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_cistatic int qedf_eh_device_reset(struct scsi_cmnd *sc_cmd)
8688c2ecf20Sopenharmony_ci{
8698c2ecf20Sopenharmony_ci	QEDF_ERR(NULL, "%d:0:%d:%lld: LUN RESET Issued... ",
8708c2ecf20Sopenharmony_ci		 sc_cmd->device->host->host_no, sc_cmd->device->id,
8718c2ecf20Sopenharmony_ci		 sc_cmd->device->lun);
8728c2ecf20Sopenharmony_ci	return qedf_initiate_tmf(sc_cmd, FCP_TMF_LUN_RESET);
8738c2ecf20Sopenharmony_ci}
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_cibool qedf_wait_for_upload(struct qedf_ctx *qedf)
8768c2ecf20Sopenharmony_ci{
8778c2ecf20Sopenharmony_ci	struct qedf_rport *fcport = NULL;
8788c2ecf20Sopenharmony_ci	int wait_cnt = 120;
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	while (wait_cnt--) {
8818c2ecf20Sopenharmony_ci		if (atomic_read(&qedf->num_offloads))
8828c2ecf20Sopenharmony_ci			QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
8838c2ecf20Sopenharmony_ci				  "Waiting for all uploads to complete num_offloads = 0x%x.\n",
8848c2ecf20Sopenharmony_ci				  atomic_read(&qedf->num_offloads));
8858c2ecf20Sopenharmony_ci		else
8868c2ecf20Sopenharmony_ci			return true;
8878c2ecf20Sopenharmony_ci		msleep(500);
8888c2ecf20Sopenharmony_ci	}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	rcu_read_lock();
8918c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(fcport, &qedf->fcports, peers) {
8928c2ecf20Sopenharmony_ci		if (fcport && test_bit(QEDF_RPORT_SESSION_READY,
8938c2ecf20Sopenharmony_ci				       &fcport->flags)) {
8948c2ecf20Sopenharmony_ci			if (fcport->rdata)
8958c2ecf20Sopenharmony_ci				QEDF_ERR(&qedf->dbg_ctx,
8968c2ecf20Sopenharmony_ci					 "Waiting for fcport %p portid=%06x.\n",
8978c2ecf20Sopenharmony_ci					 fcport, fcport->rdata->ids.port_id);
8988c2ecf20Sopenharmony_ci			} else {
8998c2ecf20Sopenharmony_ci				QEDF_ERR(&qedf->dbg_ctx,
9008c2ecf20Sopenharmony_ci					 "Waiting for fcport %p.\n", fcport);
9018c2ecf20Sopenharmony_ci			}
9028c2ecf20Sopenharmony_ci	}
9038c2ecf20Sopenharmony_ci	rcu_read_unlock();
9048c2ecf20Sopenharmony_ci	return false;
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci/* Performs soft reset of qedf_ctx by simulating a link down/up */
9098c2ecf20Sopenharmony_civoid qedf_ctx_soft_reset(struct fc_lport *lport)
9108c2ecf20Sopenharmony_ci{
9118c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf;
9128c2ecf20Sopenharmony_ci	struct qed_link_output if_link;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	if (lport->vport) {
9158c2ecf20Sopenharmony_ci		QEDF_ERR(NULL, "Cannot issue host reset on NPIV port.\n");
9168c2ecf20Sopenharmony_ci		return;
9178c2ecf20Sopenharmony_ci	}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	qedf = lport_priv(lport);
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	qedf->flogi_pending = 0;
9228c2ecf20Sopenharmony_ci	/* For host reset, essentially do a soft link up/down */
9238c2ecf20Sopenharmony_ci	atomic_set(&qedf->link_state, QEDF_LINK_DOWN);
9248c2ecf20Sopenharmony_ci	QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
9258c2ecf20Sopenharmony_ci		  "Queuing link down work.\n");
9268c2ecf20Sopenharmony_ci	queue_delayed_work(qedf->link_update_wq, &qedf->link_update,
9278c2ecf20Sopenharmony_ci	    0);
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	if (qedf_wait_for_upload(qedf) == false) {
9308c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx, "Could not upload all sessions.\n");
9318c2ecf20Sopenharmony_ci		WARN_ON(atomic_read(&qedf->num_offloads));
9328c2ecf20Sopenharmony_ci	}
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	/* Before setting link up query physical link state */
9358c2ecf20Sopenharmony_ci	qed_ops->common->get_link(qedf->cdev, &if_link);
9368c2ecf20Sopenharmony_ci	/* Bail if the physical link is not up */
9378c2ecf20Sopenharmony_ci	if (!if_link.link_up) {
9388c2ecf20Sopenharmony_ci		QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
9398c2ecf20Sopenharmony_ci			  "Physical link is not up.\n");
9408c2ecf20Sopenharmony_ci		return;
9418c2ecf20Sopenharmony_ci	}
9428c2ecf20Sopenharmony_ci	/* Flush and wait to make sure link down is processed */
9438c2ecf20Sopenharmony_ci	flush_delayed_work(&qedf->link_update);
9448c2ecf20Sopenharmony_ci	msleep(500);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	atomic_set(&qedf->link_state, QEDF_LINK_UP);
9478c2ecf20Sopenharmony_ci	qedf->vlan_id  = 0;
9488c2ecf20Sopenharmony_ci	QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
9498c2ecf20Sopenharmony_ci		  "Queue link up work.\n");
9508c2ecf20Sopenharmony_ci	queue_delayed_work(qedf->link_update_wq, &qedf->link_update,
9518c2ecf20Sopenharmony_ci	    0);
9528c2ecf20Sopenharmony_ci}
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci/* Reset the host by gracefully logging out and then logging back in */
9558c2ecf20Sopenharmony_cistatic int qedf_eh_host_reset(struct scsi_cmnd *sc_cmd)
9568c2ecf20Sopenharmony_ci{
9578c2ecf20Sopenharmony_ci	struct fc_lport *lport;
9588c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf;
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	lport = shost_priv(sc_cmd->device->host);
9618c2ecf20Sopenharmony_ci	qedf = lport_priv(lport);
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	if (atomic_read(&qedf->link_state) == QEDF_LINK_DOWN ||
9648c2ecf20Sopenharmony_ci	    test_bit(QEDF_UNLOADING, &qedf->flags))
9658c2ecf20Sopenharmony_ci		return FAILED;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	QEDF_ERR(&(qedf->dbg_ctx), "HOST RESET Issued...");
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	qedf_ctx_soft_reset(lport);
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	return SUCCESS;
9728c2ecf20Sopenharmony_ci}
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_cistatic int qedf_slave_configure(struct scsi_device *sdev)
9758c2ecf20Sopenharmony_ci{
9768c2ecf20Sopenharmony_ci	if (qedf_queue_depth) {
9778c2ecf20Sopenharmony_ci		scsi_change_queue_depth(sdev, qedf_queue_depth);
9788c2ecf20Sopenharmony_ci	}
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	return 0;
9818c2ecf20Sopenharmony_ci}
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_cistatic struct scsi_host_template qedf_host_template = {
9848c2ecf20Sopenharmony_ci	.module 	= THIS_MODULE,
9858c2ecf20Sopenharmony_ci	.name 		= QEDF_MODULE_NAME,
9868c2ecf20Sopenharmony_ci	.this_id 	= -1,
9878c2ecf20Sopenharmony_ci	.cmd_per_lun	= 32,
9888c2ecf20Sopenharmony_ci	.max_sectors 	= 0xffff,
9898c2ecf20Sopenharmony_ci	.queuecommand 	= qedf_queuecommand,
9908c2ecf20Sopenharmony_ci	.shost_attrs	= qedf_host_attrs,
9918c2ecf20Sopenharmony_ci	.eh_abort_handler	= qedf_eh_abort,
9928c2ecf20Sopenharmony_ci	.eh_device_reset_handler = qedf_eh_device_reset, /* lun reset */
9938c2ecf20Sopenharmony_ci	.eh_target_reset_handler = qedf_eh_target_reset, /* target reset */
9948c2ecf20Sopenharmony_ci	.eh_host_reset_handler  = qedf_eh_host_reset,
9958c2ecf20Sopenharmony_ci	.slave_configure	= qedf_slave_configure,
9968c2ecf20Sopenharmony_ci	.dma_boundary = QED_HW_DMA_BOUNDARY,
9978c2ecf20Sopenharmony_ci	.sg_tablesize = QEDF_MAX_BDS_PER_CMD,
9988c2ecf20Sopenharmony_ci	.can_queue = FCOE_PARAMS_NUM_TASKS,
9998c2ecf20Sopenharmony_ci	.change_queue_depth = scsi_change_queue_depth,
10008c2ecf20Sopenharmony_ci};
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_cistatic int qedf_get_paged_crc_eof(struct sk_buff *skb, int tlen)
10038c2ecf20Sopenharmony_ci{
10048c2ecf20Sopenharmony_ci	int rc;
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	spin_lock(&qedf_global_lock);
10078c2ecf20Sopenharmony_ci	rc = fcoe_get_paged_crc_eof(skb, tlen, &qedf_global);
10088c2ecf20Sopenharmony_ci	spin_unlock(&qedf_global_lock);
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	return rc;
10118c2ecf20Sopenharmony_ci}
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_cistatic struct qedf_rport *qedf_fcport_lookup(struct qedf_ctx *qedf, u32 port_id)
10148c2ecf20Sopenharmony_ci{
10158c2ecf20Sopenharmony_ci	struct qedf_rport *fcport;
10168c2ecf20Sopenharmony_ci	struct fc_rport_priv *rdata;
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	rcu_read_lock();
10198c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(fcport, &qedf->fcports, peers) {
10208c2ecf20Sopenharmony_ci		rdata = fcport->rdata;
10218c2ecf20Sopenharmony_ci		if (rdata == NULL)
10228c2ecf20Sopenharmony_ci			continue;
10238c2ecf20Sopenharmony_ci		if (rdata->ids.port_id == port_id) {
10248c2ecf20Sopenharmony_ci			rcu_read_unlock();
10258c2ecf20Sopenharmony_ci			return fcport;
10268c2ecf20Sopenharmony_ci		}
10278c2ecf20Sopenharmony_ci	}
10288c2ecf20Sopenharmony_ci	rcu_read_unlock();
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	/* Return NULL to caller to let them know fcport was not found */
10318c2ecf20Sopenharmony_ci	return NULL;
10328c2ecf20Sopenharmony_ci}
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci/* Transmits an ELS frame over an offloaded session */
10358c2ecf20Sopenharmony_cistatic int qedf_xmit_l2_frame(struct qedf_rport *fcport, struct fc_frame *fp)
10368c2ecf20Sopenharmony_ci{
10378c2ecf20Sopenharmony_ci	struct fc_frame_header *fh;
10388c2ecf20Sopenharmony_ci	int rc = 0;
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	fh = fc_frame_header_get(fp);
10418c2ecf20Sopenharmony_ci	if ((fh->fh_type == FC_TYPE_ELS) &&
10428c2ecf20Sopenharmony_ci	    (fh->fh_r_ctl == FC_RCTL_ELS_REQ)) {
10438c2ecf20Sopenharmony_ci		switch (fc_frame_payload_op(fp)) {
10448c2ecf20Sopenharmony_ci		case ELS_ADISC:
10458c2ecf20Sopenharmony_ci			qedf_send_adisc(fcport, fp);
10468c2ecf20Sopenharmony_ci			rc = 1;
10478c2ecf20Sopenharmony_ci			break;
10488c2ecf20Sopenharmony_ci		}
10498c2ecf20Sopenharmony_ci	}
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	return rc;
10528c2ecf20Sopenharmony_ci}
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci/*
10558c2ecf20Sopenharmony_ci * qedf_xmit - qedf FCoE frame transmit function
10568c2ecf20Sopenharmony_ci */
10578c2ecf20Sopenharmony_cistatic int qedf_xmit(struct fc_lport *lport, struct fc_frame *fp)
10588c2ecf20Sopenharmony_ci{
10598c2ecf20Sopenharmony_ci	struct fc_lport		*base_lport;
10608c2ecf20Sopenharmony_ci	struct qedf_ctx		*qedf;
10618c2ecf20Sopenharmony_ci	struct ethhdr		*eh;
10628c2ecf20Sopenharmony_ci	struct fcoe_crc_eof	*cp;
10638c2ecf20Sopenharmony_ci	struct sk_buff		*skb;
10648c2ecf20Sopenharmony_ci	struct fc_frame_header	*fh;
10658c2ecf20Sopenharmony_ci	struct fcoe_hdr		*hp;
10668c2ecf20Sopenharmony_ci	u8			sof, eof;
10678c2ecf20Sopenharmony_ci	u32			crc;
10688c2ecf20Sopenharmony_ci	unsigned int		hlen, tlen, elen;
10698c2ecf20Sopenharmony_ci	int			wlen;
10708c2ecf20Sopenharmony_ci	struct fc_stats		*stats;
10718c2ecf20Sopenharmony_ci	struct fc_lport *tmp_lport;
10728c2ecf20Sopenharmony_ci	struct fc_lport *vn_port = NULL;
10738c2ecf20Sopenharmony_ci	struct qedf_rport *fcport;
10748c2ecf20Sopenharmony_ci	int rc;
10758c2ecf20Sopenharmony_ci	u16 vlan_tci = 0;
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	qedf = (struct qedf_ctx *)lport_priv(lport);
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	fh = fc_frame_header_get(fp);
10808c2ecf20Sopenharmony_ci	skb = fp_skb(fp);
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	/* Filter out traffic to other NPIV ports on the same host */
10838c2ecf20Sopenharmony_ci	if (lport->vport)
10848c2ecf20Sopenharmony_ci		base_lport = shost_priv(vport_to_shost(lport->vport));
10858c2ecf20Sopenharmony_ci	else
10868c2ecf20Sopenharmony_ci		base_lport = lport;
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	/* Flag if the destination is the base port */
10898c2ecf20Sopenharmony_ci	if (base_lport->port_id == ntoh24(fh->fh_d_id)) {
10908c2ecf20Sopenharmony_ci		vn_port = base_lport;
10918c2ecf20Sopenharmony_ci	} else {
10928c2ecf20Sopenharmony_ci		/* Got through the list of vports attached to the base_lport
10938c2ecf20Sopenharmony_ci		 * and see if we have a match with the destination address.
10948c2ecf20Sopenharmony_ci		 */
10958c2ecf20Sopenharmony_ci		list_for_each_entry(tmp_lport, &base_lport->vports, list) {
10968c2ecf20Sopenharmony_ci			if (tmp_lport->port_id == ntoh24(fh->fh_d_id)) {
10978c2ecf20Sopenharmony_ci				vn_port = tmp_lport;
10988c2ecf20Sopenharmony_ci				break;
10998c2ecf20Sopenharmony_ci			}
11008c2ecf20Sopenharmony_ci		}
11018c2ecf20Sopenharmony_ci	}
11028c2ecf20Sopenharmony_ci	if (vn_port && ntoh24(fh->fh_d_id) != FC_FID_FLOGI) {
11038c2ecf20Sopenharmony_ci		struct fc_rport_priv *rdata = NULL;
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2,
11068c2ecf20Sopenharmony_ci		    "Dropping FCoE frame to %06x.\n", ntoh24(fh->fh_d_id));
11078c2ecf20Sopenharmony_ci		kfree_skb(skb);
11088c2ecf20Sopenharmony_ci		rdata = fc_rport_lookup(lport, ntoh24(fh->fh_d_id));
11098c2ecf20Sopenharmony_ci		if (rdata) {
11108c2ecf20Sopenharmony_ci			rdata->retries = lport->max_rport_retry_count;
11118c2ecf20Sopenharmony_ci			kref_put(&rdata->kref, fc_rport_destroy);
11128c2ecf20Sopenharmony_ci		}
11138c2ecf20Sopenharmony_ci		return -EINVAL;
11148c2ecf20Sopenharmony_ci	}
11158c2ecf20Sopenharmony_ci	/* End NPIV filtering */
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	if (!qedf->ctlr.sel_fcf) {
11188c2ecf20Sopenharmony_ci		kfree_skb(skb);
11198c2ecf20Sopenharmony_ci		return 0;
11208c2ecf20Sopenharmony_ci	}
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	if (!test_bit(QEDF_LL2_STARTED, &qedf->flags)) {
11238c2ecf20Sopenharmony_ci		QEDF_WARN(&(qedf->dbg_ctx), "LL2 not started\n");
11248c2ecf20Sopenharmony_ci		kfree_skb(skb);
11258c2ecf20Sopenharmony_ci		return 0;
11268c2ecf20Sopenharmony_ci	}
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	if (atomic_read(&qedf->link_state) != QEDF_LINK_UP) {
11298c2ecf20Sopenharmony_ci		QEDF_WARN(&(qedf->dbg_ctx), "qedf link down\n");
11308c2ecf20Sopenharmony_ci		kfree_skb(skb);
11318c2ecf20Sopenharmony_ci		return 0;
11328c2ecf20Sopenharmony_ci	}
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci	if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) {
11358c2ecf20Sopenharmony_ci		if (fcoe_ctlr_els_send(&qedf->ctlr, lport, skb))
11368c2ecf20Sopenharmony_ci			return 0;
11378c2ecf20Sopenharmony_ci	}
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	/* Check to see if this needs to be sent on an offloaded session */
11408c2ecf20Sopenharmony_ci	fcport = qedf_fcport_lookup(qedf, ntoh24(fh->fh_d_id));
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	if (fcport && test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) {
11438c2ecf20Sopenharmony_ci		rc = qedf_xmit_l2_frame(fcport, fp);
11448c2ecf20Sopenharmony_ci		/*
11458c2ecf20Sopenharmony_ci		 * If the frame was successfully sent over the middle path
11468c2ecf20Sopenharmony_ci		 * then do not try to also send it over the LL2 path
11478c2ecf20Sopenharmony_ci		 */
11488c2ecf20Sopenharmony_ci		if (rc)
11498c2ecf20Sopenharmony_ci			return 0;
11508c2ecf20Sopenharmony_ci	}
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci	sof = fr_sof(fp);
11538c2ecf20Sopenharmony_ci	eof = fr_eof(fp);
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	elen = sizeof(struct ethhdr);
11568c2ecf20Sopenharmony_ci	hlen = sizeof(struct fcoe_hdr);
11578c2ecf20Sopenharmony_ci	tlen = sizeof(struct fcoe_crc_eof);
11588c2ecf20Sopenharmony_ci	wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE;
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	skb->ip_summed = CHECKSUM_NONE;
11618c2ecf20Sopenharmony_ci	crc = fcoe_fc_crc(fp);
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	/* copy port crc and eof to the skb buff */
11648c2ecf20Sopenharmony_ci	if (skb_is_nonlinear(skb)) {
11658c2ecf20Sopenharmony_ci		skb_frag_t *frag;
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci		if (qedf_get_paged_crc_eof(skb, tlen)) {
11688c2ecf20Sopenharmony_ci			kfree_skb(skb);
11698c2ecf20Sopenharmony_ci			return -ENOMEM;
11708c2ecf20Sopenharmony_ci		}
11718c2ecf20Sopenharmony_ci		frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1];
11728c2ecf20Sopenharmony_ci		cp = kmap_atomic(skb_frag_page(frag)) + skb_frag_off(frag);
11738c2ecf20Sopenharmony_ci	} else {
11748c2ecf20Sopenharmony_ci		cp = skb_put(skb, tlen);
11758c2ecf20Sopenharmony_ci	}
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	memset(cp, 0, sizeof(*cp));
11788c2ecf20Sopenharmony_ci	cp->fcoe_eof = eof;
11798c2ecf20Sopenharmony_ci	cp->fcoe_crc32 = cpu_to_le32(~crc);
11808c2ecf20Sopenharmony_ci	if (skb_is_nonlinear(skb)) {
11818c2ecf20Sopenharmony_ci		kunmap_atomic(cp);
11828c2ecf20Sopenharmony_ci		cp = NULL;
11838c2ecf20Sopenharmony_ci	}
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci	/* adjust skb network/transport offsets to match mac/fcoe/port */
11878c2ecf20Sopenharmony_ci	skb_push(skb, elen + hlen);
11888c2ecf20Sopenharmony_ci	skb_reset_mac_header(skb);
11898c2ecf20Sopenharmony_ci	skb_reset_network_header(skb);
11908c2ecf20Sopenharmony_ci	skb->mac_len = elen;
11918c2ecf20Sopenharmony_ci	skb->protocol = htons(ETH_P_FCOE);
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	/*
11948c2ecf20Sopenharmony_ci	 * Add VLAN tag to non-offload FCoE frame based on current stored VLAN
11958c2ecf20Sopenharmony_ci	 * for FIP/FCoE traffic.
11968c2ecf20Sopenharmony_ci	 */
11978c2ecf20Sopenharmony_ci	__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), qedf->vlan_id);
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	/* fill up mac and fcoe headers */
12008c2ecf20Sopenharmony_ci	eh = eth_hdr(skb);
12018c2ecf20Sopenharmony_ci	eh->h_proto = htons(ETH_P_FCOE);
12028c2ecf20Sopenharmony_ci	if (qedf->ctlr.map_dest)
12038c2ecf20Sopenharmony_ci		fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id);
12048c2ecf20Sopenharmony_ci	else
12058c2ecf20Sopenharmony_ci		/* insert GW address */
12068c2ecf20Sopenharmony_ci		ether_addr_copy(eh->h_dest, qedf->ctlr.dest_addr);
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	/* Set the source MAC address */
12098c2ecf20Sopenharmony_ci	ether_addr_copy(eh->h_source, qedf->data_src_addr);
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	hp = (struct fcoe_hdr *)(eh + 1);
12128c2ecf20Sopenharmony_ci	memset(hp, 0, sizeof(*hp));
12138c2ecf20Sopenharmony_ci	if (FC_FCOE_VER)
12148c2ecf20Sopenharmony_ci		FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER);
12158c2ecf20Sopenharmony_ci	hp->fcoe_sof = sof;
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci	/*update tx stats */
12188c2ecf20Sopenharmony_ci	stats = per_cpu_ptr(lport->stats, get_cpu());
12198c2ecf20Sopenharmony_ci	stats->TxFrames++;
12208c2ecf20Sopenharmony_ci	stats->TxWords += wlen;
12218c2ecf20Sopenharmony_ci	put_cpu();
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	/* Get VLAN ID from skb for printing purposes */
12248c2ecf20Sopenharmony_ci	__vlan_hwaccel_get_tag(skb, &vlan_tci);
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	/* send down to lld */
12278c2ecf20Sopenharmony_ci	fr_dev(fp) = lport;
12288c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "FCoE frame send: "
12298c2ecf20Sopenharmony_ci	    "src=%06x dest=%06x r_ctl=%x type=%x vlan=%04x.\n",
12308c2ecf20Sopenharmony_ci	    ntoh24(fh->fh_s_id), ntoh24(fh->fh_d_id), fh->fh_r_ctl, fh->fh_type,
12318c2ecf20Sopenharmony_ci	    vlan_tci);
12328c2ecf20Sopenharmony_ci	if (qedf_dump_frames)
12338c2ecf20Sopenharmony_ci		print_hex_dump(KERN_WARNING, "fcoe: ", DUMP_PREFIX_OFFSET, 16,
12348c2ecf20Sopenharmony_ci		    1, skb->data, skb->len, false);
12358c2ecf20Sopenharmony_ci	rc = qed_ops->ll2->start_xmit(qedf->cdev, skb, 0);
12368c2ecf20Sopenharmony_ci	if (rc) {
12378c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx, "start_xmit failed rc = %d.\n", rc);
12388c2ecf20Sopenharmony_ci		kfree_skb(skb);
12398c2ecf20Sopenharmony_ci		return rc;
12408c2ecf20Sopenharmony_ci	}
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	return 0;
12438c2ecf20Sopenharmony_ci}
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_cistatic int qedf_alloc_sq(struct qedf_ctx *qedf, struct qedf_rport *fcport)
12468c2ecf20Sopenharmony_ci{
12478c2ecf20Sopenharmony_ci	int rval = 0;
12488c2ecf20Sopenharmony_ci	u32 *pbl;
12498c2ecf20Sopenharmony_ci	dma_addr_t page;
12508c2ecf20Sopenharmony_ci	int num_pages;
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	/* Calculate appropriate queue and PBL sizes */
12538c2ecf20Sopenharmony_ci	fcport->sq_mem_size = SQ_NUM_ENTRIES * sizeof(struct fcoe_wqe);
12548c2ecf20Sopenharmony_ci	fcport->sq_mem_size = ALIGN(fcport->sq_mem_size, QEDF_PAGE_SIZE);
12558c2ecf20Sopenharmony_ci	fcport->sq_pbl_size = (fcport->sq_mem_size / QEDF_PAGE_SIZE) *
12568c2ecf20Sopenharmony_ci	    sizeof(void *);
12578c2ecf20Sopenharmony_ci	fcport->sq_pbl_size = fcport->sq_pbl_size + QEDF_PAGE_SIZE;
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	fcport->sq = dma_alloc_coherent(&qedf->pdev->dev, fcport->sq_mem_size,
12608c2ecf20Sopenharmony_ci					&fcport->sq_dma, GFP_KERNEL);
12618c2ecf20Sopenharmony_ci	if (!fcport->sq) {
12628c2ecf20Sopenharmony_ci		QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate send queue.\n");
12638c2ecf20Sopenharmony_ci		rval = 1;
12648c2ecf20Sopenharmony_ci		goto out;
12658c2ecf20Sopenharmony_ci	}
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	fcport->sq_pbl = dma_alloc_coherent(&qedf->pdev->dev,
12688c2ecf20Sopenharmony_ci					    fcport->sq_pbl_size,
12698c2ecf20Sopenharmony_ci					    &fcport->sq_pbl_dma, GFP_KERNEL);
12708c2ecf20Sopenharmony_ci	if (!fcport->sq_pbl) {
12718c2ecf20Sopenharmony_ci		QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate send queue PBL.\n");
12728c2ecf20Sopenharmony_ci		rval = 1;
12738c2ecf20Sopenharmony_ci		goto out_free_sq;
12748c2ecf20Sopenharmony_ci	}
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	/* Create PBL */
12778c2ecf20Sopenharmony_ci	num_pages = fcport->sq_mem_size / QEDF_PAGE_SIZE;
12788c2ecf20Sopenharmony_ci	page = fcport->sq_dma;
12798c2ecf20Sopenharmony_ci	pbl = (u32 *)fcport->sq_pbl;
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ci	while (num_pages--) {
12828c2ecf20Sopenharmony_ci		*pbl = U64_LO(page);
12838c2ecf20Sopenharmony_ci		pbl++;
12848c2ecf20Sopenharmony_ci		*pbl = U64_HI(page);
12858c2ecf20Sopenharmony_ci		pbl++;
12868c2ecf20Sopenharmony_ci		page += QEDF_PAGE_SIZE;
12878c2ecf20Sopenharmony_ci	}
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	return rval;
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ciout_free_sq:
12928c2ecf20Sopenharmony_ci	dma_free_coherent(&qedf->pdev->dev, fcport->sq_mem_size, fcport->sq,
12938c2ecf20Sopenharmony_ci	    fcport->sq_dma);
12948c2ecf20Sopenharmony_ciout:
12958c2ecf20Sopenharmony_ci	return rval;
12968c2ecf20Sopenharmony_ci}
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_cistatic void qedf_free_sq(struct qedf_ctx *qedf, struct qedf_rport *fcport)
12998c2ecf20Sopenharmony_ci{
13008c2ecf20Sopenharmony_ci	if (fcport->sq_pbl)
13018c2ecf20Sopenharmony_ci		dma_free_coherent(&qedf->pdev->dev, fcport->sq_pbl_size,
13028c2ecf20Sopenharmony_ci		    fcport->sq_pbl, fcport->sq_pbl_dma);
13038c2ecf20Sopenharmony_ci	if (fcport->sq)
13048c2ecf20Sopenharmony_ci		dma_free_coherent(&qedf->pdev->dev, fcport->sq_mem_size,
13058c2ecf20Sopenharmony_ci		    fcport->sq, fcport->sq_dma);
13068c2ecf20Sopenharmony_ci}
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_cistatic int qedf_offload_connection(struct qedf_ctx *qedf,
13098c2ecf20Sopenharmony_ci	struct qedf_rport *fcport)
13108c2ecf20Sopenharmony_ci{
13118c2ecf20Sopenharmony_ci	struct qed_fcoe_params_offload conn_info;
13128c2ecf20Sopenharmony_ci	u32 port_id;
13138c2ecf20Sopenharmony_ci	int rval;
13148c2ecf20Sopenharmony_ci	uint16_t total_sqe = (fcport->sq_mem_size / sizeof(struct fcoe_wqe));
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, "Offloading connection "
13178c2ecf20Sopenharmony_ci		   "portid=%06x.\n", fcport->rdata->ids.port_id);
13188c2ecf20Sopenharmony_ci	rval = qed_ops->acquire_conn(qedf->cdev, &fcport->handle,
13198c2ecf20Sopenharmony_ci	    &fcport->fw_cid, &fcport->p_doorbell);
13208c2ecf20Sopenharmony_ci	if (rval) {
13218c2ecf20Sopenharmony_ci		QEDF_WARN(&(qedf->dbg_ctx), "Could not acquire connection "
13228c2ecf20Sopenharmony_ci			   "for portid=%06x.\n", fcport->rdata->ids.port_id);
13238c2ecf20Sopenharmony_ci		rval = 1; /* For some reason qed returns 0 on failure here */
13248c2ecf20Sopenharmony_ci		goto out;
13258c2ecf20Sopenharmony_ci	}
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, "portid=%06x "
13288c2ecf20Sopenharmony_ci		   "fw_cid=%08x handle=%d.\n", fcport->rdata->ids.port_id,
13298c2ecf20Sopenharmony_ci		   fcport->fw_cid, fcport->handle);
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	memset(&conn_info, 0, sizeof(struct qed_fcoe_params_offload));
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci	/* Fill in the offload connection info */
13348c2ecf20Sopenharmony_ci	conn_info.sq_pbl_addr = fcport->sq_pbl_dma;
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci	conn_info.sq_curr_page_addr = (dma_addr_t)(*(u64 *)fcport->sq_pbl);
13378c2ecf20Sopenharmony_ci	conn_info.sq_next_page_addr =
13388c2ecf20Sopenharmony_ci	    (dma_addr_t)(*(u64 *)(fcport->sq_pbl + 8));
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	/* Need to use our FCoE MAC for the offload session */
13418c2ecf20Sopenharmony_ci	ether_addr_copy(conn_info.src_mac, qedf->data_src_addr);
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci	ether_addr_copy(conn_info.dst_mac, qedf->ctlr.dest_addr);
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	conn_info.tx_max_fc_pay_len = fcport->rdata->maxframe_size;
13468c2ecf20Sopenharmony_ci	conn_info.e_d_tov_timer_val = qedf->lport->e_d_tov;
13478c2ecf20Sopenharmony_ci	conn_info.rec_tov_timer_val = 3; /* I think this is what E3 was */
13488c2ecf20Sopenharmony_ci	conn_info.rx_max_fc_pay_len = fcport->rdata->maxframe_size;
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ci	/* Set VLAN data */
13518c2ecf20Sopenharmony_ci	conn_info.vlan_tag = qedf->vlan_id <<
13528c2ecf20Sopenharmony_ci	    FCOE_CONN_OFFLOAD_RAMROD_DATA_VLAN_ID_SHIFT;
13538c2ecf20Sopenharmony_ci	conn_info.vlan_tag |=
13548c2ecf20Sopenharmony_ci	    qedf->prio << FCOE_CONN_OFFLOAD_RAMROD_DATA_PRIORITY_SHIFT;
13558c2ecf20Sopenharmony_ci	conn_info.flags |= (FCOE_CONN_OFFLOAD_RAMROD_DATA_B_VLAN_FLAG_MASK <<
13568c2ecf20Sopenharmony_ci	    FCOE_CONN_OFFLOAD_RAMROD_DATA_B_VLAN_FLAG_SHIFT);
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci	/* Set host port source id */
13598c2ecf20Sopenharmony_ci	port_id = fc_host_port_id(qedf->lport->host);
13608c2ecf20Sopenharmony_ci	fcport->sid = port_id;
13618c2ecf20Sopenharmony_ci	conn_info.s_id.addr_hi = (port_id & 0x000000FF);
13628c2ecf20Sopenharmony_ci	conn_info.s_id.addr_mid = (port_id & 0x0000FF00) >> 8;
13638c2ecf20Sopenharmony_ci	conn_info.s_id.addr_lo = (port_id & 0x00FF0000) >> 16;
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci	conn_info.max_conc_seqs_c3 = fcport->rdata->max_seq;
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci	/* Set remote port destination id */
13688c2ecf20Sopenharmony_ci	port_id = fcport->rdata->rport->port_id;
13698c2ecf20Sopenharmony_ci	conn_info.d_id.addr_hi = (port_id & 0x000000FF);
13708c2ecf20Sopenharmony_ci	conn_info.d_id.addr_mid = (port_id & 0x0000FF00) >> 8;
13718c2ecf20Sopenharmony_ci	conn_info.d_id.addr_lo = (port_id & 0x00FF0000) >> 16;
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci	conn_info.def_q_idx = 0; /* Default index for send queue? */
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	/* Set FC-TAPE specific flags if needed */
13768c2ecf20Sopenharmony_ci	if (fcport->dev_type == QEDF_RPORT_TYPE_TAPE) {
13778c2ecf20Sopenharmony_ci		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN,
13788c2ecf20Sopenharmony_ci		    "Enable CONF, REC for portid=%06x.\n",
13798c2ecf20Sopenharmony_ci		    fcport->rdata->ids.port_id);
13808c2ecf20Sopenharmony_ci		conn_info.flags |= 1 <<
13818c2ecf20Sopenharmony_ci		    FCOE_CONN_OFFLOAD_RAMROD_DATA_B_CONF_REQ_SHIFT;
13828c2ecf20Sopenharmony_ci		conn_info.flags |=
13838c2ecf20Sopenharmony_ci		    ((fcport->rdata->sp_features & FC_SP_FT_SEQC) ? 1 : 0) <<
13848c2ecf20Sopenharmony_ci		    FCOE_CONN_OFFLOAD_RAMROD_DATA_B_REC_VALID_SHIFT;
13858c2ecf20Sopenharmony_ci	}
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_ci	rval = qed_ops->offload_conn(qedf->cdev, fcport->handle, &conn_info);
13888c2ecf20Sopenharmony_ci	if (rval) {
13898c2ecf20Sopenharmony_ci		QEDF_WARN(&(qedf->dbg_ctx), "Could not offload connection "
13908c2ecf20Sopenharmony_ci			   "for portid=%06x.\n", fcport->rdata->ids.port_id);
13918c2ecf20Sopenharmony_ci		goto out_free_conn;
13928c2ecf20Sopenharmony_ci	} else
13938c2ecf20Sopenharmony_ci		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, "Offload "
13948c2ecf20Sopenharmony_ci			   "succeeded portid=%06x total_sqe=%d.\n",
13958c2ecf20Sopenharmony_ci			   fcport->rdata->ids.port_id, total_sqe);
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	spin_lock_init(&fcport->rport_lock);
13988c2ecf20Sopenharmony_ci	atomic_set(&fcport->free_sqes, total_sqe);
13998c2ecf20Sopenharmony_ci	return 0;
14008c2ecf20Sopenharmony_ciout_free_conn:
14018c2ecf20Sopenharmony_ci	qed_ops->release_conn(qedf->cdev, fcport->handle);
14028c2ecf20Sopenharmony_ciout:
14038c2ecf20Sopenharmony_ci	return rval;
14048c2ecf20Sopenharmony_ci}
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci#define QEDF_TERM_BUFF_SIZE		10
14078c2ecf20Sopenharmony_cistatic void qedf_upload_connection(struct qedf_ctx *qedf,
14088c2ecf20Sopenharmony_ci	struct qedf_rport *fcport)
14098c2ecf20Sopenharmony_ci{
14108c2ecf20Sopenharmony_ci	void *term_params;
14118c2ecf20Sopenharmony_ci	dma_addr_t term_params_dma;
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	/* Term params needs to be a DMA coherent buffer as qed shared the
14148c2ecf20Sopenharmony_ci	 * physical DMA address with the firmware. The buffer may be used in
14158c2ecf20Sopenharmony_ci	 * the receive path so we may eventually have to move this.
14168c2ecf20Sopenharmony_ci	 */
14178c2ecf20Sopenharmony_ci	term_params = dma_alloc_coherent(&qedf->pdev->dev, QEDF_TERM_BUFF_SIZE,
14188c2ecf20Sopenharmony_ci		&term_params_dma, GFP_KERNEL);
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, "Uploading connection "
14218c2ecf20Sopenharmony_ci		   "port_id=%06x.\n", fcport->rdata->ids.port_id);
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	qed_ops->destroy_conn(qedf->cdev, fcport->handle, term_params_dma);
14248c2ecf20Sopenharmony_ci	qed_ops->release_conn(qedf->cdev, fcport->handle);
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci	dma_free_coherent(&qedf->pdev->dev, QEDF_TERM_BUFF_SIZE, term_params,
14278c2ecf20Sopenharmony_ci	    term_params_dma);
14288c2ecf20Sopenharmony_ci}
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_cistatic void qedf_cleanup_fcport(struct qedf_ctx *qedf,
14318c2ecf20Sopenharmony_ci	struct qedf_rport *fcport)
14328c2ecf20Sopenharmony_ci{
14338c2ecf20Sopenharmony_ci	struct fc_rport_priv *rdata = fcport->rdata;
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_CONN, "Cleaning up portid=%06x.\n",
14368c2ecf20Sopenharmony_ci	    fcport->rdata->ids.port_id);
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci	/* Flush any remaining i/o's before we upload the connection */
14398c2ecf20Sopenharmony_ci	qedf_flush_active_ios(fcport, -1);
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci	if (test_and_clear_bit(QEDF_RPORT_SESSION_READY, &fcport->flags))
14428c2ecf20Sopenharmony_ci		qedf_upload_connection(qedf, fcport);
14438c2ecf20Sopenharmony_ci	qedf_free_sq(qedf, fcport);
14448c2ecf20Sopenharmony_ci	fcport->rdata = NULL;
14458c2ecf20Sopenharmony_ci	fcport->qedf = NULL;
14468c2ecf20Sopenharmony_ci	kref_put(&rdata->kref, fc_rport_destroy);
14478c2ecf20Sopenharmony_ci}
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci/*
14508c2ecf20Sopenharmony_ci * This event_callback is called after successful completion of libfc
14518c2ecf20Sopenharmony_ci * initiated target login. qedf can proceed with initiating the session
14528c2ecf20Sopenharmony_ci * establishment.
14538c2ecf20Sopenharmony_ci */
14548c2ecf20Sopenharmony_cistatic void qedf_rport_event_handler(struct fc_lport *lport,
14558c2ecf20Sopenharmony_ci				struct fc_rport_priv *rdata,
14568c2ecf20Sopenharmony_ci				enum fc_rport_event event)
14578c2ecf20Sopenharmony_ci{
14588c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf = lport_priv(lport);
14598c2ecf20Sopenharmony_ci	struct fc_rport *rport = rdata->rport;
14608c2ecf20Sopenharmony_ci	struct fc_rport_libfc_priv *rp;
14618c2ecf20Sopenharmony_ci	struct qedf_rport *fcport;
14628c2ecf20Sopenharmony_ci	u32 port_id;
14638c2ecf20Sopenharmony_ci	int rval;
14648c2ecf20Sopenharmony_ci	unsigned long flags;
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "event = %d, "
14678c2ecf20Sopenharmony_ci		   "port_id = 0x%x\n", event, rdata->ids.port_id);
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	switch (event) {
14708c2ecf20Sopenharmony_ci	case RPORT_EV_READY:
14718c2ecf20Sopenharmony_ci		if (!rport) {
14728c2ecf20Sopenharmony_ci			QEDF_WARN(&(qedf->dbg_ctx), "rport is NULL.\n");
14738c2ecf20Sopenharmony_ci			break;
14748c2ecf20Sopenharmony_ci		}
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci		rp = rport->dd_data;
14778c2ecf20Sopenharmony_ci		fcport = (struct qedf_rport *)&rp[1];
14788c2ecf20Sopenharmony_ci		fcport->qedf = qedf;
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci		if (atomic_read(&qedf->num_offloads) >= QEDF_MAX_SESSIONS) {
14818c2ecf20Sopenharmony_ci			QEDF_ERR(&(qedf->dbg_ctx), "Not offloading "
14828c2ecf20Sopenharmony_ci			    "portid=0x%x as max number of offloaded sessions "
14838c2ecf20Sopenharmony_ci			    "reached.\n", rdata->ids.port_id);
14848c2ecf20Sopenharmony_ci			return;
14858c2ecf20Sopenharmony_ci		}
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_ci		/*
14888c2ecf20Sopenharmony_ci		 * Don't try to offload the session again. Can happen when we
14898c2ecf20Sopenharmony_ci		 * get an ADISC
14908c2ecf20Sopenharmony_ci		 */
14918c2ecf20Sopenharmony_ci		if (test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) {
14928c2ecf20Sopenharmony_ci			QEDF_WARN(&(qedf->dbg_ctx), "Session already "
14938c2ecf20Sopenharmony_ci				   "offloaded, portid=0x%x.\n",
14948c2ecf20Sopenharmony_ci				   rdata->ids.port_id);
14958c2ecf20Sopenharmony_ci			return;
14968c2ecf20Sopenharmony_ci		}
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_ci		if (rport->port_id == FC_FID_DIR_SERV) {
14998c2ecf20Sopenharmony_ci			/*
15008c2ecf20Sopenharmony_ci			 * qedf_rport structure doesn't exist for
15018c2ecf20Sopenharmony_ci			 * directory server.
15028c2ecf20Sopenharmony_ci			 * We should not come here, as lport will
15038c2ecf20Sopenharmony_ci			 * take care of fabric login
15048c2ecf20Sopenharmony_ci			 */
15058c2ecf20Sopenharmony_ci			QEDF_WARN(&(qedf->dbg_ctx), "rport struct does not "
15068c2ecf20Sopenharmony_ci			    "exist for dir server port_id=%x\n",
15078c2ecf20Sopenharmony_ci			    rdata->ids.port_id);
15088c2ecf20Sopenharmony_ci			break;
15098c2ecf20Sopenharmony_ci		}
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci		if (rdata->spp_type != FC_TYPE_FCP) {
15128c2ecf20Sopenharmony_ci			QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
15138c2ecf20Sopenharmony_ci			    "Not offloading since spp type isn't FCP\n");
15148c2ecf20Sopenharmony_ci			break;
15158c2ecf20Sopenharmony_ci		}
15168c2ecf20Sopenharmony_ci		if (!(rdata->ids.roles & FC_RPORT_ROLE_FCP_TARGET)) {
15178c2ecf20Sopenharmony_ci			QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
15188c2ecf20Sopenharmony_ci			    "Not FCP target so not offloading\n");
15198c2ecf20Sopenharmony_ci			break;
15208c2ecf20Sopenharmony_ci		}
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci		/* Initial reference held on entry, so this can't fail */
15238c2ecf20Sopenharmony_ci		kref_get(&rdata->kref);
15248c2ecf20Sopenharmony_ci		fcport->rdata = rdata;
15258c2ecf20Sopenharmony_ci		fcport->rport = rport;
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci		rval = qedf_alloc_sq(qedf, fcport);
15288c2ecf20Sopenharmony_ci		if (rval) {
15298c2ecf20Sopenharmony_ci			qedf_cleanup_fcport(qedf, fcport);
15308c2ecf20Sopenharmony_ci			break;
15318c2ecf20Sopenharmony_ci		}
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_ci		/* Set device type */
15348c2ecf20Sopenharmony_ci		if (rdata->flags & FC_RP_FLAGS_RETRY &&
15358c2ecf20Sopenharmony_ci		    rdata->ids.roles & FC_RPORT_ROLE_FCP_TARGET &&
15368c2ecf20Sopenharmony_ci		    !(rdata->ids.roles & FC_RPORT_ROLE_FCP_INITIATOR)) {
15378c2ecf20Sopenharmony_ci			fcport->dev_type = QEDF_RPORT_TYPE_TAPE;
15388c2ecf20Sopenharmony_ci			QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
15398c2ecf20Sopenharmony_ci			    "portid=%06x is a TAPE device.\n",
15408c2ecf20Sopenharmony_ci			    rdata->ids.port_id);
15418c2ecf20Sopenharmony_ci		} else {
15428c2ecf20Sopenharmony_ci			fcport->dev_type = QEDF_RPORT_TYPE_DISK;
15438c2ecf20Sopenharmony_ci		}
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_ci		rval = qedf_offload_connection(qedf, fcport);
15468c2ecf20Sopenharmony_ci		if (rval) {
15478c2ecf20Sopenharmony_ci			qedf_cleanup_fcport(qedf, fcport);
15488c2ecf20Sopenharmony_ci			break;
15498c2ecf20Sopenharmony_ci		}
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ci		/* Add fcport to list of qedf_ctx list of offloaded ports */
15528c2ecf20Sopenharmony_ci		spin_lock_irqsave(&qedf->hba_lock, flags);
15538c2ecf20Sopenharmony_ci		list_add_rcu(&fcport->peers, &qedf->fcports);
15548c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&qedf->hba_lock, flags);
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_ci		/*
15578c2ecf20Sopenharmony_ci		 * Set the session ready bit to let everyone know that this
15588c2ecf20Sopenharmony_ci		 * connection is ready for I/O
15598c2ecf20Sopenharmony_ci		 */
15608c2ecf20Sopenharmony_ci		set_bit(QEDF_RPORT_SESSION_READY, &fcport->flags);
15618c2ecf20Sopenharmony_ci		atomic_inc(&qedf->num_offloads);
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_ci		break;
15648c2ecf20Sopenharmony_ci	case RPORT_EV_LOGO:
15658c2ecf20Sopenharmony_ci	case RPORT_EV_FAILED:
15668c2ecf20Sopenharmony_ci	case RPORT_EV_STOP:
15678c2ecf20Sopenharmony_ci		port_id = rdata->ids.port_id;
15688c2ecf20Sopenharmony_ci		if (port_id == FC_FID_DIR_SERV)
15698c2ecf20Sopenharmony_ci			break;
15708c2ecf20Sopenharmony_ci
15718c2ecf20Sopenharmony_ci		if (rdata->spp_type != FC_TYPE_FCP) {
15728c2ecf20Sopenharmony_ci			QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
15738c2ecf20Sopenharmony_ci			    "No action since spp type isn't FCP\n");
15748c2ecf20Sopenharmony_ci			break;
15758c2ecf20Sopenharmony_ci		}
15768c2ecf20Sopenharmony_ci		if (!(rdata->ids.roles & FC_RPORT_ROLE_FCP_TARGET)) {
15778c2ecf20Sopenharmony_ci			QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
15788c2ecf20Sopenharmony_ci			    "Not FCP target so no action\n");
15798c2ecf20Sopenharmony_ci			break;
15808c2ecf20Sopenharmony_ci		}
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci		if (!rport) {
15838c2ecf20Sopenharmony_ci			QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
15848c2ecf20Sopenharmony_ci			    "port_id=%x - rport notcreated Yet!!\n", port_id);
15858c2ecf20Sopenharmony_ci			break;
15868c2ecf20Sopenharmony_ci		}
15878c2ecf20Sopenharmony_ci		rp = rport->dd_data;
15888c2ecf20Sopenharmony_ci		/*
15898c2ecf20Sopenharmony_ci		 * Perform session upload. Note that rdata->peers is already
15908c2ecf20Sopenharmony_ci		 * removed from disc->rports list before we get this event.
15918c2ecf20Sopenharmony_ci		 */
15928c2ecf20Sopenharmony_ci		fcport = (struct qedf_rport *)&rp[1];
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_ci		spin_lock_irqsave(&fcport->rport_lock, flags);
15958c2ecf20Sopenharmony_ci		/* Only free this fcport if it is offloaded already */
15968c2ecf20Sopenharmony_ci		if (test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags) &&
15978c2ecf20Sopenharmony_ci		    !test_bit(QEDF_RPORT_UPLOADING_CONNECTION,
15988c2ecf20Sopenharmony_ci		    &fcport->flags)) {
15998c2ecf20Sopenharmony_ci			set_bit(QEDF_RPORT_UPLOADING_CONNECTION,
16008c2ecf20Sopenharmony_ci				&fcport->flags);
16018c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&fcport->rport_lock, flags);
16028c2ecf20Sopenharmony_ci			qedf_cleanup_fcport(qedf, fcport);
16038c2ecf20Sopenharmony_ci			/*
16048c2ecf20Sopenharmony_ci			 * Remove fcport to list of qedf_ctx list of offloaded
16058c2ecf20Sopenharmony_ci			 * ports
16068c2ecf20Sopenharmony_ci			 */
16078c2ecf20Sopenharmony_ci			spin_lock_irqsave(&qedf->hba_lock, flags);
16088c2ecf20Sopenharmony_ci			list_del_rcu(&fcport->peers);
16098c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&qedf->hba_lock, flags);
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci			clear_bit(QEDF_RPORT_UPLOADING_CONNECTION,
16128c2ecf20Sopenharmony_ci			    &fcport->flags);
16138c2ecf20Sopenharmony_ci			atomic_dec(&qedf->num_offloads);
16148c2ecf20Sopenharmony_ci		} else {
16158c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&fcport->rport_lock, flags);
16168c2ecf20Sopenharmony_ci		}
16178c2ecf20Sopenharmony_ci		break;
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci	case RPORT_EV_NONE:
16208c2ecf20Sopenharmony_ci		break;
16218c2ecf20Sopenharmony_ci	}
16228c2ecf20Sopenharmony_ci}
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_cistatic void qedf_abort_io(struct fc_lport *lport)
16258c2ecf20Sopenharmony_ci{
16268c2ecf20Sopenharmony_ci	/* NO-OP but need to fill in the template */
16278c2ecf20Sopenharmony_ci}
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_cistatic void qedf_fcp_cleanup(struct fc_lport *lport)
16308c2ecf20Sopenharmony_ci{
16318c2ecf20Sopenharmony_ci	/*
16328c2ecf20Sopenharmony_ci	 * NO-OP but need to fill in template to prevent a NULL
16338c2ecf20Sopenharmony_ci	 * function pointer dereference during link down. I/Os
16348c2ecf20Sopenharmony_ci	 * will be flushed when port is uploaded.
16358c2ecf20Sopenharmony_ci	 */
16368c2ecf20Sopenharmony_ci}
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_cistatic struct libfc_function_template qedf_lport_template = {
16398c2ecf20Sopenharmony_ci	.frame_send		= qedf_xmit,
16408c2ecf20Sopenharmony_ci	.fcp_abort_io		= qedf_abort_io,
16418c2ecf20Sopenharmony_ci	.fcp_cleanup		= qedf_fcp_cleanup,
16428c2ecf20Sopenharmony_ci	.rport_event_callback	= qedf_rport_event_handler,
16438c2ecf20Sopenharmony_ci	.elsct_send		= qedf_elsct_send,
16448c2ecf20Sopenharmony_ci};
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_cistatic void qedf_fcoe_ctlr_setup(struct qedf_ctx *qedf)
16478c2ecf20Sopenharmony_ci{
16488c2ecf20Sopenharmony_ci	fcoe_ctlr_init(&qedf->ctlr, FIP_MODE_AUTO);
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci	qedf->ctlr.send = qedf_fip_send;
16518c2ecf20Sopenharmony_ci	qedf->ctlr.get_src_addr = qedf_get_src_mac;
16528c2ecf20Sopenharmony_ci	ether_addr_copy(qedf->ctlr.ctl_src_addr, qedf->mac);
16538c2ecf20Sopenharmony_ci}
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_cistatic void qedf_setup_fdmi(struct qedf_ctx *qedf)
16568c2ecf20Sopenharmony_ci{
16578c2ecf20Sopenharmony_ci	struct fc_lport *lport = qedf->lport;
16588c2ecf20Sopenharmony_ci	u8 buf[8];
16598c2ecf20Sopenharmony_ci	int pos;
16608c2ecf20Sopenharmony_ci	uint32_t i;
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_ci	/*
16638c2ecf20Sopenharmony_ci	 * fdmi_enabled needs to be set for libfc
16648c2ecf20Sopenharmony_ci	 * to execute FDMI registration
16658c2ecf20Sopenharmony_ci	 */
16668c2ecf20Sopenharmony_ci	lport->fdmi_enabled = 1;
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci	/*
16698c2ecf20Sopenharmony_ci	 * Setup the necessary fc_host attributes to that will be used to fill
16708c2ecf20Sopenharmony_ci	 * in the FDMI information.
16718c2ecf20Sopenharmony_ci	 */
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci	/* Get the PCI-e Device Serial Number Capability */
16748c2ecf20Sopenharmony_ci	pos = pci_find_ext_capability(qedf->pdev, PCI_EXT_CAP_ID_DSN);
16758c2ecf20Sopenharmony_ci	if (pos) {
16768c2ecf20Sopenharmony_ci		pos += 4;
16778c2ecf20Sopenharmony_ci		for (i = 0; i < 8; i++)
16788c2ecf20Sopenharmony_ci			pci_read_config_byte(qedf->pdev, pos + i, &buf[i]);
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci		snprintf(fc_host_serial_number(lport->host),
16818c2ecf20Sopenharmony_ci		    FC_SERIAL_NUMBER_SIZE,
16828c2ecf20Sopenharmony_ci		    "%02X%02X%02X%02X%02X%02X%02X%02X",
16838c2ecf20Sopenharmony_ci		    buf[7], buf[6], buf[5], buf[4],
16848c2ecf20Sopenharmony_ci		    buf[3], buf[2], buf[1], buf[0]);
16858c2ecf20Sopenharmony_ci	} else
16868c2ecf20Sopenharmony_ci		snprintf(fc_host_serial_number(lport->host),
16878c2ecf20Sopenharmony_ci		    FC_SERIAL_NUMBER_SIZE, "Unknown");
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_ci	snprintf(fc_host_manufacturer(lport->host),
16908c2ecf20Sopenharmony_ci	    FC_SERIAL_NUMBER_SIZE, "%s", "Marvell Semiconductor Inc.");
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_ci	if (qedf->pdev->device == QL45xxx) {
16938c2ecf20Sopenharmony_ci		snprintf(fc_host_model(lport->host),
16948c2ecf20Sopenharmony_ci			FC_SYMBOLIC_NAME_SIZE, "%s", "QL45xxx");
16958c2ecf20Sopenharmony_ci
16968c2ecf20Sopenharmony_ci		snprintf(fc_host_model_description(lport->host),
16978c2ecf20Sopenharmony_ci			FC_SYMBOLIC_NAME_SIZE, "%s",
16988c2ecf20Sopenharmony_ci			"Marvell FastLinQ QL45xxx FCoE Adapter");
16998c2ecf20Sopenharmony_ci	}
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ci	if (qedf->pdev->device == QL41xxx) {
17028c2ecf20Sopenharmony_ci		snprintf(fc_host_model(lport->host),
17038c2ecf20Sopenharmony_ci			FC_SYMBOLIC_NAME_SIZE, "%s", "QL41xxx");
17048c2ecf20Sopenharmony_ci
17058c2ecf20Sopenharmony_ci		snprintf(fc_host_model_description(lport->host),
17068c2ecf20Sopenharmony_ci			FC_SYMBOLIC_NAME_SIZE, "%s",
17078c2ecf20Sopenharmony_ci			"Marvell FastLinQ QL41xxx FCoE Adapter");
17088c2ecf20Sopenharmony_ci	}
17098c2ecf20Sopenharmony_ci
17108c2ecf20Sopenharmony_ci	snprintf(fc_host_hardware_version(lport->host),
17118c2ecf20Sopenharmony_ci	    FC_VERSION_STRING_SIZE, "Rev %d", qedf->pdev->revision);
17128c2ecf20Sopenharmony_ci
17138c2ecf20Sopenharmony_ci	snprintf(fc_host_driver_version(lport->host),
17148c2ecf20Sopenharmony_ci	    FC_VERSION_STRING_SIZE, "%s", QEDF_VERSION);
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ci	snprintf(fc_host_firmware_version(lport->host),
17178c2ecf20Sopenharmony_ci	    FC_VERSION_STRING_SIZE, "%d.%d.%d.%d",
17188c2ecf20Sopenharmony_ci	    FW_MAJOR_VERSION, FW_MINOR_VERSION, FW_REVISION_VERSION,
17198c2ecf20Sopenharmony_ci	    FW_ENGINEERING_VERSION);
17208c2ecf20Sopenharmony_ci
17218c2ecf20Sopenharmony_ci}
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_cistatic int qedf_lport_setup(struct qedf_ctx *qedf)
17248c2ecf20Sopenharmony_ci{
17258c2ecf20Sopenharmony_ci	struct fc_lport *lport = qedf->lport;
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_ci	lport->link_up = 0;
17288c2ecf20Sopenharmony_ci	lport->max_retry_count = QEDF_FLOGI_RETRY_CNT;
17298c2ecf20Sopenharmony_ci	lport->max_rport_retry_count = QEDF_RPORT_RETRY_CNT;
17308c2ecf20Sopenharmony_ci	lport->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
17318c2ecf20Sopenharmony_ci	    FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
17328c2ecf20Sopenharmony_ci	lport->boot_time = jiffies;
17338c2ecf20Sopenharmony_ci	lport->e_d_tov = 2 * 1000;
17348c2ecf20Sopenharmony_ci	lport->r_a_tov = 10 * 1000;
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_ci	/* Set NPIV support */
17378c2ecf20Sopenharmony_ci	lport->does_npiv = 1;
17388c2ecf20Sopenharmony_ci	fc_host_max_npiv_vports(lport->host) = QEDF_MAX_NPIV;
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	fc_set_wwnn(lport, qedf->wwnn);
17418c2ecf20Sopenharmony_ci	fc_set_wwpn(lport, qedf->wwpn);
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci	if (fcoe_libfc_config(lport, &qedf->ctlr, &qedf_lport_template, 0)) {
17448c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx,
17458c2ecf20Sopenharmony_ci			 "fcoe_libfc_config failed.\n");
17468c2ecf20Sopenharmony_ci		return -ENOMEM;
17478c2ecf20Sopenharmony_ci	}
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_ci	/* Allocate the exchange manager */
17508c2ecf20Sopenharmony_ci	fc_exch_mgr_alloc(lport, FC_CLASS_3, FCOE_PARAMS_NUM_TASKS,
17518c2ecf20Sopenharmony_ci			  0xfffe, NULL);
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci	if (fc_lport_init_stats(lport))
17548c2ecf20Sopenharmony_ci		return -ENOMEM;
17558c2ecf20Sopenharmony_ci
17568c2ecf20Sopenharmony_ci	/* Finish lport config */
17578c2ecf20Sopenharmony_ci	fc_lport_config(lport);
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_ci	/* Set max frame size */
17608c2ecf20Sopenharmony_ci	fc_set_mfs(lport, QEDF_MFS);
17618c2ecf20Sopenharmony_ci	fc_host_maxframe_size(lport->host) = lport->mfs;
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ci	/* Set default dev_loss_tmo based on module parameter */
17648c2ecf20Sopenharmony_ci	fc_host_dev_loss_tmo(lport->host) = qedf_dev_loss_tmo;
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ci	/* Set symbolic node name */
17678c2ecf20Sopenharmony_ci	if (qedf->pdev->device == QL45xxx)
17688c2ecf20Sopenharmony_ci		snprintf(fc_host_symbolic_name(lport->host), 256,
17698c2ecf20Sopenharmony_ci			"Marvell FastLinQ 45xxx FCoE v%s", QEDF_VERSION);
17708c2ecf20Sopenharmony_ci
17718c2ecf20Sopenharmony_ci	if (qedf->pdev->device == QL41xxx)
17728c2ecf20Sopenharmony_ci		snprintf(fc_host_symbolic_name(lport->host), 256,
17738c2ecf20Sopenharmony_ci			"Marvell FastLinQ 41xxx FCoE v%s", QEDF_VERSION);
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_ci	qedf_setup_fdmi(qedf);
17768c2ecf20Sopenharmony_ci
17778c2ecf20Sopenharmony_ci	return 0;
17788c2ecf20Sopenharmony_ci}
17798c2ecf20Sopenharmony_ci
17808c2ecf20Sopenharmony_ci/*
17818c2ecf20Sopenharmony_ci * NPIV functions
17828c2ecf20Sopenharmony_ci */
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_cistatic int qedf_vport_libfc_config(struct fc_vport *vport,
17858c2ecf20Sopenharmony_ci	struct fc_lport *lport)
17868c2ecf20Sopenharmony_ci{
17878c2ecf20Sopenharmony_ci	lport->link_up = 0;
17888c2ecf20Sopenharmony_ci	lport->qfull = 0;
17898c2ecf20Sopenharmony_ci	lport->max_retry_count = QEDF_FLOGI_RETRY_CNT;
17908c2ecf20Sopenharmony_ci	lport->max_rport_retry_count = QEDF_RPORT_RETRY_CNT;
17918c2ecf20Sopenharmony_ci	lport->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
17928c2ecf20Sopenharmony_ci	    FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
17938c2ecf20Sopenharmony_ci	lport->boot_time = jiffies;
17948c2ecf20Sopenharmony_ci	lport->e_d_tov = 2 * 1000;
17958c2ecf20Sopenharmony_ci	lport->r_a_tov = 10 * 1000;
17968c2ecf20Sopenharmony_ci	lport->does_npiv = 1; /* Temporary until we add NPIV support */
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_ci	/* Allocate stats for vport */
17998c2ecf20Sopenharmony_ci	if (fc_lport_init_stats(lport))
18008c2ecf20Sopenharmony_ci		return -ENOMEM;
18018c2ecf20Sopenharmony_ci
18028c2ecf20Sopenharmony_ci	/* Finish lport config */
18038c2ecf20Sopenharmony_ci	fc_lport_config(lport);
18048c2ecf20Sopenharmony_ci
18058c2ecf20Sopenharmony_ci	/* offload related configuration */
18068c2ecf20Sopenharmony_ci	lport->crc_offload = 0;
18078c2ecf20Sopenharmony_ci	lport->seq_offload = 0;
18088c2ecf20Sopenharmony_ci	lport->lro_enabled = 0;
18098c2ecf20Sopenharmony_ci	lport->lro_xid = 0;
18108c2ecf20Sopenharmony_ci	lport->lso_max = 0;
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_ci	return 0;
18138c2ecf20Sopenharmony_ci}
18148c2ecf20Sopenharmony_ci
18158c2ecf20Sopenharmony_cistatic int qedf_vport_create(struct fc_vport *vport, bool disabled)
18168c2ecf20Sopenharmony_ci{
18178c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = vport_to_shost(vport);
18188c2ecf20Sopenharmony_ci	struct fc_lport *n_port = shost_priv(shost);
18198c2ecf20Sopenharmony_ci	struct fc_lport *vn_port;
18208c2ecf20Sopenharmony_ci	struct qedf_ctx *base_qedf = lport_priv(n_port);
18218c2ecf20Sopenharmony_ci	struct qedf_ctx *vport_qedf;
18228c2ecf20Sopenharmony_ci
18238c2ecf20Sopenharmony_ci	char buf[32];
18248c2ecf20Sopenharmony_ci	int rc = 0;
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci	rc = fcoe_validate_vport_create(vport);
18278c2ecf20Sopenharmony_ci	if (rc) {
18288c2ecf20Sopenharmony_ci		fcoe_wwn_to_str(vport->port_name, buf, sizeof(buf));
18298c2ecf20Sopenharmony_ci		QEDF_WARN(&(base_qedf->dbg_ctx), "Failed to create vport, "
18308c2ecf20Sopenharmony_ci			   "WWPN (0x%s) already exists.\n", buf);
18318c2ecf20Sopenharmony_ci		return rc;
18328c2ecf20Sopenharmony_ci	}
18338c2ecf20Sopenharmony_ci
18348c2ecf20Sopenharmony_ci	if (atomic_read(&base_qedf->link_state) != QEDF_LINK_UP) {
18358c2ecf20Sopenharmony_ci		QEDF_WARN(&(base_qedf->dbg_ctx), "Cannot create vport "
18368c2ecf20Sopenharmony_ci			   "because link is not up.\n");
18378c2ecf20Sopenharmony_ci		return -EIO;
18388c2ecf20Sopenharmony_ci	}
18398c2ecf20Sopenharmony_ci
18408c2ecf20Sopenharmony_ci	vn_port = libfc_vport_create(vport, sizeof(struct qedf_ctx));
18418c2ecf20Sopenharmony_ci	if (!vn_port) {
18428c2ecf20Sopenharmony_ci		QEDF_WARN(&(base_qedf->dbg_ctx), "Could not create lport "
18438c2ecf20Sopenharmony_ci			   "for vport.\n");
18448c2ecf20Sopenharmony_ci		return -ENOMEM;
18458c2ecf20Sopenharmony_ci	}
18468c2ecf20Sopenharmony_ci
18478c2ecf20Sopenharmony_ci	fcoe_wwn_to_str(vport->port_name, buf, sizeof(buf));
18488c2ecf20Sopenharmony_ci	QEDF_ERR(&(base_qedf->dbg_ctx), "Creating NPIV port, WWPN=%s.\n",
18498c2ecf20Sopenharmony_ci	    buf);
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci	/* Copy some fields from base_qedf */
18528c2ecf20Sopenharmony_ci	vport_qedf = lport_priv(vn_port);
18538c2ecf20Sopenharmony_ci	memcpy(vport_qedf, base_qedf, sizeof(struct qedf_ctx));
18548c2ecf20Sopenharmony_ci
18558c2ecf20Sopenharmony_ci	/* Set qedf data specific to this vport */
18568c2ecf20Sopenharmony_ci	vport_qedf->lport = vn_port;
18578c2ecf20Sopenharmony_ci	/* Use same hba_lock as base_qedf */
18588c2ecf20Sopenharmony_ci	vport_qedf->hba_lock = base_qedf->hba_lock;
18598c2ecf20Sopenharmony_ci	vport_qedf->pdev = base_qedf->pdev;
18608c2ecf20Sopenharmony_ci	vport_qedf->cmd_mgr = base_qedf->cmd_mgr;
18618c2ecf20Sopenharmony_ci	init_completion(&vport_qedf->flogi_compl);
18628c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&vport_qedf->fcports);
18638c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&vport_qedf->stag_work, qedf_stag_change_work);
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_ci	rc = qedf_vport_libfc_config(vport, vn_port);
18668c2ecf20Sopenharmony_ci	if (rc) {
18678c2ecf20Sopenharmony_ci		QEDF_ERR(&(base_qedf->dbg_ctx), "Could not allocate memory "
18688c2ecf20Sopenharmony_ci		    "for lport stats.\n");
18698c2ecf20Sopenharmony_ci		goto err;
18708c2ecf20Sopenharmony_ci	}
18718c2ecf20Sopenharmony_ci
18728c2ecf20Sopenharmony_ci	fc_set_wwnn(vn_port, vport->node_name);
18738c2ecf20Sopenharmony_ci	fc_set_wwpn(vn_port, vport->port_name);
18748c2ecf20Sopenharmony_ci	vport_qedf->wwnn = vn_port->wwnn;
18758c2ecf20Sopenharmony_ci	vport_qedf->wwpn = vn_port->wwpn;
18768c2ecf20Sopenharmony_ci
18778c2ecf20Sopenharmony_ci	vn_port->host->transportt = qedf_fc_vport_transport_template;
18788c2ecf20Sopenharmony_ci	vn_port->host->can_queue = FCOE_PARAMS_NUM_TASKS;
18798c2ecf20Sopenharmony_ci	vn_port->host->max_lun = qedf_max_lun;
18808c2ecf20Sopenharmony_ci	vn_port->host->sg_tablesize = QEDF_MAX_BDS_PER_CMD;
18818c2ecf20Sopenharmony_ci	vn_port->host->max_cmd_len = QEDF_MAX_CDB_LEN;
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_ci	rc = scsi_add_host(vn_port->host, &vport->dev);
18848c2ecf20Sopenharmony_ci	if (rc) {
18858c2ecf20Sopenharmony_ci		QEDF_WARN(&base_qedf->dbg_ctx,
18868c2ecf20Sopenharmony_ci			  "Error adding Scsi_Host rc=0x%x.\n", rc);
18878c2ecf20Sopenharmony_ci		goto err;
18888c2ecf20Sopenharmony_ci	}
18898c2ecf20Sopenharmony_ci
18908c2ecf20Sopenharmony_ci	/* Set default dev_loss_tmo based on module parameter */
18918c2ecf20Sopenharmony_ci	fc_host_dev_loss_tmo(vn_port->host) = qedf_dev_loss_tmo;
18928c2ecf20Sopenharmony_ci
18938c2ecf20Sopenharmony_ci	/* Init libfc stuffs */
18948c2ecf20Sopenharmony_ci	memcpy(&vn_port->tt, &qedf_lport_template,
18958c2ecf20Sopenharmony_ci		sizeof(qedf_lport_template));
18968c2ecf20Sopenharmony_ci	fc_exch_init(vn_port);
18978c2ecf20Sopenharmony_ci	fc_elsct_init(vn_port);
18988c2ecf20Sopenharmony_ci	fc_lport_init(vn_port);
18998c2ecf20Sopenharmony_ci	fc_disc_init(vn_port);
19008c2ecf20Sopenharmony_ci	fc_disc_config(vn_port, vn_port);
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci
19038c2ecf20Sopenharmony_ci	/* Allocate the exchange manager */
19048c2ecf20Sopenharmony_ci	shost = vport_to_shost(vport);
19058c2ecf20Sopenharmony_ci	n_port = shost_priv(shost);
19068c2ecf20Sopenharmony_ci	fc_exch_mgr_list_clone(n_port, vn_port);
19078c2ecf20Sopenharmony_ci
19088c2ecf20Sopenharmony_ci	/* Set max frame size */
19098c2ecf20Sopenharmony_ci	fc_set_mfs(vn_port, QEDF_MFS);
19108c2ecf20Sopenharmony_ci
19118c2ecf20Sopenharmony_ci	fc_host_port_type(vn_port->host) = FC_PORTTYPE_UNKNOWN;
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_ci	if (disabled) {
19148c2ecf20Sopenharmony_ci		fc_vport_set_state(vport, FC_VPORT_DISABLED);
19158c2ecf20Sopenharmony_ci	} else {
19168c2ecf20Sopenharmony_ci		vn_port->boot_time = jiffies;
19178c2ecf20Sopenharmony_ci		fc_fabric_login(vn_port);
19188c2ecf20Sopenharmony_ci		fc_vport_setlink(vn_port);
19198c2ecf20Sopenharmony_ci	}
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_ci	/* Set symbolic node name */
19228c2ecf20Sopenharmony_ci	if (base_qedf->pdev->device == QL45xxx)
19238c2ecf20Sopenharmony_ci		snprintf(fc_host_symbolic_name(vn_port->host), 256,
19248c2ecf20Sopenharmony_ci			 "Marvell FastLinQ 45xxx FCoE v%s", QEDF_VERSION);
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_ci	if (base_qedf->pdev->device == QL41xxx)
19278c2ecf20Sopenharmony_ci		snprintf(fc_host_symbolic_name(vn_port->host), 256,
19288c2ecf20Sopenharmony_ci			 "Marvell FastLinQ 41xxx FCoE v%s", QEDF_VERSION);
19298c2ecf20Sopenharmony_ci
19308c2ecf20Sopenharmony_ci	/* Set supported speed */
19318c2ecf20Sopenharmony_ci	fc_host_supported_speeds(vn_port->host) = n_port->link_supported_speeds;
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci	/* Set speed */
19348c2ecf20Sopenharmony_ci	vn_port->link_speed = n_port->link_speed;
19358c2ecf20Sopenharmony_ci
19368c2ecf20Sopenharmony_ci	/* Set port type */
19378c2ecf20Sopenharmony_ci	fc_host_port_type(vn_port->host) = FC_PORTTYPE_NPIV;
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_ci	/* Set maxframe size */
19408c2ecf20Sopenharmony_ci	fc_host_maxframe_size(vn_port->host) = n_port->mfs;
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_ci	QEDF_INFO(&(base_qedf->dbg_ctx), QEDF_LOG_NPIV, "vn_port=%p.\n",
19438c2ecf20Sopenharmony_ci		   vn_port);
19448c2ecf20Sopenharmony_ci
19458c2ecf20Sopenharmony_ci	/* Set up debug context for vport */
19468c2ecf20Sopenharmony_ci	vport_qedf->dbg_ctx.host_no = vn_port->host->host_no;
19478c2ecf20Sopenharmony_ci	vport_qedf->dbg_ctx.pdev = base_qedf->pdev;
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_ci	return 0;
19508c2ecf20Sopenharmony_ci
19518c2ecf20Sopenharmony_cierr:
19528c2ecf20Sopenharmony_ci	scsi_host_put(vn_port->host);
19538c2ecf20Sopenharmony_ci	return rc;
19548c2ecf20Sopenharmony_ci}
19558c2ecf20Sopenharmony_ci
19568c2ecf20Sopenharmony_cistatic int qedf_vport_destroy(struct fc_vport *vport)
19578c2ecf20Sopenharmony_ci{
19588c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = vport_to_shost(vport);
19598c2ecf20Sopenharmony_ci	struct fc_lport *n_port = shost_priv(shost);
19608c2ecf20Sopenharmony_ci	struct fc_lport *vn_port = vport->dd_data;
19618c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf = lport_priv(vn_port);
19628c2ecf20Sopenharmony_ci
19638c2ecf20Sopenharmony_ci	if (!qedf) {
19648c2ecf20Sopenharmony_ci		QEDF_ERR(NULL, "qedf is NULL.\n");
19658c2ecf20Sopenharmony_ci		goto out;
19668c2ecf20Sopenharmony_ci	}
19678c2ecf20Sopenharmony_ci
19688c2ecf20Sopenharmony_ci	/* Set unloading bit on vport qedf_ctx to prevent more I/O */
19698c2ecf20Sopenharmony_ci	set_bit(QEDF_UNLOADING, &qedf->flags);
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_ci	mutex_lock(&n_port->lp_mutex);
19728c2ecf20Sopenharmony_ci	list_del(&vn_port->list);
19738c2ecf20Sopenharmony_ci	mutex_unlock(&n_port->lp_mutex);
19748c2ecf20Sopenharmony_ci
19758c2ecf20Sopenharmony_ci	fc_fabric_logoff(vn_port);
19768c2ecf20Sopenharmony_ci	fc_lport_destroy(vn_port);
19778c2ecf20Sopenharmony_ci
19788c2ecf20Sopenharmony_ci	/* Detach from scsi-ml */
19798c2ecf20Sopenharmony_ci	fc_remove_host(vn_port->host);
19808c2ecf20Sopenharmony_ci	scsi_remove_host(vn_port->host);
19818c2ecf20Sopenharmony_ci
19828c2ecf20Sopenharmony_ci	/*
19838c2ecf20Sopenharmony_ci	 * Only try to release the exchange manager if the vn_port
19848c2ecf20Sopenharmony_ci	 * configuration is complete.
19858c2ecf20Sopenharmony_ci	 */
19868c2ecf20Sopenharmony_ci	if (vn_port->state == LPORT_ST_READY)
19878c2ecf20Sopenharmony_ci		fc_exch_mgr_free(vn_port);
19888c2ecf20Sopenharmony_ci
19898c2ecf20Sopenharmony_ci	/* Free memory used by statistical counters */
19908c2ecf20Sopenharmony_ci	fc_lport_free_stats(vn_port);
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_ci	/* Release Scsi_Host */
19938c2ecf20Sopenharmony_ci	scsi_host_put(vn_port->host);
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ciout:
19968c2ecf20Sopenharmony_ci	return 0;
19978c2ecf20Sopenharmony_ci}
19988c2ecf20Sopenharmony_ci
19998c2ecf20Sopenharmony_cistatic int qedf_vport_disable(struct fc_vport *vport, bool disable)
20008c2ecf20Sopenharmony_ci{
20018c2ecf20Sopenharmony_ci	struct fc_lport *lport = vport->dd_data;
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_ci	if (disable) {
20048c2ecf20Sopenharmony_ci		fc_vport_set_state(vport, FC_VPORT_DISABLED);
20058c2ecf20Sopenharmony_ci		fc_fabric_logoff(lport);
20068c2ecf20Sopenharmony_ci	} else {
20078c2ecf20Sopenharmony_ci		lport->boot_time = jiffies;
20088c2ecf20Sopenharmony_ci		fc_fabric_login(lport);
20098c2ecf20Sopenharmony_ci		fc_vport_setlink(lport);
20108c2ecf20Sopenharmony_ci	}
20118c2ecf20Sopenharmony_ci	return 0;
20128c2ecf20Sopenharmony_ci}
20138c2ecf20Sopenharmony_ci
20148c2ecf20Sopenharmony_ci/*
20158c2ecf20Sopenharmony_ci * During removal we need to wait for all the vports associated with a port
20168c2ecf20Sopenharmony_ci * to be destroyed so we avoid a race condition where libfc is still trying
20178c2ecf20Sopenharmony_ci * to reap vports while the driver remove function has already reaped the
20188c2ecf20Sopenharmony_ci * driver contexts associated with the physical port.
20198c2ecf20Sopenharmony_ci */
20208c2ecf20Sopenharmony_cistatic void qedf_wait_for_vport_destroy(struct qedf_ctx *qedf)
20218c2ecf20Sopenharmony_ci{
20228c2ecf20Sopenharmony_ci	struct fc_host_attrs *fc_host = shost_to_fc_host(qedf->lport->host);
20238c2ecf20Sopenharmony_ci
20248c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_NPIV,
20258c2ecf20Sopenharmony_ci	    "Entered.\n");
20268c2ecf20Sopenharmony_ci	while (fc_host->npiv_vports_inuse > 0) {
20278c2ecf20Sopenharmony_ci		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_NPIV,
20288c2ecf20Sopenharmony_ci		    "Waiting for all vports to be reaped.\n");
20298c2ecf20Sopenharmony_ci		msleep(1000);
20308c2ecf20Sopenharmony_ci	}
20318c2ecf20Sopenharmony_ci}
20328c2ecf20Sopenharmony_ci
20338c2ecf20Sopenharmony_ci/**
20348c2ecf20Sopenharmony_ci * qedf_fcoe_reset - Resets the fcoe
20358c2ecf20Sopenharmony_ci *
20368c2ecf20Sopenharmony_ci * @shost: shost the reset is from
20378c2ecf20Sopenharmony_ci *
20388c2ecf20Sopenharmony_ci * Returns: always 0
20398c2ecf20Sopenharmony_ci */
20408c2ecf20Sopenharmony_cistatic int qedf_fcoe_reset(struct Scsi_Host *shost)
20418c2ecf20Sopenharmony_ci{
20428c2ecf20Sopenharmony_ci	struct fc_lport *lport = shost_priv(shost);
20438c2ecf20Sopenharmony_ci
20448c2ecf20Sopenharmony_ci	qedf_ctx_soft_reset(lport);
20458c2ecf20Sopenharmony_ci	return 0;
20468c2ecf20Sopenharmony_ci}
20478c2ecf20Sopenharmony_ci
20488c2ecf20Sopenharmony_cistatic void qedf_get_host_port_id(struct Scsi_Host *shost)
20498c2ecf20Sopenharmony_ci{
20508c2ecf20Sopenharmony_ci	struct fc_lport *lport = shost_priv(shost);
20518c2ecf20Sopenharmony_ci
20528c2ecf20Sopenharmony_ci	fc_host_port_id(shost) = lport->port_id;
20538c2ecf20Sopenharmony_ci}
20548c2ecf20Sopenharmony_ci
20558c2ecf20Sopenharmony_cistatic struct fc_host_statistics *qedf_fc_get_host_stats(struct Scsi_Host
20568c2ecf20Sopenharmony_ci	*shost)
20578c2ecf20Sopenharmony_ci{
20588c2ecf20Sopenharmony_ci	struct fc_host_statistics *qedf_stats;
20598c2ecf20Sopenharmony_ci	struct fc_lport *lport = shost_priv(shost);
20608c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf = lport_priv(lport);
20618c2ecf20Sopenharmony_ci	struct qed_fcoe_stats *fw_fcoe_stats;
20628c2ecf20Sopenharmony_ci
20638c2ecf20Sopenharmony_ci	qedf_stats = fc_get_host_stats(shost);
20648c2ecf20Sopenharmony_ci
20658c2ecf20Sopenharmony_ci	/* We don't collect offload stats for specific NPIV ports */
20668c2ecf20Sopenharmony_ci	if (lport->vport)
20678c2ecf20Sopenharmony_ci		goto out;
20688c2ecf20Sopenharmony_ci
20698c2ecf20Sopenharmony_ci	fw_fcoe_stats = kmalloc(sizeof(struct qed_fcoe_stats), GFP_KERNEL);
20708c2ecf20Sopenharmony_ci	if (!fw_fcoe_stats) {
20718c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "Could not allocate memory for "
20728c2ecf20Sopenharmony_ci		    "fw_fcoe_stats.\n");
20738c2ecf20Sopenharmony_ci		goto out;
20748c2ecf20Sopenharmony_ci	}
20758c2ecf20Sopenharmony_ci
20768c2ecf20Sopenharmony_ci	mutex_lock(&qedf->stats_mutex);
20778c2ecf20Sopenharmony_ci
20788c2ecf20Sopenharmony_ci	/* Query firmware for offload stats */
20798c2ecf20Sopenharmony_ci	qed_ops->get_stats(qedf->cdev, fw_fcoe_stats);
20808c2ecf20Sopenharmony_ci
20818c2ecf20Sopenharmony_ci	/*
20828c2ecf20Sopenharmony_ci	 * The expectation is that we add our offload stats to the stats
20838c2ecf20Sopenharmony_ci	 * being maintained by libfc each time the fc_get_host_status callback
20848c2ecf20Sopenharmony_ci	 * is invoked. The additions are not carried over for each call to
20858c2ecf20Sopenharmony_ci	 * the fc_get_host_stats callback.
20868c2ecf20Sopenharmony_ci	 */
20878c2ecf20Sopenharmony_ci	qedf_stats->tx_frames += fw_fcoe_stats->fcoe_tx_data_pkt_cnt +
20888c2ecf20Sopenharmony_ci	    fw_fcoe_stats->fcoe_tx_xfer_pkt_cnt +
20898c2ecf20Sopenharmony_ci	    fw_fcoe_stats->fcoe_tx_other_pkt_cnt;
20908c2ecf20Sopenharmony_ci	qedf_stats->rx_frames += fw_fcoe_stats->fcoe_rx_data_pkt_cnt +
20918c2ecf20Sopenharmony_ci	    fw_fcoe_stats->fcoe_rx_xfer_pkt_cnt +
20928c2ecf20Sopenharmony_ci	    fw_fcoe_stats->fcoe_rx_other_pkt_cnt;
20938c2ecf20Sopenharmony_ci	qedf_stats->fcp_input_megabytes +=
20948c2ecf20Sopenharmony_ci	    do_div(fw_fcoe_stats->fcoe_rx_byte_cnt, 1000000);
20958c2ecf20Sopenharmony_ci	qedf_stats->fcp_output_megabytes +=
20968c2ecf20Sopenharmony_ci	    do_div(fw_fcoe_stats->fcoe_tx_byte_cnt, 1000000);
20978c2ecf20Sopenharmony_ci	qedf_stats->rx_words += fw_fcoe_stats->fcoe_rx_byte_cnt / 4;
20988c2ecf20Sopenharmony_ci	qedf_stats->tx_words += fw_fcoe_stats->fcoe_tx_byte_cnt / 4;
20998c2ecf20Sopenharmony_ci	qedf_stats->invalid_crc_count +=
21008c2ecf20Sopenharmony_ci	    fw_fcoe_stats->fcoe_silent_drop_pkt_crc_error_cnt;
21018c2ecf20Sopenharmony_ci	qedf_stats->dumped_frames =
21028c2ecf20Sopenharmony_ci	    fw_fcoe_stats->fcoe_silent_drop_total_pkt_cnt;
21038c2ecf20Sopenharmony_ci	qedf_stats->error_frames +=
21048c2ecf20Sopenharmony_ci	    fw_fcoe_stats->fcoe_silent_drop_total_pkt_cnt;
21058c2ecf20Sopenharmony_ci	qedf_stats->fcp_input_requests += qedf->input_requests;
21068c2ecf20Sopenharmony_ci	qedf_stats->fcp_output_requests += qedf->output_requests;
21078c2ecf20Sopenharmony_ci	qedf_stats->fcp_control_requests += qedf->control_requests;
21088c2ecf20Sopenharmony_ci	qedf_stats->fcp_packet_aborts += qedf->packet_aborts;
21098c2ecf20Sopenharmony_ci	qedf_stats->fcp_frame_alloc_failures += qedf->alloc_failures;
21108c2ecf20Sopenharmony_ci
21118c2ecf20Sopenharmony_ci	mutex_unlock(&qedf->stats_mutex);
21128c2ecf20Sopenharmony_ci	kfree(fw_fcoe_stats);
21138c2ecf20Sopenharmony_ciout:
21148c2ecf20Sopenharmony_ci	return qedf_stats;
21158c2ecf20Sopenharmony_ci}
21168c2ecf20Sopenharmony_ci
21178c2ecf20Sopenharmony_cistatic struct fc_function_template qedf_fc_transport_fn = {
21188c2ecf20Sopenharmony_ci	.show_host_node_name = 1,
21198c2ecf20Sopenharmony_ci	.show_host_port_name = 1,
21208c2ecf20Sopenharmony_ci	.show_host_supported_classes = 1,
21218c2ecf20Sopenharmony_ci	.show_host_supported_fc4s = 1,
21228c2ecf20Sopenharmony_ci	.show_host_active_fc4s = 1,
21238c2ecf20Sopenharmony_ci	.show_host_maxframe_size = 1,
21248c2ecf20Sopenharmony_ci
21258c2ecf20Sopenharmony_ci	.get_host_port_id = qedf_get_host_port_id,
21268c2ecf20Sopenharmony_ci	.show_host_port_id = 1,
21278c2ecf20Sopenharmony_ci	.show_host_supported_speeds = 1,
21288c2ecf20Sopenharmony_ci	.get_host_speed = fc_get_host_speed,
21298c2ecf20Sopenharmony_ci	.show_host_speed = 1,
21308c2ecf20Sopenharmony_ci	.show_host_port_type = 1,
21318c2ecf20Sopenharmony_ci	.get_host_port_state = fc_get_host_port_state,
21328c2ecf20Sopenharmony_ci	.show_host_port_state = 1,
21338c2ecf20Sopenharmony_ci	.show_host_symbolic_name = 1,
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_ci	/*
21368c2ecf20Sopenharmony_ci	 * Tell FC transport to allocate enough space to store the backpointer
21378c2ecf20Sopenharmony_ci	 * for the associate qedf_rport struct.
21388c2ecf20Sopenharmony_ci	 */
21398c2ecf20Sopenharmony_ci	.dd_fcrport_size = (sizeof(struct fc_rport_libfc_priv) +
21408c2ecf20Sopenharmony_ci				sizeof(struct qedf_rport)),
21418c2ecf20Sopenharmony_ci	.show_rport_maxframe_size = 1,
21428c2ecf20Sopenharmony_ci	.show_rport_supported_classes = 1,
21438c2ecf20Sopenharmony_ci	.show_host_fabric_name = 1,
21448c2ecf20Sopenharmony_ci	.show_starget_node_name = 1,
21458c2ecf20Sopenharmony_ci	.show_starget_port_name = 1,
21468c2ecf20Sopenharmony_ci	.show_starget_port_id = 1,
21478c2ecf20Sopenharmony_ci	.set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
21488c2ecf20Sopenharmony_ci	.show_rport_dev_loss_tmo = 1,
21498c2ecf20Sopenharmony_ci	.get_fc_host_stats = qedf_fc_get_host_stats,
21508c2ecf20Sopenharmony_ci	.issue_fc_host_lip = qedf_fcoe_reset,
21518c2ecf20Sopenharmony_ci	.vport_create = qedf_vport_create,
21528c2ecf20Sopenharmony_ci	.vport_delete = qedf_vport_destroy,
21538c2ecf20Sopenharmony_ci	.vport_disable = qedf_vport_disable,
21548c2ecf20Sopenharmony_ci	.bsg_request = fc_lport_bsg_request,
21558c2ecf20Sopenharmony_ci};
21568c2ecf20Sopenharmony_ci
21578c2ecf20Sopenharmony_cistatic struct fc_function_template qedf_fc_vport_transport_fn = {
21588c2ecf20Sopenharmony_ci	.show_host_node_name = 1,
21598c2ecf20Sopenharmony_ci	.show_host_port_name = 1,
21608c2ecf20Sopenharmony_ci	.show_host_supported_classes = 1,
21618c2ecf20Sopenharmony_ci	.show_host_supported_fc4s = 1,
21628c2ecf20Sopenharmony_ci	.show_host_active_fc4s = 1,
21638c2ecf20Sopenharmony_ci	.show_host_maxframe_size = 1,
21648c2ecf20Sopenharmony_ci	.show_host_port_id = 1,
21658c2ecf20Sopenharmony_ci	.show_host_supported_speeds = 1,
21668c2ecf20Sopenharmony_ci	.get_host_speed = fc_get_host_speed,
21678c2ecf20Sopenharmony_ci	.show_host_speed = 1,
21688c2ecf20Sopenharmony_ci	.show_host_port_type = 1,
21698c2ecf20Sopenharmony_ci	.get_host_port_state = fc_get_host_port_state,
21708c2ecf20Sopenharmony_ci	.show_host_port_state = 1,
21718c2ecf20Sopenharmony_ci	.show_host_symbolic_name = 1,
21728c2ecf20Sopenharmony_ci	.dd_fcrport_size = (sizeof(struct fc_rport_libfc_priv) +
21738c2ecf20Sopenharmony_ci				sizeof(struct qedf_rport)),
21748c2ecf20Sopenharmony_ci	.show_rport_maxframe_size = 1,
21758c2ecf20Sopenharmony_ci	.show_rport_supported_classes = 1,
21768c2ecf20Sopenharmony_ci	.show_host_fabric_name = 1,
21778c2ecf20Sopenharmony_ci	.show_starget_node_name = 1,
21788c2ecf20Sopenharmony_ci	.show_starget_port_name = 1,
21798c2ecf20Sopenharmony_ci	.show_starget_port_id = 1,
21808c2ecf20Sopenharmony_ci	.set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
21818c2ecf20Sopenharmony_ci	.show_rport_dev_loss_tmo = 1,
21828c2ecf20Sopenharmony_ci	.get_fc_host_stats = fc_get_host_stats,
21838c2ecf20Sopenharmony_ci	.issue_fc_host_lip = qedf_fcoe_reset,
21848c2ecf20Sopenharmony_ci	.bsg_request = fc_lport_bsg_request,
21858c2ecf20Sopenharmony_ci};
21868c2ecf20Sopenharmony_ci
21878c2ecf20Sopenharmony_cistatic bool qedf_fp_has_work(struct qedf_fastpath *fp)
21888c2ecf20Sopenharmony_ci{
21898c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf = fp->qedf;
21908c2ecf20Sopenharmony_ci	struct global_queue *que;
21918c2ecf20Sopenharmony_ci	struct qed_sb_info *sb_info = fp->sb_info;
21928c2ecf20Sopenharmony_ci	struct status_block_e4 *sb = sb_info->sb_virt;
21938c2ecf20Sopenharmony_ci	u16 prod_idx;
21948c2ecf20Sopenharmony_ci
21958c2ecf20Sopenharmony_ci	/* Get the pointer to the global CQ this completion is on */
21968c2ecf20Sopenharmony_ci	que = qedf->global_queues[fp->sb_id];
21978c2ecf20Sopenharmony_ci
21988c2ecf20Sopenharmony_ci	/* Be sure all responses have been written to PI */
21998c2ecf20Sopenharmony_ci	rmb();
22008c2ecf20Sopenharmony_ci
22018c2ecf20Sopenharmony_ci	/* Get the current firmware producer index */
22028c2ecf20Sopenharmony_ci	prod_idx = sb->pi_array[QEDF_FCOE_PARAMS_GL_RQ_PI];
22038c2ecf20Sopenharmony_ci
22048c2ecf20Sopenharmony_ci	return (que->cq_prod_idx != prod_idx);
22058c2ecf20Sopenharmony_ci}
22068c2ecf20Sopenharmony_ci
22078c2ecf20Sopenharmony_ci/*
22088c2ecf20Sopenharmony_ci * Interrupt handler code.
22098c2ecf20Sopenharmony_ci */
22108c2ecf20Sopenharmony_ci
22118c2ecf20Sopenharmony_ci/* Process completion queue and copy CQE contents for deferred processesing
22128c2ecf20Sopenharmony_ci *
22138c2ecf20Sopenharmony_ci * Return true if we should wake the I/O thread, false if not.
22148c2ecf20Sopenharmony_ci */
22158c2ecf20Sopenharmony_cistatic bool qedf_process_completions(struct qedf_fastpath *fp)
22168c2ecf20Sopenharmony_ci{
22178c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf = fp->qedf;
22188c2ecf20Sopenharmony_ci	struct qed_sb_info *sb_info = fp->sb_info;
22198c2ecf20Sopenharmony_ci	struct status_block_e4 *sb = sb_info->sb_virt;
22208c2ecf20Sopenharmony_ci	struct global_queue *que;
22218c2ecf20Sopenharmony_ci	u16 prod_idx;
22228c2ecf20Sopenharmony_ci	struct fcoe_cqe *cqe;
22238c2ecf20Sopenharmony_ci	struct qedf_io_work *io_work;
22248c2ecf20Sopenharmony_ci	int num_handled = 0;
22258c2ecf20Sopenharmony_ci	unsigned int cpu;
22268c2ecf20Sopenharmony_ci	struct qedf_ioreq *io_req = NULL;
22278c2ecf20Sopenharmony_ci	u16 xid;
22288c2ecf20Sopenharmony_ci	u16 new_cqes;
22298c2ecf20Sopenharmony_ci	u32 comp_type;
22308c2ecf20Sopenharmony_ci
22318c2ecf20Sopenharmony_ci	/* Get the current firmware producer index */
22328c2ecf20Sopenharmony_ci	prod_idx = sb->pi_array[QEDF_FCOE_PARAMS_GL_RQ_PI];
22338c2ecf20Sopenharmony_ci
22348c2ecf20Sopenharmony_ci	/* Get the pointer to the global CQ this completion is on */
22358c2ecf20Sopenharmony_ci	que = qedf->global_queues[fp->sb_id];
22368c2ecf20Sopenharmony_ci
22378c2ecf20Sopenharmony_ci	/* Calculate the amount of new elements since last processing */
22388c2ecf20Sopenharmony_ci	new_cqes = (prod_idx >= que->cq_prod_idx) ?
22398c2ecf20Sopenharmony_ci	    (prod_idx - que->cq_prod_idx) :
22408c2ecf20Sopenharmony_ci	    0x10000 - que->cq_prod_idx + prod_idx;
22418c2ecf20Sopenharmony_ci
22428c2ecf20Sopenharmony_ci	/* Save producer index */
22438c2ecf20Sopenharmony_ci	que->cq_prod_idx = prod_idx;
22448c2ecf20Sopenharmony_ci
22458c2ecf20Sopenharmony_ci	while (new_cqes) {
22468c2ecf20Sopenharmony_ci		fp->completions++;
22478c2ecf20Sopenharmony_ci		num_handled++;
22488c2ecf20Sopenharmony_ci		cqe = &que->cq[que->cq_cons_idx];
22498c2ecf20Sopenharmony_ci
22508c2ecf20Sopenharmony_ci		comp_type = (cqe->cqe_data >> FCOE_CQE_CQE_TYPE_SHIFT) &
22518c2ecf20Sopenharmony_ci		    FCOE_CQE_CQE_TYPE_MASK;
22528c2ecf20Sopenharmony_ci
22538c2ecf20Sopenharmony_ci		/*
22548c2ecf20Sopenharmony_ci		 * Process unsolicited CQEs directly in the interrupt handler
22558c2ecf20Sopenharmony_ci		 * sine we need the fastpath ID
22568c2ecf20Sopenharmony_ci		 */
22578c2ecf20Sopenharmony_ci		if (comp_type == FCOE_UNSOLIC_CQE_TYPE) {
22588c2ecf20Sopenharmony_ci			QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_UNSOL,
22598c2ecf20Sopenharmony_ci			   "Unsolicated CQE.\n");
22608c2ecf20Sopenharmony_ci			qedf_process_unsol_compl(qedf, fp->sb_id, cqe);
22618c2ecf20Sopenharmony_ci			/*
22628c2ecf20Sopenharmony_ci			 * Don't add a work list item.  Increment consumer
22638c2ecf20Sopenharmony_ci			 * consumer index and move on.
22648c2ecf20Sopenharmony_ci			 */
22658c2ecf20Sopenharmony_ci			goto inc_idx;
22668c2ecf20Sopenharmony_ci		}
22678c2ecf20Sopenharmony_ci
22688c2ecf20Sopenharmony_ci		xid = cqe->cqe_data & FCOE_CQE_TASK_ID_MASK;
22698c2ecf20Sopenharmony_ci		io_req = &qedf->cmd_mgr->cmds[xid];
22708c2ecf20Sopenharmony_ci
22718c2ecf20Sopenharmony_ci		/*
22728c2ecf20Sopenharmony_ci		 * Figure out which percpu thread we should queue this I/O
22738c2ecf20Sopenharmony_ci		 * on.
22748c2ecf20Sopenharmony_ci		 */
22758c2ecf20Sopenharmony_ci		if (!io_req)
22768c2ecf20Sopenharmony_ci			/* If there is not io_req assocated with this CQE
22778c2ecf20Sopenharmony_ci			 * just queue it on CPU 0
22788c2ecf20Sopenharmony_ci			 */
22798c2ecf20Sopenharmony_ci			cpu = 0;
22808c2ecf20Sopenharmony_ci		else {
22818c2ecf20Sopenharmony_ci			cpu = io_req->cpu;
22828c2ecf20Sopenharmony_ci			io_req->int_cpu = smp_processor_id();
22838c2ecf20Sopenharmony_ci		}
22848c2ecf20Sopenharmony_ci
22858c2ecf20Sopenharmony_ci		io_work = mempool_alloc(qedf->io_mempool, GFP_ATOMIC);
22868c2ecf20Sopenharmony_ci		if (!io_work) {
22878c2ecf20Sopenharmony_ci			QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate "
22888c2ecf20Sopenharmony_ci				   "work for I/O completion.\n");
22898c2ecf20Sopenharmony_ci			continue;
22908c2ecf20Sopenharmony_ci		}
22918c2ecf20Sopenharmony_ci		memset(io_work, 0, sizeof(struct qedf_io_work));
22928c2ecf20Sopenharmony_ci
22938c2ecf20Sopenharmony_ci		INIT_WORK(&io_work->work, qedf_fp_io_handler);
22948c2ecf20Sopenharmony_ci
22958c2ecf20Sopenharmony_ci		/* Copy contents of CQE for deferred processing */
22968c2ecf20Sopenharmony_ci		memcpy(&io_work->cqe, cqe, sizeof(struct fcoe_cqe));
22978c2ecf20Sopenharmony_ci
22988c2ecf20Sopenharmony_ci		io_work->qedf = fp->qedf;
22998c2ecf20Sopenharmony_ci		io_work->fp = NULL; /* Only used for unsolicited frames */
23008c2ecf20Sopenharmony_ci
23018c2ecf20Sopenharmony_ci		queue_work_on(cpu, qedf_io_wq, &io_work->work);
23028c2ecf20Sopenharmony_ci
23038c2ecf20Sopenharmony_ciinc_idx:
23048c2ecf20Sopenharmony_ci		que->cq_cons_idx++;
23058c2ecf20Sopenharmony_ci		if (que->cq_cons_idx == fp->cq_num_entries)
23068c2ecf20Sopenharmony_ci			que->cq_cons_idx = 0;
23078c2ecf20Sopenharmony_ci		new_cqes--;
23088c2ecf20Sopenharmony_ci	}
23098c2ecf20Sopenharmony_ci
23108c2ecf20Sopenharmony_ci	return true;
23118c2ecf20Sopenharmony_ci}
23128c2ecf20Sopenharmony_ci
23138c2ecf20Sopenharmony_ci
23148c2ecf20Sopenharmony_ci/* MSI-X fastpath handler code */
23158c2ecf20Sopenharmony_cistatic irqreturn_t qedf_msix_handler(int irq, void *dev_id)
23168c2ecf20Sopenharmony_ci{
23178c2ecf20Sopenharmony_ci	struct qedf_fastpath *fp = dev_id;
23188c2ecf20Sopenharmony_ci
23198c2ecf20Sopenharmony_ci	if (!fp) {
23208c2ecf20Sopenharmony_ci		QEDF_ERR(NULL, "fp is null.\n");
23218c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
23228c2ecf20Sopenharmony_ci	}
23238c2ecf20Sopenharmony_ci	if (!fp->sb_info) {
23248c2ecf20Sopenharmony_ci		QEDF_ERR(NULL, "fp->sb_info in null.");
23258c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
23268c2ecf20Sopenharmony_ci	}
23278c2ecf20Sopenharmony_ci
23288c2ecf20Sopenharmony_ci	/*
23298c2ecf20Sopenharmony_ci	 * Disable interrupts for this status block while we process new
23308c2ecf20Sopenharmony_ci	 * completions
23318c2ecf20Sopenharmony_ci	 */
23328c2ecf20Sopenharmony_ci	qed_sb_ack(fp->sb_info, IGU_INT_DISABLE, 0 /*do not update*/);
23338c2ecf20Sopenharmony_ci
23348c2ecf20Sopenharmony_ci	while (1) {
23358c2ecf20Sopenharmony_ci		qedf_process_completions(fp);
23368c2ecf20Sopenharmony_ci
23378c2ecf20Sopenharmony_ci		if (qedf_fp_has_work(fp) == 0) {
23388c2ecf20Sopenharmony_ci			/* Update the sb information */
23398c2ecf20Sopenharmony_ci			qed_sb_update_sb_idx(fp->sb_info);
23408c2ecf20Sopenharmony_ci
23418c2ecf20Sopenharmony_ci			/* Check for more work */
23428c2ecf20Sopenharmony_ci			rmb();
23438c2ecf20Sopenharmony_ci
23448c2ecf20Sopenharmony_ci			if (qedf_fp_has_work(fp) == 0) {
23458c2ecf20Sopenharmony_ci				/* Re-enable interrupts */
23468c2ecf20Sopenharmony_ci				qed_sb_ack(fp->sb_info, IGU_INT_ENABLE, 1);
23478c2ecf20Sopenharmony_ci				return IRQ_HANDLED;
23488c2ecf20Sopenharmony_ci			}
23498c2ecf20Sopenharmony_ci		}
23508c2ecf20Sopenharmony_ci	}
23518c2ecf20Sopenharmony_ci
23528c2ecf20Sopenharmony_ci	/* Do we ever want to break out of above loop? */
23538c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
23548c2ecf20Sopenharmony_ci}
23558c2ecf20Sopenharmony_ci
23568c2ecf20Sopenharmony_ci/* simd handler for MSI/INTa */
23578c2ecf20Sopenharmony_cistatic void qedf_simd_int_handler(void *cookie)
23588c2ecf20Sopenharmony_ci{
23598c2ecf20Sopenharmony_ci	/* Cookie is qedf_ctx struct */
23608c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf = (struct qedf_ctx *)cookie;
23618c2ecf20Sopenharmony_ci
23628c2ecf20Sopenharmony_ci	QEDF_WARN(&(qedf->dbg_ctx), "qedf=%p.\n", qedf);
23638c2ecf20Sopenharmony_ci}
23648c2ecf20Sopenharmony_ci
23658c2ecf20Sopenharmony_ci#define QEDF_SIMD_HANDLER_NUM		0
23668c2ecf20Sopenharmony_cistatic void qedf_sync_free_irqs(struct qedf_ctx *qedf)
23678c2ecf20Sopenharmony_ci{
23688c2ecf20Sopenharmony_ci	int i;
23698c2ecf20Sopenharmony_ci	u16 vector_idx = 0;
23708c2ecf20Sopenharmony_ci	u32 vector;
23718c2ecf20Sopenharmony_ci
23728c2ecf20Sopenharmony_ci	if (qedf->int_info.msix_cnt) {
23738c2ecf20Sopenharmony_ci		for (i = 0; i < qedf->int_info.used_cnt; i++) {
23748c2ecf20Sopenharmony_ci			vector_idx = i * qedf->dev_info.common.num_hwfns +
23758c2ecf20Sopenharmony_ci				qed_ops->common->get_affin_hwfn_idx(qedf->cdev);
23768c2ecf20Sopenharmony_ci			QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
23778c2ecf20Sopenharmony_ci				  "Freeing IRQ #%d vector_idx=%d.\n",
23788c2ecf20Sopenharmony_ci				  i, vector_idx);
23798c2ecf20Sopenharmony_ci			vector = qedf->int_info.msix[vector_idx].vector;
23808c2ecf20Sopenharmony_ci			synchronize_irq(vector);
23818c2ecf20Sopenharmony_ci			irq_set_affinity_hint(vector, NULL);
23828c2ecf20Sopenharmony_ci			irq_set_affinity_notifier(vector, NULL);
23838c2ecf20Sopenharmony_ci			free_irq(vector, &qedf->fp_array[i]);
23848c2ecf20Sopenharmony_ci		}
23858c2ecf20Sopenharmony_ci	} else
23868c2ecf20Sopenharmony_ci		qed_ops->common->simd_handler_clean(qedf->cdev,
23878c2ecf20Sopenharmony_ci		    QEDF_SIMD_HANDLER_NUM);
23888c2ecf20Sopenharmony_ci
23898c2ecf20Sopenharmony_ci	qedf->int_info.used_cnt = 0;
23908c2ecf20Sopenharmony_ci	qed_ops->common->set_fp_int(qedf->cdev, 0);
23918c2ecf20Sopenharmony_ci}
23928c2ecf20Sopenharmony_ci
23938c2ecf20Sopenharmony_cistatic int qedf_request_msix_irq(struct qedf_ctx *qedf)
23948c2ecf20Sopenharmony_ci{
23958c2ecf20Sopenharmony_ci	int i, rc, cpu;
23968c2ecf20Sopenharmony_ci	u16 vector_idx = 0;
23978c2ecf20Sopenharmony_ci	u32 vector;
23988c2ecf20Sopenharmony_ci
23998c2ecf20Sopenharmony_ci	cpu = cpumask_first(cpu_online_mask);
24008c2ecf20Sopenharmony_ci	for (i = 0; i < qedf->num_queues; i++) {
24018c2ecf20Sopenharmony_ci		vector_idx = i * qedf->dev_info.common.num_hwfns +
24028c2ecf20Sopenharmony_ci			qed_ops->common->get_affin_hwfn_idx(qedf->cdev);
24038c2ecf20Sopenharmony_ci		QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
24048c2ecf20Sopenharmony_ci			  "Requesting IRQ #%d vector_idx=%d.\n",
24058c2ecf20Sopenharmony_ci			  i, vector_idx);
24068c2ecf20Sopenharmony_ci		vector = qedf->int_info.msix[vector_idx].vector;
24078c2ecf20Sopenharmony_ci		rc = request_irq(vector, qedf_msix_handler, 0, "qedf",
24088c2ecf20Sopenharmony_ci				 &qedf->fp_array[i]);
24098c2ecf20Sopenharmony_ci
24108c2ecf20Sopenharmony_ci		if (rc) {
24118c2ecf20Sopenharmony_ci			QEDF_WARN(&(qedf->dbg_ctx), "request_irq failed.\n");
24128c2ecf20Sopenharmony_ci			qedf_sync_free_irqs(qedf);
24138c2ecf20Sopenharmony_ci			return rc;
24148c2ecf20Sopenharmony_ci		}
24158c2ecf20Sopenharmony_ci
24168c2ecf20Sopenharmony_ci		qedf->int_info.used_cnt++;
24178c2ecf20Sopenharmony_ci		rc = irq_set_affinity_hint(vector, get_cpu_mask(cpu));
24188c2ecf20Sopenharmony_ci		cpu = cpumask_next(cpu, cpu_online_mask);
24198c2ecf20Sopenharmony_ci	}
24208c2ecf20Sopenharmony_ci
24218c2ecf20Sopenharmony_ci	return 0;
24228c2ecf20Sopenharmony_ci}
24238c2ecf20Sopenharmony_ci
24248c2ecf20Sopenharmony_cistatic int qedf_setup_int(struct qedf_ctx *qedf)
24258c2ecf20Sopenharmony_ci{
24268c2ecf20Sopenharmony_ci	int rc = 0;
24278c2ecf20Sopenharmony_ci
24288c2ecf20Sopenharmony_ci	/*
24298c2ecf20Sopenharmony_ci	 * Learn interrupt configuration
24308c2ecf20Sopenharmony_ci	 */
24318c2ecf20Sopenharmony_ci	rc = qed_ops->common->set_fp_int(qedf->cdev, num_online_cpus());
24328c2ecf20Sopenharmony_ci	if (rc <= 0)
24338c2ecf20Sopenharmony_ci		return 0;
24348c2ecf20Sopenharmony_ci
24358c2ecf20Sopenharmony_ci	rc  = qed_ops->common->get_fp_int(qedf->cdev, &qedf->int_info);
24368c2ecf20Sopenharmony_ci	if (rc)
24378c2ecf20Sopenharmony_ci		return 0;
24388c2ecf20Sopenharmony_ci
24398c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Number of msix_cnt = "
24408c2ecf20Sopenharmony_ci		   "0x%x num of cpus = 0x%x\n", qedf->int_info.msix_cnt,
24418c2ecf20Sopenharmony_ci		   num_online_cpus());
24428c2ecf20Sopenharmony_ci
24438c2ecf20Sopenharmony_ci	if (qedf->int_info.msix_cnt)
24448c2ecf20Sopenharmony_ci		return qedf_request_msix_irq(qedf);
24458c2ecf20Sopenharmony_ci
24468c2ecf20Sopenharmony_ci	qed_ops->common->simd_handler_config(qedf->cdev, &qedf,
24478c2ecf20Sopenharmony_ci	    QEDF_SIMD_HANDLER_NUM, qedf_simd_int_handler);
24488c2ecf20Sopenharmony_ci	qedf->int_info.used_cnt = 1;
24498c2ecf20Sopenharmony_ci
24508c2ecf20Sopenharmony_ci	QEDF_ERR(&qedf->dbg_ctx,
24518c2ecf20Sopenharmony_ci		 "Cannot load driver due to a lack of MSI-X vectors.\n");
24528c2ecf20Sopenharmony_ci	return -EINVAL;
24538c2ecf20Sopenharmony_ci}
24548c2ecf20Sopenharmony_ci
24558c2ecf20Sopenharmony_ci/* Main function for libfc frame reception */
24568c2ecf20Sopenharmony_cistatic void qedf_recv_frame(struct qedf_ctx *qedf,
24578c2ecf20Sopenharmony_ci	struct sk_buff *skb)
24588c2ecf20Sopenharmony_ci{
24598c2ecf20Sopenharmony_ci	u32 fr_len;
24608c2ecf20Sopenharmony_ci	struct fc_lport *lport;
24618c2ecf20Sopenharmony_ci	struct fc_frame_header *fh;
24628c2ecf20Sopenharmony_ci	struct fcoe_crc_eof crc_eof;
24638c2ecf20Sopenharmony_ci	struct fc_frame *fp;
24648c2ecf20Sopenharmony_ci	u8 *mac = NULL;
24658c2ecf20Sopenharmony_ci	u8 *dest_mac = NULL;
24668c2ecf20Sopenharmony_ci	struct fcoe_hdr *hp;
24678c2ecf20Sopenharmony_ci	struct qedf_rport *fcport;
24688c2ecf20Sopenharmony_ci	struct fc_lport *vn_port;
24698c2ecf20Sopenharmony_ci	u32 f_ctl;
24708c2ecf20Sopenharmony_ci
24718c2ecf20Sopenharmony_ci	lport = qedf->lport;
24728c2ecf20Sopenharmony_ci	if (lport == NULL || lport->state == LPORT_ST_DISABLED) {
24738c2ecf20Sopenharmony_ci		QEDF_WARN(NULL, "Invalid lport struct or lport disabled.\n");
24748c2ecf20Sopenharmony_ci		kfree_skb(skb);
24758c2ecf20Sopenharmony_ci		return;
24768c2ecf20Sopenharmony_ci	}
24778c2ecf20Sopenharmony_ci
24788c2ecf20Sopenharmony_ci	if (skb_is_nonlinear(skb))
24798c2ecf20Sopenharmony_ci		skb_linearize(skb);
24808c2ecf20Sopenharmony_ci	mac = eth_hdr(skb)->h_source;
24818c2ecf20Sopenharmony_ci	dest_mac = eth_hdr(skb)->h_dest;
24828c2ecf20Sopenharmony_ci
24838c2ecf20Sopenharmony_ci	/* Pull the header */
24848c2ecf20Sopenharmony_ci	hp = (struct fcoe_hdr *)skb->data;
24858c2ecf20Sopenharmony_ci	fh = (struct fc_frame_header *) skb_transport_header(skb);
24868c2ecf20Sopenharmony_ci	skb_pull(skb, sizeof(struct fcoe_hdr));
24878c2ecf20Sopenharmony_ci	fr_len = skb->len - sizeof(struct fcoe_crc_eof);
24888c2ecf20Sopenharmony_ci
24898c2ecf20Sopenharmony_ci	fp = (struct fc_frame *)skb;
24908c2ecf20Sopenharmony_ci	fc_frame_init(fp);
24918c2ecf20Sopenharmony_ci	fr_dev(fp) = lport;
24928c2ecf20Sopenharmony_ci	fr_sof(fp) = hp->fcoe_sof;
24938c2ecf20Sopenharmony_ci	if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) {
24948c2ecf20Sopenharmony_ci		QEDF_INFO(NULL, QEDF_LOG_LL2, "skb_copy_bits failed.\n");
24958c2ecf20Sopenharmony_ci		kfree_skb(skb);
24968c2ecf20Sopenharmony_ci		return;
24978c2ecf20Sopenharmony_ci	}
24988c2ecf20Sopenharmony_ci	fr_eof(fp) = crc_eof.fcoe_eof;
24998c2ecf20Sopenharmony_ci	fr_crc(fp) = crc_eof.fcoe_crc32;
25008c2ecf20Sopenharmony_ci	if (pskb_trim(skb, fr_len)) {
25018c2ecf20Sopenharmony_ci		QEDF_INFO(NULL, QEDF_LOG_LL2, "pskb_trim failed.\n");
25028c2ecf20Sopenharmony_ci		kfree_skb(skb);
25038c2ecf20Sopenharmony_ci		return;
25048c2ecf20Sopenharmony_ci	}
25058c2ecf20Sopenharmony_ci
25068c2ecf20Sopenharmony_ci	fh = fc_frame_header_get(fp);
25078c2ecf20Sopenharmony_ci
25088c2ecf20Sopenharmony_ci	/*
25098c2ecf20Sopenharmony_ci	 * Invalid frame filters.
25108c2ecf20Sopenharmony_ci	 */
25118c2ecf20Sopenharmony_ci
25128c2ecf20Sopenharmony_ci	if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA &&
25138c2ecf20Sopenharmony_ci	    fh->fh_type == FC_TYPE_FCP) {
25148c2ecf20Sopenharmony_ci		/* Drop FCP data. We dont this in L2 path */
25158c2ecf20Sopenharmony_ci		kfree_skb(skb);
25168c2ecf20Sopenharmony_ci		return;
25178c2ecf20Sopenharmony_ci	}
25188c2ecf20Sopenharmony_ci	if (fh->fh_r_ctl == FC_RCTL_ELS_REQ &&
25198c2ecf20Sopenharmony_ci	    fh->fh_type == FC_TYPE_ELS) {
25208c2ecf20Sopenharmony_ci		switch (fc_frame_payload_op(fp)) {
25218c2ecf20Sopenharmony_ci		case ELS_LOGO:
25228c2ecf20Sopenharmony_ci			if (ntoh24(fh->fh_s_id) == FC_FID_FLOGI) {
25238c2ecf20Sopenharmony_ci				/* drop non-FIP LOGO */
25248c2ecf20Sopenharmony_ci				kfree_skb(skb);
25258c2ecf20Sopenharmony_ci				return;
25268c2ecf20Sopenharmony_ci			}
25278c2ecf20Sopenharmony_ci			break;
25288c2ecf20Sopenharmony_ci		}
25298c2ecf20Sopenharmony_ci	}
25308c2ecf20Sopenharmony_ci
25318c2ecf20Sopenharmony_ci	if (fh->fh_r_ctl == FC_RCTL_BA_ABTS) {
25328c2ecf20Sopenharmony_ci		/* Drop incoming ABTS */
25338c2ecf20Sopenharmony_ci		kfree_skb(skb);
25348c2ecf20Sopenharmony_ci		return;
25358c2ecf20Sopenharmony_ci	}
25368c2ecf20Sopenharmony_ci
25378c2ecf20Sopenharmony_ci	if (ntoh24(&dest_mac[3]) != ntoh24(fh->fh_d_id)) {
25388c2ecf20Sopenharmony_ci		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2,
25398c2ecf20Sopenharmony_ci		    "FC frame d_id mismatch with MAC %pM.\n", dest_mac);
25408c2ecf20Sopenharmony_ci		kfree_skb(skb);
25418c2ecf20Sopenharmony_ci		return;
25428c2ecf20Sopenharmony_ci	}
25438c2ecf20Sopenharmony_ci
25448c2ecf20Sopenharmony_ci	if (qedf->ctlr.state) {
25458c2ecf20Sopenharmony_ci		if (!ether_addr_equal(mac, qedf->ctlr.dest_addr)) {
25468c2ecf20Sopenharmony_ci			QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2,
25478c2ecf20Sopenharmony_ci			    "Wrong source address: mac:%pM dest_addr:%pM.\n",
25488c2ecf20Sopenharmony_ci			    mac, qedf->ctlr.dest_addr);
25498c2ecf20Sopenharmony_ci			kfree_skb(skb);
25508c2ecf20Sopenharmony_ci			return;
25518c2ecf20Sopenharmony_ci		}
25528c2ecf20Sopenharmony_ci	}
25538c2ecf20Sopenharmony_ci
25548c2ecf20Sopenharmony_ci	vn_port = fc_vport_id_lookup(lport, ntoh24(fh->fh_d_id));
25558c2ecf20Sopenharmony_ci
25568c2ecf20Sopenharmony_ci	/*
25578c2ecf20Sopenharmony_ci	 * If the destination ID from the frame header does not match what we
25588c2ecf20Sopenharmony_ci	 * have on record for lport and the search for a NPIV port came up
25598c2ecf20Sopenharmony_ci	 * empty then this is not addressed to our port so simply drop it.
25608c2ecf20Sopenharmony_ci	 */
25618c2ecf20Sopenharmony_ci	if (lport->port_id != ntoh24(fh->fh_d_id) && !vn_port) {
25628c2ecf20Sopenharmony_ci		QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_LL2,
25638c2ecf20Sopenharmony_ci			  "Dropping frame due to destination mismatch: lport->port_id=0x%x fh->d_id=0x%x.\n",
25648c2ecf20Sopenharmony_ci			  lport->port_id, ntoh24(fh->fh_d_id));
25658c2ecf20Sopenharmony_ci		kfree_skb(skb);
25668c2ecf20Sopenharmony_ci		return;
25678c2ecf20Sopenharmony_ci	}
25688c2ecf20Sopenharmony_ci
25698c2ecf20Sopenharmony_ci	f_ctl = ntoh24(fh->fh_f_ctl);
25708c2ecf20Sopenharmony_ci	if ((fh->fh_type == FC_TYPE_BLS) && (f_ctl & FC_FC_SEQ_CTX) &&
25718c2ecf20Sopenharmony_ci	    (f_ctl & FC_FC_EX_CTX)) {
25728c2ecf20Sopenharmony_ci		/* Drop incoming ABTS response that has both SEQ/EX CTX set */
25738c2ecf20Sopenharmony_ci		QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_LL2,
25748c2ecf20Sopenharmony_ci			  "Dropping ABTS response as both SEQ/EX CTX set.\n");
25758c2ecf20Sopenharmony_ci		kfree_skb(skb);
25768c2ecf20Sopenharmony_ci		return;
25778c2ecf20Sopenharmony_ci	}
25788c2ecf20Sopenharmony_ci
25798c2ecf20Sopenharmony_ci	/*
25808c2ecf20Sopenharmony_ci	 * If a connection is uploading, drop incoming FCoE frames as there
25818c2ecf20Sopenharmony_ci	 * is a small window where we could try to return a frame while libfc
25828c2ecf20Sopenharmony_ci	 * is trying to clean things up.
25838c2ecf20Sopenharmony_ci	 */
25848c2ecf20Sopenharmony_ci
25858c2ecf20Sopenharmony_ci	/* Get fcport associated with d_id if it exists */
25868c2ecf20Sopenharmony_ci	fcport = qedf_fcport_lookup(qedf, ntoh24(fh->fh_d_id));
25878c2ecf20Sopenharmony_ci
25888c2ecf20Sopenharmony_ci	if (fcport && test_bit(QEDF_RPORT_UPLOADING_CONNECTION,
25898c2ecf20Sopenharmony_ci	    &fcport->flags)) {
25908c2ecf20Sopenharmony_ci		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2,
25918c2ecf20Sopenharmony_ci		    "Connection uploading, dropping fp=%p.\n", fp);
25928c2ecf20Sopenharmony_ci		kfree_skb(skb);
25938c2ecf20Sopenharmony_ci		return;
25948c2ecf20Sopenharmony_ci	}
25958c2ecf20Sopenharmony_ci
25968c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "FCoE frame receive: "
25978c2ecf20Sopenharmony_ci	    "skb=%p fp=%p src=%06x dest=%06x r_ctl=%x fh_type=%x.\n", skb, fp,
25988c2ecf20Sopenharmony_ci	    ntoh24(fh->fh_s_id), ntoh24(fh->fh_d_id), fh->fh_r_ctl,
25998c2ecf20Sopenharmony_ci	    fh->fh_type);
26008c2ecf20Sopenharmony_ci	if (qedf_dump_frames)
26018c2ecf20Sopenharmony_ci		print_hex_dump(KERN_WARNING, "fcoe: ", DUMP_PREFIX_OFFSET, 16,
26028c2ecf20Sopenharmony_ci		    1, skb->data, skb->len, false);
26038c2ecf20Sopenharmony_ci	fc_exch_recv(lport, fp);
26048c2ecf20Sopenharmony_ci}
26058c2ecf20Sopenharmony_ci
26068c2ecf20Sopenharmony_cistatic void qedf_ll2_process_skb(struct work_struct *work)
26078c2ecf20Sopenharmony_ci{
26088c2ecf20Sopenharmony_ci	struct qedf_skb_work *skb_work =
26098c2ecf20Sopenharmony_ci	    container_of(work, struct qedf_skb_work, work);
26108c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf = skb_work->qedf;
26118c2ecf20Sopenharmony_ci	struct sk_buff *skb = skb_work->skb;
26128c2ecf20Sopenharmony_ci	struct ethhdr *eh;
26138c2ecf20Sopenharmony_ci
26148c2ecf20Sopenharmony_ci	if (!qedf) {
26158c2ecf20Sopenharmony_ci		QEDF_ERR(NULL, "qedf is NULL\n");
26168c2ecf20Sopenharmony_ci		goto err_out;
26178c2ecf20Sopenharmony_ci	}
26188c2ecf20Sopenharmony_ci
26198c2ecf20Sopenharmony_ci	eh = (struct ethhdr *)skb->data;
26208c2ecf20Sopenharmony_ci
26218c2ecf20Sopenharmony_ci	/* Undo VLAN encapsulation */
26228c2ecf20Sopenharmony_ci	if (eh->h_proto == htons(ETH_P_8021Q)) {
26238c2ecf20Sopenharmony_ci		memmove((u8 *)eh + VLAN_HLEN, eh, ETH_ALEN * 2);
26248c2ecf20Sopenharmony_ci		eh = skb_pull(skb, VLAN_HLEN);
26258c2ecf20Sopenharmony_ci		skb_reset_mac_header(skb);
26268c2ecf20Sopenharmony_ci	}
26278c2ecf20Sopenharmony_ci
26288c2ecf20Sopenharmony_ci	/*
26298c2ecf20Sopenharmony_ci	 * Process either a FIP frame or FCoE frame based on the
26308c2ecf20Sopenharmony_ci	 * protocol value.  If it's not either just drop the
26318c2ecf20Sopenharmony_ci	 * frame.
26328c2ecf20Sopenharmony_ci	 */
26338c2ecf20Sopenharmony_ci	if (eh->h_proto == htons(ETH_P_FIP)) {
26348c2ecf20Sopenharmony_ci		qedf_fip_recv(qedf, skb);
26358c2ecf20Sopenharmony_ci		goto out;
26368c2ecf20Sopenharmony_ci	} else if (eh->h_proto == htons(ETH_P_FCOE)) {
26378c2ecf20Sopenharmony_ci		__skb_pull(skb, ETH_HLEN);
26388c2ecf20Sopenharmony_ci		qedf_recv_frame(qedf, skb);
26398c2ecf20Sopenharmony_ci		goto out;
26408c2ecf20Sopenharmony_ci	} else
26418c2ecf20Sopenharmony_ci		goto err_out;
26428c2ecf20Sopenharmony_ci
26438c2ecf20Sopenharmony_cierr_out:
26448c2ecf20Sopenharmony_ci	kfree_skb(skb);
26458c2ecf20Sopenharmony_ciout:
26468c2ecf20Sopenharmony_ci	kfree(skb_work);
26478c2ecf20Sopenharmony_ci	return;
26488c2ecf20Sopenharmony_ci}
26498c2ecf20Sopenharmony_ci
26508c2ecf20Sopenharmony_cistatic int qedf_ll2_rx(void *cookie, struct sk_buff *skb,
26518c2ecf20Sopenharmony_ci	u32 arg1, u32 arg2)
26528c2ecf20Sopenharmony_ci{
26538c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf = (struct qedf_ctx *)cookie;
26548c2ecf20Sopenharmony_ci	struct qedf_skb_work *skb_work;
26558c2ecf20Sopenharmony_ci
26568c2ecf20Sopenharmony_ci	if (atomic_read(&qedf->link_state) == QEDF_LINK_DOWN) {
26578c2ecf20Sopenharmony_ci		QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_LL2,
26588c2ecf20Sopenharmony_ci			  "Dropping frame as link state is down.\n");
26598c2ecf20Sopenharmony_ci		kfree_skb(skb);
26608c2ecf20Sopenharmony_ci		return 0;
26618c2ecf20Sopenharmony_ci	}
26628c2ecf20Sopenharmony_ci
26638c2ecf20Sopenharmony_ci	skb_work = kzalloc(sizeof(struct qedf_skb_work), GFP_ATOMIC);
26648c2ecf20Sopenharmony_ci	if (!skb_work) {
26658c2ecf20Sopenharmony_ci		QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate skb_work so "
26668c2ecf20Sopenharmony_ci			   "dropping frame.\n");
26678c2ecf20Sopenharmony_ci		kfree_skb(skb);
26688c2ecf20Sopenharmony_ci		return 0;
26698c2ecf20Sopenharmony_ci	}
26708c2ecf20Sopenharmony_ci
26718c2ecf20Sopenharmony_ci	INIT_WORK(&skb_work->work, qedf_ll2_process_skb);
26728c2ecf20Sopenharmony_ci	skb_work->skb = skb;
26738c2ecf20Sopenharmony_ci	skb_work->qedf = qedf;
26748c2ecf20Sopenharmony_ci	queue_work(qedf->ll2_recv_wq, &skb_work->work);
26758c2ecf20Sopenharmony_ci
26768c2ecf20Sopenharmony_ci	return 0;
26778c2ecf20Sopenharmony_ci}
26788c2ecf20Sopenharmony_ci
26798c2ecf20Sopenharmony_cistatic struct qed_ll2_cb_ops qedf_ll2_cb_ops = {
26808c2ecf20Sopenharmony_ci	.rx_cb = qedf_ll2_rx,
26818c2ecf20Sopenharmony_ci	.tx_cb = NULL,
26828c2ecf20Sopenharmony_ci};
26838c2ecf20Sopenharmony_ci
26848c2ecf20Sopenharmony_ci/* Main thread to process I/O completions */
26858c2ecf20Sopenharmony_civoid qedf_fp_io_handler(struct work_struct *work)
26868c2ecf20Sopenharmony_ci{
26878c2ecf20Sopenharmony_ci	struct qedf_io_work *io_work =
26888c2ecf20Sopenharmony_ci	    container_of(work, struct qedf_io_work, work);
26898c2ecf20Sopenharmony_ci	u32 comp_type;
26908c2ecf20Sopenharmony_ci
26918c2ecf20Sopenharmony_ci	/*
26928c2ecf20Sopenharmony_ci	 * Deferred part of unsolicited CQE sends
26938c2ecf20Sopenharmony_ci	 * frame to libfc.
26948c2ecf20Sopenharmony_ci	 */
26958c2ecf20Sopenharmony_ci	comp_type = (io_work->cqe.cqe_data >>
26968c2ecf20Sopenharmony_ci	    FCOE_CQE_CQE_TYPE_SHIFT) &
26978c2ecf20Sopenharmony_ci	    FCOE_CQE_CQE_TYPE_MASK;
26988c2ecf20Sopenharmony_ci	if (comp_type == FCOE_UNSOLIC_CQE_TYPE &&
26998c2ecf20Sopenharmony_ci	    io_work->fp)
27008c2ecf20Sopenharmony_ci		fc_exch_recv(io_work->qedf->lport, io_work->fp);
27018c2ecf20Sopenharmony_ci	else
27028c2ecf20Sopenharmony_ci		qedf_process_cqe(io_work->qedf, &io_work->cqe);
27038c2ecf20Sopenharmony_ci
27048c2ecf20Sopenharmony_ci	kfree(io_work);
27058c2ecf20Sopenharmony_ci}
27068c2ecf20Sopenharmony_ci
27078c2ecf20Sopenharmony_cistatic int qedf_alloc_and_init_sb(struct qedf_ctx *qedf,
27088c2ecf20Sopenharmony_ci	struct qed_sb_info *sb_info, u16 sb_id)
27098c2ecf20Sopenharmony_ci{
27108c2ecf20Sopenharmony_ci	struct status_block_e4 *sb_virt;
27118c2ecf20Sopenharmony_ci	dma_addr_t sb_phys;
27128c2ecf20Sopenharmony_ci	int ret;
27138c2ecf20Sopenharmony_ci
27148c2ecf20Sopenharmony_ci	sb_virt = dma_alloc_coherent(&qedf->pdev->dev,
27158c2ecf20Sopenharmony_ci	    sizeof(struct status_block_e4), &sb_phys, GFP_KERNEL);
27168c2ecf20Sopenharmony_ci
27178c2ecf20Sopenharmony_ci	if (!sb_virt) {
27188c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx,
27198c2ecf20Sopenharmony_ci			 "Status block allocation failed for id = %d.\n",
27208c2ecf20Sopenharmony_ci			 sb_id);
27218c2ecf20Sopenharmony_ci		return -ENOMEM;
27228c2ecf20Sopenharmony_ci	}
27238c2ecf20Sopenharmony_ci
27248c2ecf20Sopenharmony_ci	ret = qed_ops->common->sb_init(qedf->cdev, sb_info, sb_virt, sb_phys,
27258c2ecf20Sopenharmony_ci	    sb_id, QED_SB_TYPE_STORAGE);
27268c2ecf20Sopenharmony_ci
27278c2ecf20Sopenharmony_ci	if (ret) {
27288c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx,
27298c2ecf20Sopenharmony_ci			 "Status block initialization failed (0x%x) for id = %d.\n",
27308c2ecf20Sopenharmony_ci			 ret, sb_id);
27318c2ecf20Sopenharmony_ci		return ret;
27328c2ecf20Sopenharmony_ci	}
27338c2ecf20Sopenharmony_ci
27348c2ecf20Sopenharmony_ci	return 0;
27358c2ecf20Sopenharmony_ci}
27368c2ecf20Sopenharmony_ci
27378c2ecf20Sopenharmony_cistatic void qedf_free_sb(struct qedf_ctx *qedf, struct qed_sb_info *sb_info)
27388c2ecf20Sopenharmony_ci{
27398c2ecf20Sopenharmony_ci	if (sb_info->sb_virt)
27408c2ecf20Sopenharmony_ci		dma_free_coherent(&qedf->pdev->dev, sizeof(*sb_info->sb_virt),
27418c2ecf20Sopenharmony_ci		    (void *)sb_info->sb_virt, sb_info->sb_phys);
27428c2ecf20Sopenharmony_ci}
27438c2ecf20Sopenharmony_ci
27448c2ecf20Sopenharmony_cistatic void qedf_destroy_sb(struct qedf_ctx *qedf)
27458c2ecf20Sopenharmony_ci{
27468c2ecf20Sopenharmony_ci	int id;
27478c2ecf20Sopenharmony_ci	struct qedf_fastpath *fp = NULL;
27488c2ecf20Sopenharmony_ci
27498c2ecf20Sopenharmony_ci	for (id = 0; id < qedf->num_queues; id++) {
27508c2ecf20Sopenharmony_ci		fp = &(qedf->fp_array[id]);
27518c2ecf20Sopenharmony_ci		if (fp->sb_id == QEDF_SB_ID_NULL)
27528c2ecf20Sopenharmony_ci			break;
27538c2ecf20Sopenharmony_ci		qedf_free_sb(qedf, fp->sb_info);
27548c2ecf20Sopenharmony_ci		kfree(fp->sb_info);
27558c2ecf20Sopenharmony_ci	}
27568c2ecf20Sopenharmony_ci	kfree(qedf->fp_array);
27578c2ecf20Sopenharmony_ci}
27588c2ecf20Sopenharmony_ci
27598c2ecf20Sopenharmony_cistatic int qedf_prepare_sb(struct qedf_ctx *qedf)
27608c2ecf20Sopenharmony_ci{
27618c2ecf20Sopenharmony_ci	int id;
27628c2ecf20Sopenharmony_ci	struct qedf_fastpath *fp;
27638c2ecf20Sopenharmony_ci	int ret;
27648c2ecf20Sopenharmony_ci
27658c2ecf20Sopenharmony_ci	qedf->fp_array =
27668c2ecf20Sopenharmony_ci	    kcalloc(qedf->num_queues, sizeof(struct qedf_fastpath),
27678c2ecf20Sopenharmony_ci		GFP_KERNEL);
27688c2ecf20Sopenharmony_ci
27698c2ecf20Sopenharmony_ci	if (!qedf->fp_array) {
27708c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "fastpath array allocation "
27718c2ecf20Sopenharmony_ci			  "failed.\n");
27728c2ecf20Sopenharmony_ci		return -ENOMEM;
27738c2ecf20Sopenharmony_ci	}
27748c2ecf20Sopenharmony_ci
27758c2ecf20Sopenharmony_ci	for (id = 0; id < qedf->num_queues; id++) {
27768c2ecf20Sopenharmony_ci		fp = &(qedf->fp_array[id]);
27778c2ecf20Sopenharmony_ci		fp->sb_id = QEDF_SB_ID_NULL;
27788c2ecf20Sopenharmony_ci		fp->sb_info = kcalloc(1, sizeof(*fp->sb_info), GFP_KERNEL);
27798c2ecf20Sopenharmony_ci		if (!fp->sb_info) {
27808c2ecf20Sopenharmony_ci			QEDF_ERR(&(qedf->dbg_ctx), "SB info struct "
27818c2ecf20Sopenharmony_ci				  "allocation failed.\n");
27828c2ecf20Sopenharmony_ci			goto err;
27838c2ecf20Sopenharmony_ci		}
27848c2ecf20Sopenharmony_ci		ret = qedf_alloc_and_init_sb(qedf, fp->sb_info, id);
27858c2ecf20Sopenharmony_ci		if (ret) {
27868c2ecf20Sopenharmony_ci			QEDF_ERR(&(qedf->dbg_ctx), "SB allocation and "
27878c2ecf20Sopenharmony_ci				  "initialization failed.\n");
27888c2ecf20Sopenharmony_ci			goto err;
27898c2ecf20Sopenharmony_ci		}
27908c2ecf20Sopenharmony_ci		fp->sb_id = id;
27918c2ecf20Sopenharmony_ci		fp->qedf = qedf;
27928c2ecf20Sopenharmony_ci		fp->cq_num_entries =
27938c2ecf20Sopenharmony_ci		    qedf->global_queues[id]->cq_mem_size /
27948c2ecf20Sopenharmony_ci		    sizeof(struct fcoe_cqe);
27958c2ecf20Sopenharmony_ci	}
27968c2ecf20Sopenharmony_cierr:
27978c2ecf20Sopenharmony_ci	return 0;
27988c2ecf20Sopenharmony_ci}
27998c2ecf20Sopenharmony_ci
28008c2ecf20Sopenharmony_civoid qedf_process_cqe(struct qedf_ctx *qedf, struct fcoe_cqe *cqe)
28018c2ecf20Sopenharmony_ci{
28028c2ecf20Sopenharmony_ci	u16 xid;
28038c2ecf20Sopenharmony_ci	struct qedf_ioreq *io_req;
28048c2ecf20Sopenharmony_ci	struct qedf_rport *fcport;
28058c2ecf20Sopenharmony_ci	u32 comp_type;
28068c2ecf20Sopenharmony_ci	u8 io_comp_type;
28078c2ecf20Sopenharmony_ci	unsigned long flags;
28088c2ecf20Sopenharmony_ci
28098c2ecf20Sopenharmony_ci	comp_type = (cqe->cqe_data >> FCOE_CQE_CQE_TYPE_SHIFT) &
28108c2ecf20Sopenharmony_ci	    FCOE_CQE_CQE_TYPE_MASK;
28118c2ecf20Sopenharmony_ci
28128c2ecf20Sopenharmony_ci	xid = cqe->cqe_data & FCOE_CQE_TASK_ID_MASK;
28138c2ecf20Sopenharmony_ci	io_req = &qedf->cmd_mgr->cmds[xid];
28148c2ecf20Sopenharmony_ci
28158c2ecf20Sopenharmony_ci	/* Completion not for a valid I/O anymore so just return */
28168c2ecf20Sopenharmony_ci	if (!io_req) {
28178c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx,
28188c2ecf20Sopenharmony_ci			 "io_req is NULL for xid=0x%x.\n", xid);
28198c2ecf20Sopenharmony_ci		return;
28208c2ecf20Sopenharmony_ci	}
28218c2ecf20Sopenharmony_ci
28228c2ecf20Sopenharmony_ci	fcport = io_req->fcport;
28238c2ecf20Sopenharmony_ci
28248c2ecf20Sopenharmony_ci	if (fcport == NULL) {
28258c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx,
28268c2ecf20Sopenharmony_ci			 "fcport is NULL for xid=0x%x io_req=%p.\n",
28278c2ecf20Sopenharmony_ci			 xid, io_req);
28288c2ecf20Sopenharmony_ci		return;
28298c2ecf20Sopenharmony_ci	}
28308c2ecf20Sopenharmony_ci
28318c2ecf20Sopenharmony_ci	/*
28328c2ecf20Sopenharmony_ci	 * Check that fcport is offloaded.  If it isn't then the spinlock
28338c2ecf20Sopenharmony_ci	 * isn't valid and shouldn't be taken. We should just return.
28348c2ecf20Sopenharmony_ci	 */
28358c2ecf20Sopenharmony_ci	if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) {
28368c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx,
28378c2ecf20Sopenharmony_ci			 "Session not offloaded yet, fcport = %p.\n", fcport);
28388c2ecf20Sopenharmony_ci		return;
28398c2ecf20Sopenharmony_ci	}
28408c2ecf20Sopenharmony_ci
28418c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fcport->rport_lock, flags);
28428c2ecf20Sopenharmony_ci	io_comp_type = io_req->cmd_type;
28438c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fcport->rport_lock, flags);
28448c2ecf20Sopenharmony_ci
28458c2ecf20Sopenharmony_ci	switch (comp_type) {
28468c2ecf20Sopenharmony_ci	case FCOE_GOOD_COMPLETION_CQE_TYPE:
28478c2ecf20Sopenharmony_ci		atomic_inc(&fcport->free_sqes);
28488c2ecf20Sopenharmony_ci		switch (io_comp_type) {
28498c2ecf20Sopenharmony_ci		case QEDF_SCSI_CMD:
28508c2ecf20Sopenharmony_ci			qedf_scsi_completion(qedf, cqe, io_req);
28518c2ecf20Sopenharmony_ci			break;
28528c2ecf20Sopenharmony_ci		case QEDF_ELS:
28538c2ecf20Sopenharmony_ci			qedf_process_els_compl(qedf, cqe, io_req);
28548c2ecf20Sopenharmony_ci			break;
28558c2ecf20Sopenharmony_ci		case QEDF_TASK_MGMT_CMD:
28568c2ecf20Sopenharmony_ci			qedf_process_tmf_compl(qedf, cqe, io_req);
28578c2ecf20Sopenharmony_ci			break;
28588c2ecf20Sopenharmony_ci		case QEDF_SEQ_CLEANUP:
28598c2ecf20Sopenharmony_ci			qedf_process_seq_cleanup_compl(qedf, cqe, io_req);
28608c2ecf20Sopenharmony_ci			break;
28618c2ecf20Sopenharmony_ci		}
28628c2ecf20Sopenharmony_ci		break;
28638c2ecf20Sopenharmony_ci	case FCOE_ERROR_DETECTION_CQE_TYPE:
28648c2ecf20Sopenharmony_ci		atomic_inc(&fcport->free_sqes);
28658c2ecf20Sopenharmony_ci		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO,
28668c2ecf20Sopenharmony_ci		    "Error detect CQE.\n");
28678c2ecf20Sopenharmony_ci		qedf_process_error_detect(qedf, cqe, io_req);
28688c2ecf20Sopenharmony_ci		break;
28698c2ecf20Sopenharmony_ci	case FCOE_EXCH_CLEANUP_CQE_TYPE:
28708c2ecf20Sopenharmony_ci		atomic_inc(&fcport->free_sqes);
28718c2ecf20Sopenharmony_ci		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO,
28728c2ecf20Sopenharmony_ci		    "Cleanup CQE.\n");
28738c2ecf20Sopenharmony_ci		qedf_process_cleanup_compl(qedf, cqe, io_req);
28748c2ecf20Sopenharmony_ci		break;
28758c2ecf20Sopenharmony_ci	case FCOE_ABTS_CQE_TYPE:
28768c2ecf20Sopenharmony_ci		atomic_inc(&fcport->free_sqes);
28778c2ecf20Sopenharmony_ci		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO,
28788c2ecf20Sopenharmony_ci		    "Abort CQE.\n");
28798c2ecf20Sopenharmony_ci		qedf_process_abts_compl(qedf, cqe, io_req);
28808c2ecf20Sopenharmony_ci		break;
28818c2ecf20Sopenharmony_ci	case FCOE_DUMMY_CQE_TYPE:
28828c2ecf20Sopenharmony_ci		atomic_inc(&fcport->free_sqes);
28838c2ecf20Sopenharmony_ci		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO,
28848c2ecf20Sopenharmony_ci		    "Dummy CQE.\n");
28858c2ecf20Sopenharmony_ci		break;
28868c2ecf20Sopenharmony_ci	case FCOE_LOCAL_COMP_CQE_TYPE:
28878c2ecf20Sopenharmony_ci		atomic_inc(&fcport->free_sqes);
28888c2ecf20Sopenharmony_ci		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO,
28898c2ecf20Sopenharmony_ci		    "Local completion CQE.\n");
28908c2ecf20Sopenharmony_ci		break;
28918c2ecf20Sopenharmony_ci	case FCOE_WARNING_CQE_TYPE:
28928c2ecf20Sopenharmony_ci		atomic_inc(&fcport->free_sqes);
28938c2ecf20Sopenharmony_ci		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO,
28948c2ecf20Sopenharmony_ci		    "Warning CQE.\n");
28958c2ecf20Sopenharmony_ci		qedf_process_warning_compl(qedf, cqe, io_req);
28968c2ecf20Sopenharmony_ci		break;
28978c2ecf20Sopenharmony_ci	case MAX_FCOE_CQE_TYPE:
28988c2ecf20Sopenharmony_ci		atomic_inc(&fcport->free_sqes);
28998c2ecf20Sopenharmony_ci		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO,
29008c2ecf20Sopenharmony_ci		    "Max FCoE CQE.\n");
29018c2ecf20Sopenharmony_ci		break;
29028c2ecf20Sopenharmony_ci	default:
29038c2ecf20Sopenharmony_ci		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO,
29048c2ecf20Sopenharmony_ci		    "Default CQE.\n");
29058c2ecf20Sopenharmony_ci		break;
29068c2ecf20Sopenharmony_ci	}
29078c2ecf20Sopenharmony_ci}
29088c2ecf20Sopenharmony_ci
29098c2ecf20Sopenharmony_cistatic void qedf_free_bdq(struct qedf_ctx *qedf)
29108c2ecf20Sopenharmony_ci{
29118c2ecf20Sopenharmony_ci	int i;
29128c2ecf20Sopenharmony_ci
29138c2ecf20Sopenharmony_ci	if (qedf->bdq_pbl_list)
29148c2ecf20Sopenharmony_ci		dma_free_coherent(&qedf->pdev->dev, QEDF_PAGE_SIZE,
29158c2ecf20Sopenharmony_ci		    qedf->bdq_pbl_list, qedf->bdq_pbl_list_dma);
29168c2ecf20Sopenharmony_ci
29178c2ecf20Sopenharmony_ci	if (qedf->bdq_pbl)
29188c2ecf20Sopenharmony_ci		dma_free_coherent(&qedf->pdev->dev, qedf->bdq_pbl_mem_size,
29198c2ecf20Sopenharmony_ci		    qedf->bdq_pbl, qedf->bdq_pbl_dma);
29208c2ecf20Sopenharmony_ci
29218c2ecf20Sopenharmony_ci	for (i = 0; i < QEDF_BDQ_SIZE; i++) {
29228c2ecf20Sopenharmony_ci		if (qedf->bdq[i].buf_addr) {
29238c2ecf20Sopenharmony_ci			dma_free_coherent(&qedf->pdev->dev, QEDF_BDQ_BUF_SIZE,
29248c2ecf20Sopenharmony_ci			    qedf->bdq[i].buf_addr, qedf->bdq[i].buf_dma);
29258c2ecf20Sopenharmony_ci		}
29268c2ecf20Sopenharmony_ci	}
29278c2ecf20Sopenharmony_ci}
29288c2ecf20Sopenharmony_ci
29298c2ecf20Sopenharmony_cistatic void qedf_free_global_queues(struct qedf_ctx *qedf)
29308c2ecf20Sopenharmony_ci{
29318c2ecf20Sopenharmony_ci	int i;
29328c2ecf20Sopenharmony_ci	struct global_queue **gl = qedf->global_queues;
29338c2ecf20Sopenharmony_ci
29348c2ecf20Sopenharmony_ci	for (i = 0; i < qedf->num_queues; i++) {
29358c2ecf20Sopenharmony_ci		if (!gl[i])
29368c2ecf20Sopenharmony_ci			continue;
29378c2ecf20Sopenharmony_ci
29388c2ecf20Sopenharmony_ci		if (gl[i]->cq)
29398c2ecf20Sopenharmony_ci			dma_free_coherent(&qedf->pdev->dev,
29408c2ecf20Sopenharmony_ci			    gl[i]->cq_mem_size, gl[i]->cq, gl[i]->cq_dma);
29418c2ecf20Sopenharmony_ci		if (gl[i]->cq_pbl)
29428c2ecf20Sopenharmony_ci			dma_free_coherent(&qedf->pdev->dev, gl[i]->cq_pbl_size,
29438c2ecf20Sopenharmony_ci			    gl[i]->cq_pbl, gl[i]->cq_pbl_dma);
29448c2ecf20Sopenharmony_ci
29458c2ecf20Sopenharmony_ci		kfree(gl[i]);
29468c2ecf20Sopenharmony_ci	}
29478c2ecf20Sopenharmony_ci
29488c2ecf20Sopenharmony_ci	qedf_free_bdq(qedf);
29498c2ecf20Sopenharmony_ci}
29508c2ecf20Sopenharmony_ci
29518c2ecf20Sopenharmony_cistatic int qedf_alloc_bdq(struct qedf_ctx *qedf)
29528c2ecf20Sopenharmony_ci{
29538c2ecf20Sopenharmony_ci	int i;
29548c2ecf20Sopenharmony_ci	struct scsi_bd *pbl;
29558c2ecf20Sopenharmony_ci	u64 *list;
29568c2ecf20Sopenharmony_ci	dma_addr_t page;
29578c2ecf20Sopenharmony_ci
29588c2ecf20Sopenharmony_ci	/* Alloc dma memory for BDQ buffers */
29598c2ecf20Sopenharmony_ci	for (i = 0; i < QEDF_BDQ_SIZE; i++) {
29608c2ecf20Sopenharmony_ci		qedf->bdq[i].buf_addr = dma_alloc_coherent(&qedf->pdev->dev,
29618c2ecf20Sopenharmony_ci		    QEDF_BDQ_BUF_SIZE, &qedf->bdq[i].buf_dma, GFP_KERNEL);
29628c2ecf20Sopenharmony_ci		if (!qedf->bdq[i].buf_addr) {
29638c2ecf20Sopenharmony_ci			QEDF_ERR(&(qedf->dbg_ctx), "Could not allocate BDQ "
29648c2ecf20Sopenharmony_ci			    "buffer %d.\n", i);
29658c2ecf20Sopenharmony_ci			return -ENOMEM;
29668c2ecf20Sopenharmony_ci		}
29678c2ecf20Sopenharmony_ci	}
29688c2ecf20Sopenharmony_ci
29698c2ecf20Sopenharmony_ci	/* Alloc dma memory for BDQ page buffer list */
29708c2ecf20Sopenharmony_ci	qedf->bdq_pbl_mem_size =
29718c2ecf20Sopenharmony_ci	    QEDF_BDQ_SIZE * sizeof(struct scsi_bd);
29728c2ecf20Sopenharmony_ci	qedf->bdq_pbl_mem_size =
29738c2ecf20Sopenharmony_ci	    ALIGN(qedf->bdq_pbl_mem_size, QEDF_PAGE_SIZE);
29748c2ecf20Sopenharmony_ci
29758c2ecf20Sopenharmony_ci	qedf->bdq_pbl = dma_alloc_coherent(&qedf->pdev->dev,
29768c2ecf20Sopenharmony_ci	    qedf->bdq_pbl_mem_size, &qedf->bdq_pbl_dma, GFP_KERNEL);
29778c2ecf20Sopenharmony_ci	if (!qedf->bdq_pbl) {
29788c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "Could not allocate BDQ PBL.\n");
29798c2ecf20Sopenharmony_ci		return -ENOMEM;
29808c2ecf20Sopenharmony_ci	}
29818c2ecf20Sopenharmony_ci
29828c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
29838c2ecf20Sopenharmony_ci		  "BDQ PBL addr=0x%p dma=%pad\n",
29848c2ecf20Sopenharmony_ci		  qedf->bdq_pbl, &qedf->bdq_pbl_dma);
29858c2ecf20Sopenharmony_ci
29868c2ecf20Sopenharmony_ci	/*
29878c2ecf20Sopenharmony_ci	 * Populate BDQ PBL with physical and virtual address of individual
29888c2ecf20Sopenharmony_ci	 * BDQ buffers
29898c2ecf20Sopenharmony_ci	 */
29908c2ecf20Sopenharmony_ci	pbl = (struct scsi_bd *)qedf->bdq_pbl;
29918c2ecf20Sopenharmony_ci	for (i = 0; i < QEDF_BDQ_SIZE; i++) {
29928c2ecf20Sopenharmony_ci		pbl->address.hi = cpu_to_le32(U64_HI(qedf->bdq[i].buf_dma));
29938c2ecf20Sopenharmony_ci		pbl->address.lo = cpu_to_le32(U64_LO(qedf->bdq[i].buf_dma));
29948c2ecf20Sopenharmony_ci		pbl->opaque.fcoe_opaque.hi = 0;
29958c2ecf20Sopenharmony_ci		/* Opaque lo data is an index into the BDQ array */
29968c2ecf20Sopenharmony_ci		pbl->opaque.fcoe_opaque.lo = cpu_to_le32(i);
29978c2ecf20Sopenharmony_ci		pbl++;
29988c2ecf20Sopenharmony_ci	}
29998c2ecf20Sopenharmony_ci
30008c2ecf20Sopenharmony_ci	/* Allocate list of PBL pages */
30018c2ecf20Sopenharmony_ci	qedf->bdq_pbl_list = dma_alloc_coherent(&qedf->pdev->dev,
30028c2ecf20Sopenharmony_ci						QEDF_PAGE_SIZE,
30038c2ecf20Sopenharmony_ci						&qedf->bdq_pbl_list_dma,
30048c2ecf20Sopenharmony_ci						GFP_KERNEL);
30058c2ecf20Sopenharmony_ci	if (!qedf->bdq_pbl_list) {
30068c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "Could not allocate list of PBL pages.\n");
30078c2ecf20Sopenharmony_ci		return -ENOMEM;
30088c2ecf20Sopenharmony_ci	}
30098c2ecf20Sopenharmony_ci
30108c2ecf20Sopenharmony_ci	/*
30118c2ecf20Sopenharmony_ci	 * Now populate PBL list with pages that contain pointers to the
30128c2ecf20Sopenharmony_ci	 * individual buffers.
30138c2ecf20Sopenharmony_ci	 */
30148c2ecf20Sopenharmony_ci	qedf->bdq_pbl_list_num_entries = qedf->bdq_pbl_mem_size /
30158c2ecf20Sopenharmony_ci	    QEDF_PAGE_SIZE;
30168c2ecf20Sopenharmony_ci	list = (u64 *)qedf->bdq_pbl_list;
30178c2ecf20Sopenharmony_ci	page = qedf->bdq_pbl_list_dma;
30188c2ecf20Sopenharmony_ci	for (i = 0; i < qedf->bdq_pbl_list_num_entries; i++) {
30198c2ecf20Sopenharmony_ci		*list = qedf->bdq_pbl_dma;
30208c2ecf20Sopenharmony_ci		list++;
30218c2ecf20Sopenharmony_ci		page += QEDF_PAGE_SIZE;
30228c2ecf20Sopenharmony_ci	}
30238c2ecf20Sopenharmony_ci
30248c2ecf20Sopenharmony_ci	return 0;
30258c2ecf20Sopenharmony_ci}
30268c2ecf20Sopenharmony_ci
30278c2ecf20Sopenharmony_cistatic int qedf_alloc_global_queues(struct qedf_ctx *qedf)
30288c2ecf20Sopenharmony_ci{
30298c2ecf20Sopenharmony_ci	u32 *list;
30308c2ecf20Sopenharmony_ci	int i;
30318c2ecf20Sopenharmony_ci	int status;
30328c2ecf20Sopenharmony_ci	u32 *pbl;
30338c2ecf20Sopenharmony_ci	dma_addr_t page;
30348c2ecf20Sopenharmony_ci	int num_pages;
30358c2ecf20Sopenharmony_ci
30368c2ecf20Sopenharmony_ci	/* Allocate and map CQs, RQs */
30378c2ecf20Sopenharmony_ci	/*
30388c2ecf20Sopenharmony_ci	 * Number of global queues (CQ / RQ). This should
30398c2ecf20Sopenharmony_ci	 * be <= number of available MSIX vectors for the PF
30408c2ecf20Sopenharmony_ci	 */
30418c2ecf20Sopenharmony_ci	if (!qedf->num_queues) {
30428c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "No MSI-X vectors available!\n");
30438c2ecf20Sopenharmony_ci		return -ENOMEM;
30448c2ecf20Sopenharmony_ci	}
30458c2ecf20Sopenharmony_ci
30468c2ecf20Sopenharmony_ci	/*
30478c2ecf20Sopenharmony_ci	 * Make sure we allocated the PBL that will contain the physical
30488c2ecf20Sopenharmony_ci	 * addresses of our queues
30498c2ecf20Sopenharmony_ci	 */
30508c2ecf20Sopenharmony_ci	if (!qedf->p_cpuq) {
30518c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx, "p_cpuq is NULL.\n");
30528c2ecf20Sopenharmony_ci		return -EINVAL;
30538c2ecf20Sopenharmony_ci	}
30548c2ecf20Sopenharmony_ci
30558c2ecf20Sopenharmony_ci	qedf->global_queues = kzalloc((sizeof(struct global_queue *)
30568c2ecf20Sopenharmony_ci	    * qedf->num_queues), GFP_KERNEL);
30578c2ecf20Sopenharmony_ci	if (!qedf->global_queues) {
30588c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "Unable to allocate global "
30598c2ecf20Sopenharmony_ci			  "queues array ptr memory\n");
30608c2ecf20Sopenharmony_ci		return -ENOMEM;
30618c2ecf20Sopenharmony_ci	}
30628c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
30638c2ecf20Sopenharmony_ci		   "qedf->global_queues=%p.\n", qedf->global_queues);
30648c2ecf20Sopenharmony_ci
30658c2ecf20Sopenharmony_ci	/* Allocate DMA coherent buffers for BDQ */
30668c2ecf20Sopenharmony_ci	status = qedf_alloc_bdq(qedf);
30678c2ecf20Sopenharmony_ci	if (status) {
30688c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx, "Unable to allocate bdq.\n");
30698c2ecf20Sopenharmony_ci		goto mem_alloc_failure;
30708c2ecf20Sopenharmony_ci	}
30718c2ecf20Sopenharmony_ci
30728c2ecf20Sopenharmony_ci	/* Allocate a CQ and an associated PBL for each MSI-X vector */
30738c2ecf20Sopenharmony_ci	for (i = 0; i < qedf->num_queues; i++) {
30748c2ecf20Sopenharmony_ci		qedf->global_queues[i] = kzalloc(sizeof(struct global_queue),
30758c2ecf20Sopenharmony_ci		    GFP_KERNEL);
30768c2ecf20Sopenharmony_ci		if (!qedf->global_queues[i]) {
30778c2ecf20Sopenharmony_ci			QEDF_WARN(&(qedf->dbg_ctx), "Unable to allocate "
30788c2ecf20Sopenharmony_ci				   "global queue %d.\n", i);
30798c2ecf20Sopenharmony_ci			status = -ENOMEM;
30808c2ecf20Sopenharmony_ci			goto mem_alloc_failure;
30818c2ecf20Sopenharmony_ci		}
30828c2ecf20Sopenharmony_ci
30838c2ecf20Sopenharmony_ci		qedf->global_queues[i]->cq_mem_size =
30848c2ecf20Sopenharmony_ci		    FCOE_PARAMS_CQ_NUM_ENTRIES * sizeof(struct fcoe_cqe);
30858c2ecf20Sopenharmony_ci		qedf->global_queues[i]->cq_mem_size =
30868c2ecf20Sopenharmony_ci		    ALIGN(qedf->global_queues[i]->cq_mem_size, QEDF_PAGE_SIZE);
30878c2ecf20Sopenharmony_ci
30888c2ecf20Sopenharmony_ci		qedf->global_queues[i]->cq_pbl_size =
30898c2ecf20Sopenharmony_ci		    (qedf->global_queues[i]->cq_mem_size /
30908c2ecf20Sopenharmony_ci		    PAGE_SIZE) * sizeof(void *);
30918c2ecf20Sopenharmony_ci		qedf->global_queues[i]->cq_pbl_size =
30928c2ecf20Sopenharmony_ci		    ALIGN(qedf->global_queues[i]->cq_pbl_size, QEDF_PAGE_SIZE);
30938c2ecf20Sopenharmony_ci
30948c2ecf20Sopenharmony_ci		qedf->global_queues[i]->cq =
30958c2ecf20Sopenharmony_ci		    dma_alloc_coherent(&qedf->pdev->dev,
30968c2ecf20Sopenharmony_ci				       qedf->global_queues[i]->cq_mem_size,
30978c2ecf20Sopenharmony_ci				       &qedf->global_queues[i]->cq_dma,
30988c2ecf20Sopenharmony_ci				       GFP_KERNEL);
30998c2ecf20Sopenharmony_ci
31008c2ecf20Sopenharmony_ci		if (!qedf->global_queues[i]->cq) {
31018c2ecf20Sopenharmony_ci			QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate cq.\n");
31028c2ecf20Sopenharmony_ci			status = -ENOMEM;
31038c2ecf20Sopenharmony_ci			goto mem_alloc_failure;
31048c2ecf20Sopenharmony_ci		}
31058c2ecf20Sopenharmony_ci
31068c2ecf20Sopenharmony_ci		qedf->global_queues[i]->cq_pbl =
31078c2ecf20Sopenharmony_ci		    dma_alloc_coherent(&qedf->pdev->dev,
31088c2ecf20Sopenharmony_ci				       qedf->global_queues[i]->cq_pbl_size,
31098c2ecf20Sopenharmony_ci				       &qedf->global_queues[i]->cq_pbl_dma,
31108c2ecf20Sopenharmony_ci				       GFP_KERNEL);
31118c2ecf20Sopenharmony_ci
31128c2ecf20Sopenharmony_ci		if (!qedf->global_queues[i]->cq_pbl) {
31138c2ecf20Sopenharmony_ci			QEDF_WARN(&(qedf->dbg_ctx), "Could not allocate cq PBL.\n");
31148c2ecf20Sopenharmony_ci			status = -ENOMEM;
31158c2ecf20Sopenharmony_ci			goto mem_alloc_failure;
31168c2ecf20Sopenharmony_ci		}
31178c2ecf20Sopenharmony_ci
31188c2ecf20Sopenharmony_ci		/* Create PBL */
31198c2ecf20Sopenharmony_ci		num_pages = qedf->global_queues[i]->cq_mem_size /
31208c2ecf20Sopenharmony_ci		    QEDF_PAGE_SIZE;
31218c2ecf20Sopenharmony_ci		page = qedf->global_queues[i]->cq_dma;
31228c2ecf20Sopenharmony_ci		pbl = (u32 *)qedf->global_queues[i]->cq_pbl;
31238c2ecf20Sopenharmony_ci
31248c2ecf20Sopenharmony_ci		while (num_pages--) {
31258c2ecf20Sopenharmony_ci			*pbl = U64_LO(page);
31268c2ecf20Sopenharmony_ci			pbl++;
31278c2ecf20Sopenharmony_ci			*pbl = U64_HI(page);
31288c2ecf20Sopenharmony_ci			pbl++;
31298c2ecf20Sopenharmony_ci			page += QEDF_PAGE_SIZE;
31308c2ecf20Sopenharmony_ci		}
31318c2ecf20Sopenharmony_ci		/* Set the initial consumer index for cq */
31328c2ecf20Sopenharmony_ci		qedf->global_queues[i]->cq_cons_idx = 0;
31338c2ecf20Sopenharmony_ci	}
31348c2ecf20Sopenharmony_ci
31358c2ecf20Sopenharmony_ci	list = (u32 *)qedf->p_cpuq;
31368c2ecf20Sopenharmony_ci
31378c2ecf20Sopenharmony_ci	/*
31388c2ecf20Sopenharmony_ci	 * The list is built as follows: CQ#0 PBL pointer, RQ#0 PBL pointer,
31398c2ecf20Sopenharmony_ci	 * CQ#1 PBL pointer, RQ#1 PBL pointer, etc.  Each PBL pointer points
31408c2ecf20Sopenharmony_ci	 * to the physical address which contains an array of pointers to
31418c2ecf20Sopenharmony_ci	 * the physical addresses of the specific queue pages.
31428c2ecf20Sopenharmony_ci	 */
31438c2ecf20Sopenharmony_ci	for (i = 0; i < qedf->num_queues; i++) {
31448c2ecf20Sopenharmony_ci		*list = U64_LO(qedf->global_queues[i]->cq_pbl_dma);
31458c2ecf20Sopenharmony_ci		list++;
31468c2ecf20Sopenharmony_ci		*list = U64_HI(qedf->global_queues[i]->cq_pbl_dma);
31478c2ecf20Sopenharmony_ci		list++;
31488c2ecf20Sopenharmony_ci		*list = U64_LO(0);
31498c2ecf20Sopenharmony_ci		list++;
31508c2ecf20Sopenharmony_ci		*list = U64_HI(0);
31518c2ecf20Sopenharmony_ci		list++;
31528c2ecf20Sopenharmony_ci	}
31538c2ecf20Sopenharmony_ci
31548c2ecf20Sopenharmony_ci	return 0;
31558c2ecf20Sopenharmony_ci
31568c2ecf20Sopenharmony_cimem_alloc_failure:
31578c2ecf20Sopenharmony_ci	qedf_free_global_queues(qedf);
31588c2ecf20Sopenharmony_ci	return status;
31598c2ecf20Sopenharmony_ci}
31608c2ecf20Sopenharmony_ci
31618c2ecf20Sopenharmony_cistatic int qedf_set_fcoe_pf_param(struct qedf_ctx *qedf)
31628c2ecf20Sopenharmony_ci{
31638c2ecf20Sopenharmony_ci	u8 sq_num_pbl_pages;
31648c2ecf20Sopenharmony_ci	u32 sq_mem_size;
31658c2ecf20Sopenharmony_ci	u32 cq_mem_size;
31668c2ecf20Sopenharmony_ci	u32 cq_num_entries;
31678c2ecf20Sopenharmony_ci	int rval;
31688c2ecf20Sopenharmony_ci
31698c2ecf20Sopenharmony_ci	/*
31708c2ecf20Sopenharmony_ci	 * The number of completion queues/fastpath interrupts/status blocks
31718c2ecf20Sopenharmony_ci	 * we allocation is the minimum off:
31728c2ecf20Sopenharmony_ci	 *
31738c2ecf20Sopenharmony_ci	 * Number of CPUs
31748c2ecf20Sopenharmony_ci	 * Number allocated by qed for our PCI function
31758c2ecf20Sopenharmony_ci	 */
31768c2ecf20Sopenharmony_ci	qedf->num_queues = MIN_NUM_CPUS_MSIX(qedf);
31778c2ecf20Sopenharmony_ci
31788c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Number of CQs is %d.\n",
31798c2ecf20Sopenharmony_ci		   qedf->num_queues);
31808c2ecf20Sopenharmony_ci
31818c2ecf20Sopenharmony_ci	qedf->p_cpuq = dma_alloc_coherent(&qedf->pdev->dev,
31828c2ecf20Sopenharmony_ci	    qedf->num_queues * sizeof(struct qedf_glbl_q_params),
31838c2ecf20Sopenharmony_ci	    &qedf->hw_p_cpuq, GFP_KERNEL);
31848c2ecf20Sopenharmony_ci
31858c2ecf20Sopenharmony_ci	if (!qedf->p_cpuq) {
31868c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "dma_alloc_coherent failed.\n");
31878c2ecf20Sopenharmony_ci		return 1;
31888c2ecf20Sopenharmony_ci	}
31898c2ecf20Sopenharmony_ci
31908c2ecf20Sopenharmony_ci	rval = qedf_alloc_global_queues(qedf);
31918c2ecf20Sopenharmony_ci	if (rval) {
31928c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "Global queue allocation "
31938c2ecf20Sopenharmony_ci			  "failed.\n");
31948c2ecf20Sopenharmony_ci		return 1;
31958c2ecf20Sopenharmony_ci	}
31968c2ecf20Sopenharmony_ci
31978c2ecf20Sopenharmony_ci	/* Calculate SQ PBL size in the same manner as in qedf_sq_alloc() */
31988c2ecf20Sopenharmony_ci	sq_mem_size = SQ_NUM_ENTRIES * sizeof(struct fcoe_wqe);
31998c2ecf20Sopenharmony_ci	sq_mem_size = ALIGN(sq_mem_size, QEDF_PAGE_SIZE);
32008c2ecf20Sopenharmony_ci	sq_num_pbl_pages = (sq_mem_size / QEDF_PAGE_SIZE);
32018c2ecf20Sopenharmony_ci
32028c2ecf20Sopenharmony_ci	/* Calculate CQ num entries */
32038c2ecf20Sopenharmony_ci	cq_mem_size = FCOE_PARAMS_CQ_NUM_ENTRIES * sizeof(struct fcoe_cqe);
32048c2ecf20Sopenharmony_ci	cq_mem_size = ALIGN(cq_mem_size, QEDF_PAGE_SIZE);
32058c2ecf20Sopenharmony_ci	cq_num_entries = cq_mem_size / sizeof(struct fcoe_cqe);
32068c2ecf20Sopenharmony_ci
32078c2ecf20Sopenharmony_ci	memset(&(qedf->pf_params), 0, sizeof(qedf->pf_params));
32088c2ecf20Sopenharmony_ci
32098c2ecf20Sopenharmony_ci	/* Setup the value for fcoe PF */
32108c2ecf20Sopenharmony_ci	qedf->pf_params.fcoe_pf_params.num_cons = QEDF_MAX_SESSIONS;
32118c2ecf20Sopenharmony_ci	qedf->pf_params.fcoe_pf_params.num_tasks = FCOE_PARAMS_NUM_TASKS;
32128c2ecf20Sopenharmony_ci	qedf->pf_params.fcoe_pf_params.glbl_q_params_addr =
32138c2ecf20Sopenharmony_ci	    (u64)qedf->hw_p_cpuq;
32148c2ecf20Sopenharmony_ci	qedf->pf_params.fcoe_pf_params.sq_num_pbl_pages = sq_num_pbl_pages;
32158c2ecf20Sopenharmony_ci
32168c2ecf20Sopenharmony_ci	qedf->pf_params.fcoe_pf_params.rq_buffer_log_size = 0;
32178c2ecf20Sopenharmony_ci
32188c2ecf20Sopenharmony_ci	qedf->pf_params.fcoe_pf_params.cq_num_entries = cq_num_entries;
32198c2ecf20Sopenharmony_ci	qedf->pf_params.fcoe_pf_params.num_cqs = qedf->num_queues;
32208c2ecf20Sopenharmony_ci
32218c2ecf20Sopenharmony_ci	/* log_page_size: 12 for 4KB pages */
32228c2ecf20Sopenharmony_ci	qedf->pf_params.fcoe_pf_params.log_page_size = ilog2(QEDF_PAGE_SIZE);
32238c2ecf20Sopenharmony_ci
32248c2ecf20Sopenharmony_ci	qedf->pf_params.fcoe_pf_params.mtu = 9000;
32258c2ecf20Sopenharmony_ci	qedf->pf_params.fcoe_pf_params.gl_rq_pi = QEDF_FCOE_PARAMS_GL_RQ_PI;
32268c2ecf20Sopenharmony_ci	qedf->pf_params.fcoe_pf_params.gl_cmd_pi = QEDF_FCOE_PARAMS_GL_CMD_PI;
32278c2ecf20Sopenharmony_ci
32288c2ecf20Sopenharmony_ci	/* BDQ address and size */
32298c2ecf20Sopenharmony_ci	qedf->pf_params.fcoe_pf_params.bdq_pbl_base_addr[0] =
32308c2ecf20Sopenharmony_ci	    qedf->bdq_pbl_list_dma;
32318c2ecf20Sopenharmony_ci	qedf->pf_params.fcoe_pf_params.bdq_pbl_num_entries[0] =
32328c2ecf20Sopenharmony_ci	    qedf->bdq_pbl_list_num_entries;
32338c2ecf20Sopenharmony_ci	qedf->pf_params.fcoe_pf_params.rq_buffer_size = QEDF_BDQ_BUF_SIZE;
32348c2ecf20Sopenharmony_ci
32358c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
32368c2ecf20Sopenharmony_ci	    "bdq_list=%p bdq_pbl_list_dma=%llx bdq_pbl_list_entries=%d.\n",
32378c2ecf20Sopenharmony_ci	    qedf->bdq_pbl_list,
32388c2ecf20Sopenharmony_ci	    qedf->pf_params.fcoe_pf_params.bdq_pbl_base_addr[0],
32398c2ecf20Sopenharmony_ci	    qedf->pf_params.fcoe_pf_params.bdq_pbl_num_entries[0]);
32408c2ecf20Sopenharmony_ci
32418c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
32428c2ecf20Sopenharmony_ci	    "cq_num_entries=%d.\n",
32438c2ecf20Sopenharmony_ci	    qedf->pf_params.fcoe_pf_params.cq_num_entries);
32448c2ecf20Sopenharmony_ci
32458c2ecf20Sopenharmony_ci	return 0;
32468c2ecf20Sopenharmony_ci}
32478c2ecf20Sopenharmony_ci
32488c2ecf20Sopenharmony_ci/* Free DMA coherent memory for array of queue pointers we pass to qed */
32498c2ecf20Sopenharmony_cistatic void qedf_free_fcoe_pf_param(struct qedf_ctx *qedf)
32508c2ecf20Sopenharmony_ci{
32518c2ecf20Sopenharmony_ci	size_t size = 0;
32528c2ecf20Sopenharmony_ci
32538c2ecf20Sopenharmony_ci	if (qedf->p_cpuq) {
32548c2ecf20Sopenharmony_ci		size = qedf->num_queues * sizeof(struct qedf_glbl_q_params);
32558c2ecf20Sopenharmony_ci		dma_free_coherent(&qedf->pdev->dev, size, qedf->p_cpuq,
32568c2ecf20Sopenharmony_ci		    qedf->hw_p_cpuq);
32578c2ecf20Sopenharmony_ci	}
32588c2ecf20Sopenharmony_ci
32598c2ecf20Sopenharmony_ci	qedf_free_global_queues(qedf);
32608c2ecf20Sopenharmony_ci
32618c2ecf20Sopenharmony_ci	kfree(qedf->global_queues);
32628c2ecf20Sopenharmony_ci}
32638c2ecf20Sopenharmony_ci
32648c2ecf20Sopenharmony_ci/*
32658c2ecf20Sopenharmony_ci * PCI driver functions
32668c2ecf20Sopenharmony_ci */
32678c2ecf20Sopenharmony_ci
32688c2ecf20Sopenharmony_cistatic const struct pci_device_id qedf_pci_tbl[] = {
32698c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, 0x165c) },
32708c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, 0x8080) },
32718c2ecf20Sopenharmony_ci	{0}
32728c2ecf20Sopenharmony_ci};
32738c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, qedf_pci_tbl);
32748c2ecf20Sopenharmony_ci
32758c2ecf20Sopenharmony_cistatic struct pci_driver qedf_pci_driver = {
32768c2ecf20Sopenharmony_ci	.name = QEDF_MODULE_NAME,
32778c2ecf20Sopenharmony_ci	.id_table = qedf_pci_tbl,
32788c2ecf20Sopenharmony_ci	.probe = qedf_probe,
32798c2ecf20Sopenharmony_ci	.remove = qedf_remove,
32808c2ecf20Sopenharmony_ci	.shutdown = qedf_shutdown,
32818c2ecf20Sopenharmony_ci	.suspend = qedf_suspend,
32828c2ecf20Sopenharmony_ci};
32838c2ecf20Sopenharmony_ci
32848c2ecf20Sopenharmony_cistatic int __qedf_probe(struct pci_dev *pdev, int mode)
32858c2ecf20Sopenharmony_ci{
32868c2ecf20Sopenharmony_ci	int rc = -EINVAL;
32878c2ecf20Sopenharmony_ci	struct fc_lport *lport;
32888c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf = NULL;
32898c2ecf20Sopenharmony_ci	struct Scsi_Host *host;
32908c2ecf20Sopenharmony_ci	bool is_vf = false;
32918c2ecf20Sopenharmony_ci	struct qed_ll2_params params;
32928c2ecf20Sopenharmony_ci	char host_buf[20];
32938c2ecf20Sopenharmony_ci	struct qed_link_params link_params;
32948c2ecf20Sopenharmony_ci	int status;
32958c2ecf20Sopenharmony_ci	void *task_start, *task_end;
32968c2ecf20Sopenharmony_ci	struct qed_slowpath_params slowpath_params;
32978c2ecf20Sopenharmony_ci	struct qed_probe_params qed_params;
32988c2ecf20Sopenharmony_ci	u16 retry_cnt = 10;
32998c2ecf20Sopenharmony_ci
33008c2ecf20Sopenharmony_ci	/*
33018c2ecf20Sopenharmony_ci	 * When doing error recovery we didn't reap the lport so don't try
33028c2ecf20Sopenharmony_ci	 * to reallocate it.
33038c2ecf20Sopenharmony_ci	 */
33048c2ecf20Sopenharmony_ciretry_probe:
33058c2ecf20Sopenharmony_ci	if (mode == QEDF_MODE_RECOVERY)
33068c2ecf20Sopenharmony_ci		msleep(2000);
33078c2ecf20Sopenharmony_ci
33088c2ecf20Sopenharmony_ci	if (mode != QEDF_MODE_RECOVERY) {
33098c2ecf20Sopenharmony_ci		lport = libfc_host_alloc(&qedf_host_template,
33108c2ecf20Sopenharmony_ci		    sizeof(struct qedf_ctx));
33118c2ecf20Sopenharmony_ci
33128c2ecf20Sopenharmony_ci		if (!lport) {
33138c2ecf20Sopenharmony_ci			QEDF_ERR(NULL, "Could not allocate lport.\n");
33148c2ecf20Sopenharmony_ci			rc = -ENOMEM;
33158c2ecf20Sopenharmony_ci			goto err0;
33168c2ecf20Sopenharmony_ci		}
33178c2ecf20Sopenharmony_ci
33188c2ecf20Sopenharmony_ci		fc_disc_init(lport);
33198c2ecf20Sopenharmony_ci
33208c2ecf20Sopenharmony_ci		/* Initialize qedf_ctx */
33218c2ecf20Sopenharmony_ci		qedf = lport_priv(lport);
33228c2ecf20Sopenharmony_ci		set_bit(QEDF_PROBING, &qedf->flags);
33238c2ecf20Sopenharmony_ci		qedf->lport = lport;
33248c2ecf20Sopenharmony_ci		qedf->ctlr.lp = lport;
33258c2ecf20Sopenharmony_ci		qedf->pdev = pdev;
33268c2ecf20Sopenharmony_ci		qedf->dbg_ctx.pdev = pdev;
33278c2ecf20Sopenharmony_ci		qedf->dbg_ctx.host_no = lport->host->host_no;
33288c2ecf20Sopenharmony_ci		spin_lock_init(&qedf->hba_lock);
33298c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&qedf->fcports);
33308c2ecf20Sopenharmony_ci		qedf->curr_conn_id = QEDF_MAX_SESSIONS - 1;
33318c2ecf20Sopenharmony_ci		atomic_set(&qedf->num_offloads, 0);
33328c2ecf20Sopenharmony_ci		qedf->stop_io_on_error = false;
33338c2ecf20Sopenharmony_ci		pci_set_drvdata(pdev, qedf);
33348c2ecf20Sopenharmony_ci		init_completion(&qedf->fipvlan_compl);
33358c2ecf20Sopenharmony_ci		mutex_init(&qedf->stats_mutex);
33368c2ecf20Sopenharmony_ci		mutex_init(&qedf->flush_mutex);
33378c2ecf20Sopenharmony_ci		qedf->flogi_pending = 0;
33388c2ecf20Sopenharmony_ci
33398c2ecf20Sopenharmony_ci		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_INFO,
33408c2ecf20Sopenharmony_ci		   "QLogic FastLinQ FCoE Module qedf %s, "
33418c2ecf20Sopenharmony_ci		   "FW %d.%d.%d.%d\n", QEDF_VERSION,
33428c2ecf20Sopenharmony_ci		   FW_MAJOR_VERSION, FW_MINOR_VERSION, FW_REVISION_VERSION,
33438c2ecf20Sopenharmony_ci		   FW_ENGINEERING_VERSION);
33448c2ecf20Sopenharmony_ci	} else {
33458c2ecf20Sopenharmony_ci		/* Init pointers during recovery */
33468c2ecf20Sopenharmony_ci		qedf = pci_get_drvdata(pdev);
33478c2ecf20Sopenharmony_ci		set_bit(QEDF_PROBING, &qedf->flags);
33488c2ecf20Sopenharmony_ci		lport = qedf->lport;
33498c2ecf20Sopenharmony_ci	}
33508c2ecf20Sopenharmony_ci
33518c2ecf20Sopenharmony_ci	QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, "Probe started.\n");
33528c2ecf20Sopenharmony_ci
33538c2ecf20Sopenharmony_ci	host = lport->host;
33548c2ecf20Sopenharmony_ci
33558c2ecf20Sopenharmony_ci	/* Allocate mempool for qedf_io_work structs */
33568c2ecf20Sopenharmony_ci	qedf->io_mempool = mempool_create_slab_pool(QEDF_IO_WORK_MIN,
33578c2ecf20Sopenharmony_ci	    qedf_io_work_cache);
33588c2ecf20Sopenharmony_ci	if (qedf->io_mempool == NULL) {
33598c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "qedf->io_mempool is NULL.\n");
33608c2ecf20Sopenharmony_ci		goto err1;
33618c2ecf20Sopenharmony_ci	}
33628c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_INFO, "qedf->io_mempool=%p.\n",
33638c2ecf20Sopenharmony_ci	    qedf->io_mempool);
33648c2ecf20Sopenharmony_ci
33658c2ecf20Sopenharmony_ci	sprintf(host_buf, "qedf_%u_link",
33668c2ecf20Sopenharmony_ci	    qedf->lport->host->host_no);
33678c2ecf20Sopenharmony_ci	qedf->link_update_wq = create_workqueue(host_buf);
33688c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&qedf->link_update, qedf_handle_link_update);
33698c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&qedf->link_recovery, qedf_link_recovery);
33708c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&qedf->grcdump_work, qedf_wq_grcdump);
33718c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&qedf->stag_work, qedf_stag_change_work);
33728c2ecf20Sopenharmony_ci	qedf->fipvlan_retries = qedf_fipvlan_retries;
33738c2ecf20Sopenharmony_ci	/* Set a default prio in case DCBX doesn't converge */
33748c2ecf20Sopenharmony_ci	if (qedf_default_prio > -1) {
33758c2ecf20Sopenharmony_ci		/*
33768c2ecf20Sopenharmony_ci		 * This is the case where we pass a modparam in so we want to
33778c2ecf20Sopenharmony_ci		 * honor it even if dcbx doesn't converge.
33788c2ecf20Sopenharmony_ci		 */
33798c2ecf20Sopenharmony_ci		qedf->prio = qedf_default_prio;
33808c2ecf20Sopenharmony_ci	} else
33818c2ecf20Sopenharmony_ci		qedf->prio = QEDF_DEFAULT_PRIO;
33828c2ecf20Sopenharmony_ci
33838c2ecf20Sopenharmony_ci	/*
33848c2ecf20Sopenharmony_ci	 * Common probe. Takes care of basic hardware init and pci_*
33858c2ecf20Sopenharmony_ci	 * functions.
33868c2ecf20Sopenharmony_ci	 */
33878c2ecf20Sopenharmony_ci	memset(&qed_params, 0, sizeof(qed_params));
33888c2ecf20Sopenharmony_ci	qed_params.protocol = QED_PROTOCOL_FCOE;
33898c2ecf20Sopenharmony_ci	qed_params.dp_module = qedf_dp_module;
33908c2ecf20Sopenharmony_ci	qed_params.dp_level = qedf_dp_level;
33918c2ecf20Sopenharmony_ci	qed_params.is_vf = is_vf;
33928c2ecf20Sopenharmony_ci	qedf->cdev = qed_ops->common->probe(pdev, &qed_params);
33938c2ecf20Sopenharmony_ci	if (!qedf->cdev) {
33948c2ecf20Sopenharmony_ci		if ((mode == QEDF_MODE_RECOVERY) && retry_cnt) {
33958c2ecf20Sopenharmony_ci			QEDF_ERR(&qedf->dbg_ctx,
33968c2ecf20Sopenharmony_ci				"Retry %d initialize hardware\n", retry_cnt);
33978c2ecf20Sopenharmony_ci			retry_cnt--;
33988c2ecf20Sopenharmony_ci			goto retry_probe;
33998c2ecf20Sopenharmony_ci		}
34008c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx, "common probe failed.\n");
34018c2ecf20Sopenharmony_ci		rc = -ENODEV;
34028c2ecf20Sopenharmony_ci		goto err1;
34038c2ecf20Sopenharmony_ci	}
34048c2ecf20Sopenharmony_ci
34058c2ecf20Sopenharmony_ci	/* Learn information crucial for qedf to progress */
34068c2ecf20Sopenharmony_ci	rc = qed_ops->fill_dev_info(qedf->cdev, &qedf->dev_info);
34078c2ecf20Sopenharmony_ci	if (rc) {
34088c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "Failed to dev info.\n");
34098c2ecf20Sopenharmony_ci		goto err1;
34108c2ecf20Sopenharmony_ci	}
34118c2ecf20Sopenharmony_ci
34128c2ecf20Sopenharmony_ci	QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
34138c2ecf20Sopenharmony_ci		  "dev_info: num_hwfns=%d affin_hwfn_idx=%d.\n",
34148c2ecf20Sopenharmony_ci		  qedf->dev_info.common.num_hwfns,
34158c2ecf20Sopenharmony_ci		  qed_ops->common->get_affin_hwfn_idx(qedf->cdev));
34168c2ecf20Sopenharmony_ci
34178c2ecf20Sopenharmony_ci	/* queue allocation code should come here
34188c2ecf20Sopenharmony_ci	 * order should be
34198c2ecf20Sopenharmony_ci	 * 	slowpath_start
34208c2ecf20Sopenharmony_ci	 * 	status block allocation
34218c2ecf20Sopenharmony_ci	 *	interrupt registration (to get min number of queues)
34228c2ecf20Sopenharmony_ci	 *	set_fcoe_pf_param
34238c2ecf20Sopenharmony_ci	 *	qed_sp_fcoe_func_start
34248c2ecf20Sopenharmony_ci	 */
34258c2ecf20Sopenharmony_ci	rc = qedf_set_fcoe_pf_param(qedf);
34268c2ecf20Sopenharmony_ci	if (rc) {
34278c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "Cannot set fcoe pf param.\n");
34288c2ecf20Sopenharmony_ci		goto err2;
34298c2ecf20Sopenharmony_ci	}
34308c2ecf20Sopenharmony_ci	qed_ops->common->update_pf_params(qedf->cdev, &qedf->pf_params);
34318c2ecf20Sopenharmony_ci
34328c2ecf20Sopenharmony_ci	/* Learn information crucial for qedf to progress */
34338c2ecf20Sopenharmony_ci	rc = qed_ops->fill_dev_info(qedf->cdev, &qedf->dev_info);
34348c2ecf20Sopenharmony_ci	if (rc) {
34358c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx, "Failed to fill dev info.\n");
34368c2ecf20Sopenharmony_ci		goto err2;
34378c2ecf20Sopenharmony_ci	}
34388c2ecf20Sopenharmony_ci
34398c2ecf20Sopenharmony_ci	/* Record BDQ producer doorbell addresses */
34408c2ecf20Sopenharmony_ci	qedf->bdq_primary_prod = qedf->dev_info.primary_dbq_rq_addr;
34418c2ecf20Sopenharmony_ci	qedf->bdq_secondary_prod = qedf->dev_info.secondary_bdq_rq_addr;
34428c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
34438c2ecf20Sopenharmony_ci	    "BDQ primary_prod=%p secondary_prod=%p.\n", qedf->bdq_primary_prod,
34448c2ecf20Sopenharmony_ci	    qedf->bdq_secondary_prod);
34458c2ecf20Sopenharmony_ci
34468c2ecf20Sopenharmony_ci	qed_ops->register_ops(qedf->cdev, &qedf_cb_ops, qedf);
34478c2ecf20Sopenharmony_ci
34488c2ecf20Sopenharmony_ci	rc = qedf_prepare_sb(qedf);
34498c2ecf20Sopenharmony_ci	if (rc) {
34508c2ecf20Sopenharmony_ci
34518c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "Cannot start slowpath.\n");
34528c2ecf20Sopenharmony_ci		goto err2;
34538c2ecf20Sopenharmony_ci	}
34548c2ecf20Sopenharmony_ci
34558c2ecf20Sopenharmony_ci	/* Start the Slowpath-process */
34568c2ecf20Sopenharmony_ci	slowpath_params.int_mode = QED_INT_MODE_MSIX;
34578c2ecf20Sopenharmony_ci	slowpath_params.drv_major = QEDF_DRIVER_MAJOR_VER;
34588c2ecf20Sopenharmony_ci	slowpath_params.drv_minor = QEDF_DRIVER_MINOR_VER;
34598c2ecf20Sopenharmony_ci	slowpath_params.drv_rev = QEDF_DRIVER_REV_VER;
34608c2ecf20Sopenharmony_ci	slowpath_params.drv_eng = QEDF_DRIVER_ENG_VER;
34618c2ecf20Sopenharmony_ci	strncpy(slowpath_params.name, "qedf", QED_DRV_VER_STR_SIZE);
34628c2ecf20Sopenharmony_ci	rc = qed_ops->common->slowpath_start(qedf->cdev, &slowpath_params);
34638c2ecf20Sopenharmony_ci	if (rc) {
34648c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "Cannot start slowpath.\n");
34658c2ecf20Sopenharmony_ci		goto err2;
34668c2ecf20Sopenharmony_ci	}
34678c2ecf20Sopenharmony_ci
34688c2ecf20Sopenharmony_ci	/*
34698c2ecf20Sopenharmony_ci	 * update_pf_params needs to be called before and after slowpath
34708c2ecf20Sopenharmony_ci	 * start
34718c2ecf20Sopenharmony_ci	 */
34728c2ecf20Sopenharmony_ci	qed_ops->common->update_pf_params(qedf->cdev, &qedf->pf_params);
34738c2ecf20Sopenharmony_ci
34748c2ecf20Sopenharmony_ci	/* Setup interrupts */
34758c2ecf20Sopenharmony_ci	rc = qedf_setup_int(qedf);
34768c2ecf20Sopenharmony_ci	if (rc) {
34778c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx, "Setup interrupts failed.\n");
34788c2ecf20Sopenharmony_ci		goto err3;
34798c2ecf20Sopenharmony_ci	}
34808c2ecf20Sopenharmony_ci
34818c2ecf20Sopenharmony_ci	rc = qed_ops->start(qedf->cdev, &qedf->tasks);
34828c2ecf20Sopenharmony_ci	if (rc) {
34838c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "Cannot start FCoE function.\n");
34848c2ecf20Sopenharmony_ci		goto err4;
34858c2ecf20Sopenharmony_ci	}
34868c2ecf20Sopenharmony_ci	task_start = qedf_get_task_mem(&qedf->tasks, 0);
34878c2ecf20Sopenharmony_ci	task_end = qedf_get_task_mem(&qedf->tasks, MAX_TID_BLOCKS_FCOE - 1);
34888c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Task context start=%p, "
34898c2ecf20Sopenharmony_ci		   "end=%p block_size=%u.\n", task_start, task_end,
34908c2ecf20Sopenharmony_ci		   qedf->tasks.size);
34918c2ecf20Sopenharmony_ci
34928c2ecf20Sopenharmony_ci	/*
34938c2ecf20Sopenharmony_ci	 * We need to write the number of BDs in the BDQ we've preallocated so
34948c2ecf20Sopenharmony_ci	 * the f/w will do a prefetch and we'll get an unsolicited CQE when a
34958c2ecf20Sopenharmony_ci	 * packet arrives.
34968c2ecf20Sopenharmony_ci	 */
34978c2ecf20Sopenharmony_ci	qedf->bdq_prod_idx = QEDF_BDQ_SIZE;
34988c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
34998c2ecf20Sopenharmony_ci	    "Writing %d to primary and secondary BDQ doorbell registers.\n",
35008c2ecf20Sopenharmony_ci	    qedf->bdq_prod_idx);
35018c2ecf20Sopenharmony_ci	writew(qedf->bdq_prod_idx, qedf->bdq_primary_prod);
35028c2ecf20Sopenharmony_ci	readw(qedf->bdq_primary_prod);
35038c2ecf20Sopenharmony_ci	writew(qedf->bdq_prod_idx, qedf->bdq_secondary_prod);
35048c2ecf20Sopenharmony_ci	readw(qedf->bdq_secondary_prod);
35058c2ecf20Sopenharmony_ci
35068c2ecf20Sopenharmony_ci	qed_ops->common->set_power_state(qedf->cdev, PCI_D0);
35078c2ecf20Sopenharmony_ci
35088c2ecf20Sopenharmony_ci	/* Now that the dev_info struct has been filled in set the MAC
35098c2ecf20Sopenharmony_ci	 * address
35108c2ecf20Sopenharmony_ci	 */
35118c2ecf20Sopenharmony_ci	ether_addr_copy(qedf->mac, qedf->dev_info.common.hw_mac);
35128c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "MAC address is %pM.\n",
35138c2ecf20Sopenharmony_ci		   qedf->mac);
35148c2ecf20Sopenharmony_ci
35158c2ecf20Sopenharmony_ci	/*
35168c2ecf20Sopenharmony_ci	 * Set the WWNN and WWPN in the following way:
35178c2ecf20Sopenharmony_ci	 *
35188c2ecf20Sopenharmony_ci	 * If the info we get from qed is non-zero then use that to set the
35198c2ecf20Sopenharmony_ci	 * WWPN and WWNN. Otherwise fall back to use fcoe_wwn_from_mac() based
35208c2ecf20Sopenharmony_ci	 * on the MAC address.
35218c2ecf20Sopenharmony_ci	 */
35228c2ecf20Sopenharmony_ci	if (qedf->dev_info.wwnn != 0 && qedf->dev_info.wwpn != 0) {
35238c2ecf20Sopenharmony_ci		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
35248c2ecf20Sopenharmony_ci		    "Setting WWPN and WWNN from qed dev_info.\n");
35258c2ecf20Sopenharmony_ci		qedf->wwnn = qedf->dev_info.wwnn;
35268c2ecf20Sopenharmony_ci		qedf->wwpn = qedf->dev_info.wwpn;
35278c2ecf20Sopenharmony_ci	} else {
35288c2ecf20Sopenharmony_ci		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
35298c2ecf20Sopenharmony_ci		    "Setting WWPN and WWNN using fcoe_wwn_from_mac().\n");
35308c2ecf20Sopenharmony_ci		qedf->wwnn = fcoe_wwn_from_mac(qedf->mac, 1, 0);
35318c2ecf20Sopenharmony_ci		qedf->wwpn = fcoe_wwn_from_mac(qedf->mac, 2, 0);
35328c2ecf20Sopenharmony_ci	}
35338c2ecf20Sopenharmony_ci	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,  "WWNN=%016llx "
35348c2ecf20Sopenharmony_ci		   "WWPN=%016llx.\n", qedf->wwnn, qedf->wwpn);
35358c2ecf20Sopenharmony_ci
35368c2ecf20Sopenharmony_ci	sprintf(host_buf, "host_%d", host->host_no);
35378c2ecf20Sopenharmony_ci	qed_ops->common->set_name(qedf->cdev, host_buf);
35388c2ecf20Sopenharmony_ci
35398c2ecf20Sopenharmony_ci	/* Allocate cmd mgr */
35408c2ecf20Sopenharmony_ci	qedf->cmd_mgr = qedf_cmd_mgr_alloc(qedf);
35418c2ecf20Sopenharmony_ci	if (!qedf->cmd_mgr) {
35428c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "Failed to allocate cmd mgr.\n");
35438c2ecf20Sopenharmony_ci		rc = -ENOMEM;
35448c2ecf20Sopenharmony_ci		goto err5;
35458c2ecf20Sopenharmony_ci	}
35468c2ecf20Sopenharmony_ci
35478c2ecf20Sopenharmony_ci	if (mode != QEDF_MODE_RECOVERY) {
35488c2ecf20Sopenharmony_ci		host->transportt = qedf_fc_transport_template;
35498c2ecf20Sopenharmony_ci		host->max_lun = qedf_max_lun;
35508c2ecf20Sopenharmony_ci		host->max_cmd_len = QEDF_MAX_CDB_LEN;
35518c2ecf20Sopenharmony_ci		host->can_queue = FCOE_PARAMS_NUM_TASKS;
35528c2ecf20Sopenharmony_ci		rc = scsi_add_host(host, &pdev->dev);
35538c2ecf20Sopenharmony_ci		if (rc) {
35548c2ecf20Sopenharmony_ci			QEDF_WARN(&qedf->dbg_ctx,
35558c2ecf20Sopenharmony_ci				  "Error adding Scsi_Host rc=0x%x.\n", rc);
35568c2ecf20Sopenharmony_ci			goto err6;
35578c2ecf20Sopenharmony_ci		}
35588c2ecf20Sopenharmony_ci	}
35598c2ecf20Sopenharmony_ci
35608c2ecf20Sopenharmony_ci	memset(&params, 0, sizeof(params));
35618c2ecf20Sopenharmony_ci	params.mtu = QEDF_LL2_BUF_SIZE;
35628c2ecf20Sopenharmony_ci	ether_addr_copy(params.ll2_mac_address, qedf->mac);
35638c2ecf20Sopenharmony_ci
35648c2ecf20Sopenharmony_ci	/* Start LL2 processing thread */
35658c2ecf20Sopenharmony_ci	snprintf(host_buf, 20, "qedf_%d_ll2", host->host_no);
35668c2ecf20Sopenharmony_ci	qedf->ll2_recv_wq =
35678c2ecf20Sopenharmony_ci		create_workqueue(host_buf);
35688c2ecf20Sopenharmony_ci	if (!qedf->ll2_recv_wq) {
35698c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "Failed to LL2 workqueue.\n");
35708c2ecf20Sopenharmony_ci		rc = -ENOMEM;
35718c2ecf20Sopenharmony_ci		goto err7;
35728c2ecf20Sopenharmony_ci	}
35738c2ecf20Sopenharmony_ci
35748c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
35758c2ecf20Sopenharmony_ci	qedf_dbg_host_init(&(qedf->dbg_ctx), qedf_debugfs_ops,
35768c2ecf20Sopenharmony_ci			    qedf_dbg_fops);
35778c2ecf20Sopenharmony_ci#endif
35788c2ecf20Sopenharmony_ci
35798c2ecf20Sopenharmony_ci	/* Start LL2 */
35808c2ecf20Sopenharmony_ci	qed_ops->ll2->register_cb_ops(qedf->cdev, &qedf_ll2_cb_ops, qedf);
35818c2ecf20Sopenharmony_ci	rc = qed_ops->ll2->start(qedf->cdev, &params);
35828c2ecf20Sopenharmony_ci	if (rc) {
35838c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "Could not start Light L2.\n");
35848c2ecf20Sopenharmony_ci		goto err7;
35858c2ecf20Sopenharmony_ci	}
35868c2ecf20Sopenharmony_ci	set_bit(QEDF_LL2_STARTED, &qedf->flags);
35878c2ecf20Sopenharmony_ci
35888c2ecf20Sopenharmony_ci	/* Set initial FIP/FCoE VLAN to NULL */
35898c2ecf20Sopenharmony_ci	qedf->vlan_id = 0;
35908c2ecf20Sopenharmony_ci
35918c2ecf20Sopenharmony_ci	/*
35928c2ecf20Sopenharmony_ci	 * No need to setup fcoe_ctlr or fc_lport objects during recovery since
35938c2ecf20Sopenharmony_ci	 * they were not reaped during the unload process.
35948c2ecf20Sopenharmony_ci	 */
35958c2ecf20Sopenharmony_ci	if (mode != QEDF_MODE_RECOVERY) {
35968c2ecf20Sopenharmony_ci		/* Setup imbedded fcoe controller */
35978c2ecf20Sopenharmony_ci		qedf_fcoe_ctlr_setup(qedf);
35988c2ecf20Sopenharmony_ci
35998c2ecf20Sopenharmony_ci		/* Setup lport */
36008c2ecf20Sopenharmony_ci		rc = qedf_lport_setup(qedf);
36018c2ecf20Sopenharmony_ci		if (rc) {
36028c2ecf20Sopenharmony_ci			QEDF_ERR(&(qedf->dbg_ctx),
36038c2ecf20Sopenharmony_ci			    "qedf_lport_setup failed.\n");
36048c2ecf20Sopenharmony_ci			goto err7;
36058c2ecf20Sopenharmony_ci		}
36068c2ecf20Sopenharmony_ci	}
36078c2ecf20Sopenharmony_ci
36088c2ecf20Sopenharmony_ci	sprintf(host_buf, "qedf_%u_timer", qedf->lport->host->host_no);
36098c2ecf20Sopenharmony_ci	qedf->timer_work_queue =
36108c2ecf20Sopenharmony_ci		create_workqueue(host_buf);
36118c2ecf20Sopenharmony_ci	if (!qedf->timer_work_queue) {
36128c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx), "Failed to start timer "
36138c2ecf20Sopenharmony_ci			  "workqueue.\n");
36148c2ecf20Sopenharmony_ci		rc = -ENOMEM;
36158c2ecf20Sopenharmony_ci		goto err7;
36168c2ecf20Sopenharmony_ci	}
36178c2ecf20Sopenharmony_ci
36188c2ecf20Sopenharmony_ci	/* DPC workqueue is not reaped during recovery unload */
36198c2ecf20Sopenharmony_ci	if (mode != QEDF_MODE_RECOVERY) {
36208c2ecf20Sopenharmony_ci		sprintf(host_buf, "qedf_%u_dpc",
36218c2ecf20Sopenharmony_ci		    qedf->lport->host->host_no);
36228c2ecf20Sopenharmony_ci		qedf->dpc_wq = create_workqueue(host_buf);
36238c2ecf20Sopenharmony_ci	}
36248c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&qedf->recovery_work, qedf_recovery_handler);
36258c2ecf20Sopenharmony_ci
36268c2ecf20Sopenharmony_ci	/*
36278c2ecf20Sopenharmony_ci	 * GRC dump and sysfs parameters are not reaped during the recovery
36288c2ecf20Sopenharmony_ci	 * unload process.
36298c2ecf20Sopenharmony_ci	 */
36308c2ecf20Sopenharmony_ci	if (mode != QEDF_MODE_RECOVERY) {
36318c2ecf20Sopenharmony_ci		qedf->grcdump_size =
36328c2ecf20Sopenharmony_ci		    qed_ops->common->dbg_all_data_size(qedf->cdev);
36338c2ecf20Sopenharmony_ci		if (qedf->grcdump_size) {
36348c2ecf20Sopenharmony_ci			rc = qedf_alloc_grc_dump_buf(&qedf->grcdump,
36358c2ecf20Sopenharmony_ci			    qedf->grcdump_size);
36368c2ecf20Sopenharmony_ci			if (rc) {
36378c2ecf20Sopenharmony_ci				QEDF_ERR(&(qedf->dbg_ctx),
36388c2ecf20Sopenharmony_ci				    "GRC Dump buffer alloc failed.\n");
36398c2ecf20Sopenharmony_ci				qedf->grcdump = NULL;
36408c2ecf20Sopenharmony_ci			}
36418c2ecf20Sopenharmony_ci
36428c2ecf20Sopenharmony_ci			QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
36438c2ecf20Sopenharmony_ci			    "grcdump: addr=%p, size=%u.\n",
36448c2ecf20Sopenharmony_ci			    qedf->grcdump, qedf->grcdump_size);
36458c2ecf20Sopenharmony_ci		}
36468c2ecf20Sopenharmony_ci		qedf_create_sysfs_ctx_attr(qedf);
36478c2ecf20Sopenharmony_ci
36488c2ecf20Sopenharmony_ci		/* Initialize I/O tracing for this adapter */
36498c2ecf20Sopenharmony_ci		spin_lock_init(&qedf->io_trace_lock);
36508c2ecf20Sopenharmony_ci		qedf->io_trace_idx = 0;
36518c2ecf20Sopenharmony_ci	}
36528c2ecf20Sopenharmony_ci
36538c2ecf20Sopenharmony_ci	init_completion(&qedf->flogi_compl);
36548c2ecf20Sopenharmony_ci
36558c2ecf20Sopenharmony_ci	status = qed_ops->common->update_drv_state(qedf->cdev, true);
36568c2ecf20Sopenharmony_ci	if (status)
36578c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx),
36588c2ecf20Sopenharmony_ci			"Failed to send drv state to MFW.\n");
36598c2ecf20Sopenharmony_ci
36608c2ecf20Sopenharmony_ci	memset(&link_params, 0, sizeof(struct qed_link_params));
36618c2ecf20Sopenharmony_ci	link_params.link_up = true;
36628c2ecf20Sopenharmony_ci	status = qed_ops->common->set_link(qedf->cdev, &link_params);
36638c2ecf20Sopenharmony_ci	if (status)
36648c2ecf20Sopenharmony_ci		QEDF_WARN(&(qedf->dbg_ctx), "set_link failed.\n");
36658c2ecf20Sopenharmony_ci
36668c2ecf20Sopenharmony_ci	/* Start/restart discovery */
36678c2ecf20Sopenharmony_ci	if (mode == QEDF_MODE_RECOVERY)
36688c2ecf20Sopenharmony_ci		fcoe_ctlr_link_up(&qedf->ctlr);
36698c2ecf20Sopenharmony_ci	else
36708c2ecf20Sopenharmony_ci		fc_fabric_login(lport);
36718c2ecf20Sopenharmony_ci
36728c2ecf20Sopenharmony_ci	QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, "Probe done.\n");
36738c2ecf20Sopenharmony_ci
36748c2ecf20Sopenharmony_ci	clear_bit(QEDF_PROBING, &qedf->flags);
36758c2ecf20Sopenharmony_ci
36768c2ecf20Sopenharmony_ci	/* All good */
36778c2ecf20Sopenharmony_ci	return 0;
36788c2ecf20Sopenharmony_ci
36798c2ecf20Sopenharmony_cierr7:
36808c2ecf20Sopenharmony_ci	if (qedf->ll2_recv_wq)
36818c2ecf20Sopenharmony_ci		destroy_workqueue(qedf->ll2_recv_wq);
36828c2ecf20Sopenharmony_ci	fc_remove_host(qedf->lport->host);
36838c2ecf20Sopenharmony_ci	scsi_remove_host(qedf->lport->host);
36848c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
36858c2ecf20Sopenharmony_ci	qedf_dbg_host_exit(&(qedf->dbg_ctx));
36868c2ecf20Sopenharmony_ci#endif
36878c2ecf20Sopenharmony_cierr6:
36888c2ecf20Sopenharmony_ci	qedf_cmd_mgr_free(qedf->cmd_mgr);
36898c2ecf20Sopenharmony_cierr5:
36908c2ecf20Sopenharmony_ci	qed_ops->stop(qedf->cdev);
36918c2ecf20Sopenharmony_cierr4:
36928c2ecf20Sopenharmony_ci	qedf_free_fcoe_pf_param(qedf);
36938c2ecf20Sopenharmony_ci	qedf_sync_free_irqs(qedf);
36948c2ecf20Sopenharmony_cierr3:
36958c2ecf20Sopenharmony_ci	qed_ops->common->slowpath_stop(qedf->cdev);
36968c2ecf20Sopenharmony_cierr2:
36978c2ecf20Sopenharmony_ci	qed_ops->common->remove(qedf->cdev);
36988c2ecf20Sopenharmony_cierr1:
36998c2ecf20Sopenharmony_ci	scsi_host_put(lport->host);
37008c2ecf20Sopenharmony_cierr0:
37018c2ecf20Sopenharmony_ci	return rc;
37028c2ecf20Sopenharmony_ci}
37038c2ecf20Sopenharmony_ci
37048c2ecf20Sopenharmony_cistatic int qedf_probe(struct pci_dev *pdev, const struct pci_device_id *id)
37058c2ecf20Sopenharmony_ci{
37068c2ecf20Sopenharmony_ci	return __qedf_probe(pdev, QEDF_MODE_NORMAL);
37078c2ecf20Sopenharmony_ci}
37088c2ecf20Sopenharmony_ci
37098c2ecf20Sopenharmony_cistatic void __qedf_remove(struct pci_dev *pdev, int mode)
37108c2ecf20Sopenharmony_ci{
37118c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf;
37128c2ecf20Sopenharmony_ci	int rc;
37138c2ecf20Sopenharmony_ci
37148c2ecf20Sopenharmony_ci	if (!pdev) {
37158c2ecf20Sopenharmony_ci		QEDF_ERR(NULL, "pdev is NULL.\n");
37168c2ecf20Sopenharmony_ci		return;
37178c2ecf20Sopenharmony_ci	}
37188c2ecf20Sopenharmony_ci
37198c2ecf20Sopenharmony_ci	qedf = pci_get_drvdata(pdev);
37208c2ecf20Sopenharmony_ci
37218c2ecf20Sopenharmony_ci	/*
37228c2ecf20Sopenharmony_ci	 * Prevent race where we're in board disable work and then try to
37238c2ecf20Sopenharmony_ci	 * rmmod the module.
37248c2ecf20Sopenharmony_ci	 */
37258c2ecf20Sopenharmony_ci	if (test_bit(QEDF_UNLOADING, &qedf->flags)) {
37268c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx, "Already removing PCI function.\n");
37278c2ecf20Sopenharmony_ci		return;
37288c2ecf20Sopenharmony_ci	}
37298c2ecf20Sopenharmony_ci
37308c2ecf20Sopenharmony_ci	if (mode != QEDF_MODE_RECOVERY)
37318c2ecf20Sopenharmony_ci		set_bit(QEDF_UNLOADING, &qedf->flags);
37328c2ecf20Sopenharmony_ci
37338c2ecf20Sopenharmony_ci	/* Logoff the fabric to upload all connections */
37348c2ecf20Sopenharmony_ci	if (mode == QEDF_MODE_RECOVERY)
37358c2ecf20Sopenharmony_ci		fcoe_ctlr_link_down(&qedf->ctlr);
37368c2ecf20Sopenharmony_ci	else
37378c2ecf20Sopenharmony_ci		fc_fabric_logoff(qedf->lport);
37388c2ecf20Sopenharmony_ci
37398c2ecf20Sopenharmony_ci	if (qedf_wait_for_upload(qedf) == false)
37408c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx, "Could not upload all sessions.\n");
37418c2ecf20Sopenharmony_ci
37428c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
37438c2ecf20Sopenharmony_ci	qedf_dbg_host_exit(&(qedf->dbg_ctx));
37448c2ecf20Sopenharmony_ci#endif
37458c2ecf20Sopenharmony_ci
37468c2ecf20Sopenharmony_ci	/* Stop any link update handling */
37478c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&qedf->link_update);
37488c2ecf20Sopenharmony_ci	destroy_workqueue(qedf->link_update_wq);
37498c2ecf20Sopenharmony_ci	qedf->link_update_wq = NULL;
37508c2ecf20Sopenharmony_ci
37518c2ecf20Sopenharmony_ci	if (qedf->timer_work_queue)
37528c2ecf20Sopenharmony_ci		destroy_workqueue(qedf->timer_work_queue);
37538c2ecf20Sopenharmony_ci
37548c2ecf20Sopenharmony_ci	/* Stop Light L2 */
37558c2ecf20Sopenharmony_ci	clear_bit(QEDF_LL2_STARTED, &qedf->flags);
37568c2ecf20Sopenharmony_ci	qed_ops->ll2->stop(qedf->cdev);
37578c2ecf20Sopenharmony_ci	if (qedf->ll2_recv_wq)
37588c2ecf20Sopenharmony_ci		destroy_workqueue(qedf->ll2_recv_wq);
37598c2ecf20Sopenharmony_ci
37608c2ecf20Sopenharmony_ci	/* Stop fastpath */
37618c2ecf20Sopenharmony_ci	qedf_sync_free_irqs(qedf);
37628c2ecf20Sopenharmony_ci	qedf_destroy_sb(qedf);
37638c2ecf20Sopenharmony_ci
37648c2ecf20Sopenharmony_ci	/*
37658c2ecf20Sopenharmony_ci	 * During recovery don't destroy OS constructs that represent the
37668c2ecf20Sopenharmony_ci	 * physical port.
37678c2ecf20Sopenharmony_ci	 */
37688c2ecf20Sopenharmony_ci	if (mode != QEDF_MODE_RECOVERY) {
37698c2ecf20Sopenharmony_ci		qedf_free_grc_dump_buf(&qedf->grcdump);
37708c2ecf20Sopenharmony_ci		qedf_remove_sysfs_ctx_attr(qedf);
37718c2ecf20Sopenharmony_ci
37728c2ecf20Sopenharmony_ci		/* Remove all SCSI/libfc/libfcoe structures */
37738c2ecf20Sopenharmony_ci		fcoe_ctlr_destroy(&qedf->ctlr);
37748c2ecf20Sopenharmony_ci		fc_lport_destroy(qedf->lport);
37758c2ecf20Sopenharmony_ci		fc_remove_host(qedf->lport->host);
37768c2ecf20Sopenharmony_ci		scsi_remove_host(qedf->lport->host);
37778c2ecf20Sopenharmony_ci	}
37788c2ecf20Sopenharmony_ci
37798c2ecf20Sopenharmony_ci	qedf_cmd_mgr_free(qedf->cmd_mgr);
37808c2ecf20Sopenharmony_ci
37818c2ecf20Sopenharmony_ci	if (mode != QEDF_MODE_RECOVERY) {
37828c2ecf20Sopenharmony_ci		fc_exch_mgr_free(qedf->lport);
37838c2ecf20Sopenharmony_ci		fc_lport_free_stats(qedf->lport);
37848c2ecf20Sopenharmony_ci
37858c2ecf20Sopenharmony_ci		/* Wait for all vports to be reaped */
37868c2ecf20Sopenharmony_ci		qedf_wait_for_vport_destroy(qedf);
37878c2ecf20Sopenharmony_ci	}
37888c2ecf20Sopenharmony_ci
37898c2ecf20Sopenharmony_ci	/*
37908c2ecf20Sopenharmony_ci	 * Now that all connections have been uploaded we can stop the
37918c2ecf20Sopenharmony_ci	 * rest of the qed operations
37928c2ecf20Sopenharmony_ci	 */
37938c2ecf20Sopenharmony_ci	qed_ops->stop(qedf->cdev);
37948c2ecf20Sopenharmony_ci
37958c2ecf20Sopenharmony_ci	if (mode != QEDF_MODE_RECOVERY) {
37968c2ecf20Sopenharmony_ci		if (qedf->dpc_wq) {
37978c2ecf20Sopenharmony_ci			/* Stop general DPC handling */
37988c2ecf20Sopenharmony_ci			destroy_workqueue(qedf->dpc_wq);
37998c2ecf20Sopenharmony_ci			qedf->dpc_wq = NULL;
38008c2ecf20Sopenharmony_ci		}
38018c2ecf20Sopenharmony_ci	}
38028c2ecf20Sopenharmony_ci
38038c2ecf20Sopenharmony_ci	/* Final shutdown for the board */
38048c2ecf20Sopenharmony_ci	qedf_free_fcoe_pf_param(qedf);
38058c2ecf20Sopenharmony_ci	if (mode != QEDF_MODE_RECOVERY) {
38068c2ecf20Sopenharmony_ci		qed_ops->common->set_power_state(qedf->cdev, PCI_D0);
38078c2ecf20Sopenharmony_ci		pci_set_drvdata(pdev, NULL);
38088c2ecf20Sopenharmony_ci	}
38098c2ecf20Sopenharmony_ci
38108c2ecf20Sopenharmony_ci	rc = qed_ops->common->update_drv_state(qedf->cdev, false);
38118c2ecf20Sopenharmony_ci	if (rc)
38128c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx),
38138c2ecf20Sopenharmony_ci			"Failed to send drv state to MFW.\n");
38148c2ecf20Sopenharmony_ci
38158c2ecf20Sopenharmony_ci	qed_ops->common->slowpath_stop(qedf->cdev);
38168c2ecf20Sopenharmony_ci	qed_ops->common->remove(qedf->cdev);
38178c2ecf20Sopenharmony_ci
38188c2ecf20Sopenharmony_ci	mempool_destroy(qedf->io_mempool);
38198c2ecf20Sopenharmony_ci
38208c2ecf20Sopenharmony_ci	/* Only reap the Scsi_host on a real removal */
38218c2ecf20Sopenharmony_ci	if (mode != QEDF_MODE_RECOVERY)
38228c2ecf20Sopenharmony_ci		scsi_host_put(qedf->lport->host);
38238c2ecf20Sopenharmony_ci}
38248c2ecf20Sopenharmony_ci
38258c2ecf20Sopenharmony_cistatic void qedf_remove(struct pci_dev *pdev)
38268c2ecf20Sopenharmony_ci{
38278c2ecf20Sopenharmony_ci	/* Check to make sure this function wasn't already disabled */
38288c2ecf20Sopenharmony_ci	if (!atomic_read(&pdev->enable_cnt))
38298c2ecf20Sopenharmony_ci		return;
38308c2ecf20Sopenharmony_ci
38318c2ecf20Sopenharmony_ci	__qedf_remove(pdev, QEDF_MODE_NORMAL);
38328c2ecf20Sopenharmony_ci}
38338c2ecf20Sopenharmony_ci
38348c2ecf20Sopenharmony_civoid qedf_wq_grcdump(struct work_struct *work)
38358c2ecf20Sopenharmony_ci{
38368c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf =
38378c2ecf20Sopenharmony_ci	    container_of(work, struct qedf_ctx, grcdump_work.work);
38388c2ecf20Sopenharmony_ci
38398c2ecf20Sopenharmony_ci	QEDF_ERR(&(qedf->dbg_ctx), "Collecting GRC dump.\n");
38408c2ecf20Sopenharmony_ci	qedf_capture_grc_dump(qedf);
38418c2ecf20Sopenharmony_ci}
38428c2ecf20Sopenharmony_ci
38438c2ecf20Sopenharmony_civoid qedf_schedule_hw_err_handler(void *dev, enum qed_hw_err_type err_type)
38448c2ecf20Sopenharmony_ci{
38458c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf = dev;
38468c2ecf20Sopenharmony_ci
38478c2ecf20Sopenharmony_ci	QEDF_ERR(&(qedf->dbg_ctx),
38488c2ecf20Sopenharmony_ci			"Hardware error handler scheduled, event=%d.\n",
38498c2ecf20Sopenharmony_ci			err_type);
38508c2ecf20Sopenharmony_ci
38518c2ecf20Sopenharmony_ci	if (test_bit(QEDF_IN_RECOVERY, &qedf->flags)) {
38528c2ecf20Sopenharmony_ci		QEDF_ERR(&(qedf->dbg_ctx),
38538c2ecf20Sopenharmony_ci				"Already in recovery, not scheduling board disable work.\n");
38548c2ecf20Sopenharmony_ci		return;
38558c2ecf20Sopenharmony_ci	}
38568c2ecf20Sopenharmony_ci
38578c2ecf20Sopenharmony_ci	switch (err_type) {
38588c2ecf20Sopenharmony_ci	case QED_HW_ERR_FAN_FAIL:
38598c2ecf20Sopenharmony_ci		schedule_delayed_work(&qedf->board_disable_work, 0);
38608c2ecf20Sopenharmony_ci		break;
38618c2ecf20Sopenharmony_ci	case QED_HW_ERR_MFW_RESP_FAIL:
38628c2ecf20Sopenharmony_ci	case QED_HW_ERR_HW_ATTN:
38638c2ecf20Sopenharmony_ci	case QED_HW_ERR_DMAE_FAIL:
38648c2ecf20Sopenharmony_ci	case QED_HW_ERR_FW_ASSERT:
38658c2ecf20Sopenharmony_ci		/* Prevent HW attentions from being reasserted */
38668c2ecf20Sopenharmony_ci		qed_ops->common->attn_clr_enable(qedf->cdev, true);
38678c2ecf20Sopenharmony_ci		break;
38688c2ecf20Sopenharmony_ci	case QED_HW_ERR_RAMROD_FAIL:
38698c2ecf20Sopenharmony_ci		/* Prevent HW attentions from being reasserted */
38708c2ecf20Sopenharmony_ci		qed_ops->common->attn_clr_enable(qedf->cdev, true);
38718c2ecf20Sopenharmony_ci
38728c2ecf20Sopenharmony_ci		if (qedf_enable_recovery)
38738c2ecf20Sopenharmony_ci			qed_ops->common->recovery_process(qedf->cdev);
38748c2ecf20Sopenharmony_ci
38758c2ecf20Sopenharmony_ci		break;
38768c2ecf20Sopenharmony_ci	default:
38778c2ecf20Sopenharmony_ci		break;
38788c2ecf20Sopenharmony_ci	}
38798c2ecf20Sopenharmony_ci}
38808c2ecf20Sopenharmony_ci
38818c2ecf20Sopenharmony_ci/*
38828c2ecf20Sopenharmony_ci * Protocol TLV handler
38838c2ecf20Sopenharmony_ci */
38848c2ecf20Sopenharmony_civoid qedf_get_protocol_tlv_data(void *dev, void *data)
38858c2ecf20Sopenharmony_ci{
38868c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf = dev;
38878c2ecf20Sopenharmony_ci	struct qed_mfw_tlv_fcoe *fcoe = data;
38888c2ecf20Sopenharmony_ci	struct fc_lport *lport;
38898c2ecf20Sopenharmony_ci	struct Scsi_Host *host;
38908c2ecf20Sopenharmony_ci	struct fc_host_attrs *fc_host;
38918c2ecf20Sopenharmony_ci	struct fc_host_statistics *hst;
38928c2ecf20Sopenharmony_ci
38938c2ecf20Sopenharmony_ci	if (!qedf) {
38948c2ecf20Sopenharmony_ci		QEDF_ERR(NULL, "qedf is null.\n");
38958c2ecf20Sopenharmony_ci		return;
38968c2ecf20Sopenharmony_ci	}
38978c2ecf20Sopenharmony_ci
38988c2ecf20Sopenharmony_ci	if (test_bit(QEDF_PROBING, &qedf->flags)) {
38998c2ecf20Sopenharmony_ci		QEDF_ERR(&qedf->dbg_ctx, "Function is still probing.\n");
39008c2ecf20Sopenharmony_ci		return;
39018c2ecf20Sopenharmony_ci	}
39028c2ecf20Sopenharmony_ci
39038c2ecf20Sopenharmony_ci	lport = qedf->lport;
39048c2ecf20Sopenharmony_ci	host = lport->host;
39058c2ecf20Sopenharmony_ci	fc_host = shost_to_fc_host(host);
39068c2ecf20Sopenharmony_ci
39078c2ecf20Sopenharmony_ci	/* Force a refresh of the fc_host stats including offload stats */
39088c2ecf20Sopenharmony_ci	hst = qedf_fc_get_host_stats(host);
39098c2ecf20Sopenharmony_ci
39108c2ecf20Sopenharmony_ci	fcoe->qos_pri_set = true;
39118c2ecf20Sopenharmony_ci	fcoe->qos_pri = 3; /* Hard coded to 3 in driver */
39128c2ecf20Sopenharmony_ci
39138c2ecf20Sopenharmony_ci	fcoe->ra_tov_set = true;
39148c2ecf20Sopenharmony_ci	fcoe->ra_tov = lport->r_a_tov;
39158c2ecf20Sopenharmony_ci
39168c2ecf20Sopenharmony_ci	fcoe->ed_tov_set = true;
39178c2ecf20Sopenharmony_ci	fcoe->ed_tov = lport->e_d_tov;
39188c2ecf20Sopenharmony_ci
39198c2ecf20Sopenharmony_ci	fcoe->npiv_state_set = true;
39208c2ecf20Sopenharmony_ci	fcoe->npiv_state = 1; /* NPIV always enabled */
39218c2ecf20Sopenharmony_ci
39228c2ecf20Sopenharmony_ci	fcoe->num_npiv_ids_set = true;
39238c2ecf20Sopenharmony_ci	fcoe->num_npiv_ids = fc_host->npiv_vports_inuse;
39248c2ecf20Sopenharmony_ci
39258c2ecf20Sopenharmony_ci	/* Certain attributes we only want to set if we've selected an FCF */
39268c2ecf20Sopenharmony_ci	if (qedf->ctlr.sel_fcf) {
39278c2ecf20Sopenharmony_ci		fcoe->switch_name_set = true;
39288c2ecf20Sopenharmony_ci		u64_to_wwn(qedf->ctlr.sel_fcf->switch_name, fcoe->switch_name);
39298c2ecf20Sopenharmony_ci	}
39308c2ecf20Sopenharmony_ci
39318c2ecf20Sopenharmony_ci	fcoe->port_state_set = true;
39328c2ecf20Sopenharmony_ci	/* For qedf we're either link down or fabric attach */
39338c2ecf20Sopenharmony_ci	if (lport->link_up)
39348c2ecf20Sopenharmony_ci		fcoe->port_state = QED_MFW_TLV_PORT_STATE_FABRIC;
39358c2ecf20Sopenharmony_ci	else
39368c2ecf20Sopenharmony_ci		fcoe->port_state = QED_MFW_TLV_PORT_STATE_OFFLINE;
39378c2ecf20Sopenharmony_ci
39388c2ecf20Sopenharmony_ci	fcoe->link_failures_set = true;
39398c2ecf20Sopenharmony_ci	fcoe->link_failures = (u16)hst->link_failure_count;
39408c2ecf20Sopenharmony_ci
39418c2ecf20Sopenharmony_ci	fcoe->fcoe_txq_depth_set = true;
39428c2ecf20Sopenharmony_ci	fcoe->fcoe_rxq_depth_set = true;
39438c2ecf20Sopenharmony_ci	fcoe->fcoe_rxq_depth = FCOE_PARAMS_NUM_TASKS;
39448c2ecf20Sopenharmony_ci	fcoe->fcoe_txq_depth = FCOE_PARAMS_NUM_TASKS;
39458c2ecf20Sopenharmony_ci
39468c2ecf20Sopenharmony_ci	fcoe->fcoe_rx_frames_set = true;
39478c2ecf20Sopenharmony_ci	fcoe->fcoe_rx_frames = hst->rx_frames;
39488c2ecf20Sopenharmony_ci
39498c2ecf20Sopenharmony_ci	fcoe->fcoe_tx_frames_set = true;
39508c2ecf20Sopenharmony_ci	fcoe->fcoe_tx_frames = hst->tx_frames;
39518c2ecf20Sopenharmony_ci
39528c2ecf20Sopenharmony_ci	fcoe->fcoe_rx_bytes_set = true;
39538c2ecf20Sopenharmony_ci	fcoe->fcoe_rx_bytes = hst->fcp_input_megabytes * 1000000;
39548c2ecf20Sopenharmony_ci
39558c2ecf20Sopenharmony_ci	fcoe->fcoe_tx_bytes_set = true;
39568c2ecf20Sopenharmony_ci	fcoe->fcoe_tx_bytes = hst->fcp_output_megabytes * 1000000;
39578c2ecf20Sopenharmony_ci
39588c2ecf20Sopenharmony_ci	fcoe->crc_count_set = true;
39598c2ecf20Sopenharmony_ci	fcoe->crc_count = hst->invalid_crc_count;
39608c2ecf20Sopenharmony_ci
39618c2ecf20Sopenharmony_ci	fcoe->tx_abts_set = true;
39628c2ecf20Sopenharmony_ci	fcoe->tx_abts = hst->fcp_packet_aborts;
39638c2ecf20Sopenharmony_ci
39648c2ecf20Sopenharmony_ci	fcoe->tx_lun_rst_set = true;
39658c2ecf20Sopenharmony_ci	fcoe->tx_lun_rst = qedf->lun_resets;
39668c2ecf20Sopenharmony_ci
39678c2ecf20Sopenharmony_ci	fcoe->abort_task_sets_set = true;
39688c2ecf20Sopenharmony_ci	fcoe->abort_task_sets = qedf->packet_aborts;
39698c2ecf20Sopenharmony_ci
39708c2ecf20Sopenharmony_ci	fcoe->scsi_busy_set = true;
39718c2ecf20Sopenharmony_ci	fcoe->scsi_busy = qedf->busy;
39728c2ecf20Sopenharmony_ci
39738c2ecf20Sopenharmony_ci	fcoe->scsi_tsk_full_set = true;
39748c2ecf20Sopenharmony_ci	fcoe->scsi_tsk_full = qedf->task_set_fulls;
39758c2ecf20Sopenharmony_ci}
39768c2ecf20Sopenharmony_ci
39778c2ecf20Sopenharmony_ci/* Deferred work function to perform soft context reset on STAG change */
39788c2ecf20Sopenharmony_civoid qedf_stag_change_work(struct work_struct *work)
39798c2ecf20Sopenharmony_ci{
39808c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf =
39818c2ecf20Sopenharmony_ci	    container_of(work, struct qedf_ctx, stag_work.work);
39828c2ecf20Sopenharmony_ci
39838c2ecf20Sopenharmony_ci	if (!qedf) {
39848c2ecf20Sopenharmony_ci		QEDF_ERR(NULL, "qedf is NULL");
39858c2ecf20Sopenharmony_ci		return;
39868c2ecf20Sopenharmony_ci	}
39878c2ecf20Sopenharmony_ci	QEDF_ERR(&qedf->dbg_ctx, "Performing software context reset.\n");
39888c2ecf20Sopenharmony_ci	qedf_ctx_soft_reset(qedf->lport);
39898c2ecf20Sopenharmony_ci}
39908c2ecf20Sopenharmony_ci
39918c2ecf20Sopenharmony_cistatic void qedf_shutdown(struct pci_dev *pdev)
39928c2ecf20Sopenharmony_ci{
39938c2ecf20Sopenharmony_ci	__qedf_remove(pdev, QEDF_MODE_NORMAL);
39948c2ecf20Sopenharmony_ci}
39958c2ecf20Sopenharmony_ci
39968c2ecf20Sopenharmony_cistatic int qedf_suspend(struct pci_dev *pdev, pm_message_t state)
39978c2ecf20Sopenharmony_ci{
39988c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf;
39998c2ecf20Sopenharmony_ci
40008c2ecf20Sopenharmony_ci	if (!pdev) {
40018c2ecf20Sopenharmony_ci		QEDF_ERR(NULL, "pdev is NULL.\n");
40028c2ecf20Sopenharmony_ci		return -ENODEV;
40038c2ecf20Sopenharmony_ci	}
40048c2ecf20Sopenharmony_ci
40058c2ecf20Sopenharmony_ci	qedf = pci_get_drvdata(pdev);
40068c2ecf20Sopenharmony_ci
40078c2ecf20Sopenharmony_ci	QEDF_ERR(&qedf->dbg_ctx, "%s: Device does not support suspend operation\n", __func__);
40088c2ecf20Sopenharmony_ci
40098c2ecf20Sopenharmony_ci	return -EPERM;
40108c2ecf20Sopenharmony_ci}
40118c2ecf20Sopenharmony_ci
40128c2ecf20Sopenharmony_ci/*
40138c2ecf20Sopenharmony_ci * Recovery handler code
40148c2ecf20Sopenharmony_ci */
40158c2ecf20Sopenharmony_cistatic void qedf_schedule_recovery_handler(void *dev)
40168c2ecf20Sopenharmony_ci{
40178c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf = dev;
40188c2ecf20Sopenharmony_ci
40198c2ecf20Sopenharmony_ci	QEDF_ERR(&qedf->dbg_ctx, "Recovery handler scheduled.\n");
40208c2ecf20Sopenharmony_ci	schedule_delayed_work(&qedf->recovery_work, 0);
40218c2ecf20Sopenharmony_ci}
40228c2ecf20Sopenharmony_ci
40238c2ecf20Sopenharmony_cistatic void qedf_recovery_handler(struct work_struct *work)
40248c2ecf20Sopenharmony_ci{
40258c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf =
40268c2ecf20Sopenharmony_ci	    container_of(work, struct qedf_ctx, recovery_work.work);
40278c2ecf20Sopenharmony_ci
40288c2ecf20Sopenharmony_ci	if (test_and_set_bit(QEDF_IN_RECOVERY, &qedf->flags))
40298c2ecf20Sopenharmony_ci		return;
40308c2ecf20Sopenharmony_ci
40318c2ecf20Sopenharmony_ci	/*
40328c2ecf20Sopenharmony_ci	 * Call common_ops->recovery_prolog to allow the MFW to quiesce
40338c2ecf20Sopenharmony_ci	 * any PCI transactions.
40348c2ecf20Sopenharmony_ci	 */
40358c2ecf20Sopenharmony_ci	qed_ops->common->recovery_prolog(qedf->cdev);
40368c2ecf20Sopenharmony_ci
40378c2ecf20Sopenharmony_ci	QEDF_ERR(&qedf->dbg_ctx, "Recovery work start.\n");
40388c2ecf20Sopenharmony_ci	__qedf_remove(qedf->pdev, QEDF_MODE_RECOVERY);
40398c2ecf20Sopenharmony_ci	/*
40408c2ecf20Sopenharmony_ci	 * Reset link and dcbx to down state since we will not get a link down
40418c2ecf20Sopenharmony_ci	 * event from the MFW but calling __qedf_remove will essentially be a
40428c2ecf20Sopenharmony_ci	 * link down event.
40438c2ecf20Sopenharmony_ci	 */
40448c2ecf20Sopenharmony_ci	atomic_set(&qedf->link_state, QEDF_LINK_DOWN);
40458c2ecf20Sopenharmony_ci	atomic_set(&qedf->dcbx, QEDF_DCBX_PENDING);
40468c2ecf20Sopenharmony_ci	__qedf_probe(qedf->pdev, QEDF_MODE_RECOVERY);
40478c2ecf20Sopenharmony_ci	clear_bit(QEDF_IN_RECOVERY, &qedf->flags);
40488c2ecf20Sopenharmony_ci	QEDF_ERR(&qedf->dbg_ctx, "Recovery work complete.\n");
40498c2ecf20Sopenharmony_ci}
40508c2ecf20Sopenharmony_ci
40518c2ecf20Sopenharmony_ci/* Generic TLV data callback */
40528c2ecf20Sopenharmony_civoid qedf_get_generic_tlv_data(void *dev, struct qed_generic_tlvs *data)
40538c2ecf20Sopenharmony_ci{
40548c2ecf20Sopenharmony_ci	struct qedf_ctx *qedf;
40558c2ecf20Sopenharmony_ci
40568c2ecf20Sopenharmony_ci	if (!dev) {
40578c2ecf20Sopenharmony_ci		QEDF_INFO(NULL, QEDF_LOG_EVT,
40588c2ecf20Sopenharmony_ci			  "dev is NULL so ignoring get_generic_tlv_data request.\n");
40598c2ecf20Sopenharmony_ci		return;
40608c2ecf20Sopenharmony_ci	}
40618c2ecf20Sopenharmony_ci	qedf = (struct qedf_ctx *)dev;
40628c2ecf20Sopenharmony_ci
40638c2ecf20Sopenharmony_ci	memset(data, 0, sizeof(struct qed_generic_tlvs));
40648c2ecf20Sopenharmony_ci	ether_addr_copy(data->mac[0], qedf->mac);
40658c2ecf20Sopenharmony_ci}
40668c2ecf20Sopenharmony_ci
40678c2ecf20Sopenharmony_ci/*
40688c2ecf20Sopenharmony_ci * Module Init/Remove
40698c2ecf20Sopenharmony_ci */
40708c2ecf20Sopenharmony_ci
40718c2ecf20Sopenharmony_cistatic int __init qedf_init(void)
40728c2ecf20Sopenharmony_ci{
40738c2ecf20Sopenharmony_ci	int ret;
40748c2ecf20Sopenharmony_ci
40758c2ecf20Sopenharmony_ci	/* If debug=1 passed, set the default log mask */
40768c2ecf20Sopenharmony_ci	if (qedf_debug == QEDF_LOG_DEFAULT)
40778c2ecf20Sopenharmony_ci		qedf_debug = QEDF_DEFAULT_LOG_MASK;
40788c2ecf20Sopenharmony_ci
40798c2ecf20Sopenharmony_ci	/*
40808c2ecf20Sopenharmony_ci	 * Check that default prio for FIP/FCoE traffic is between 0..7 if a
40818c2ecf20Sopenharmony_ci	 * value has been set
40828c2ecf20Sopenharmony_ci	 */
40838c2ecf20Sopenharmony_ci	if (qedf_default_prio > -1)
40848c2ecf20Sopenharmony_ci		if (qedf_default_prio > 7) {
40858c2ecf20Sopenharmony_ci			qedf_default_prio = QEDF_DEFAULT_PRIO;
40868c2ecf20Sopenharmony_ci			QEDF_ERR(NULL, "FCoE/FIP priority out of range, resetting to %d.\n",
40878c2ecf20Sopenharmony_ci			    QEDF_DEFAULT_PRIO);
40888c2ecf20Sopenharmony_ci		}
40898c2ecf20Sopenharmony_ci
40908c2ecf20Sopenharmony_ci	/* Print driver banner */
40918c2ecf20Sopenharmony_ci	QEDF_INFO(NULL, QEDF_LOG_INFO, "%s v%s.\n", QEDF_DESCR,
40928c2ecf20Sopenharmony_ci		   QEDF_VERSION);
40938c2ecf20Sopenharmony_ci
40948c2ecf20Sopenharmony_ci	/* Create kmem_cache for qedf_io_work structs */
40958c2ecf20Sopenharmony_ci	qedf_io_work_cache = kmem_cache_create("qedf_io_work_cache",
40968c2ecf20Sopenharmony_ci	    sizeof(struct qedf_io_work), 0, SLAB_HWCACHE_ALIGN, NULL);
40978c2ecf20Sopenharmony_ci	if (qedf_io_work_cache == NULL) {
40988c2ecf20Sopenharmony_ci		QEDF_ERR(NULL, "qedf_io_work_cache is NULL.\n");
40998c2ecf20Sopenharmony_ci		goto err1;
41008c2ecf20Sopenharmony_ci	}
41018c2ecf20Sopenharmony_ci	QEDF_INFO(NULL, QEDF_LOG_DISC, "qedf_io_work_cache=%p.\n",
41028c2ecf20Sopenharmony_ci	    qedf_io_work_cache);
41038c2ecf20Sopenharmony_ci
41048c2ecf20Sopenharmony_ci	qed_ops = qed_get_fcoe_ops();
41058c2ecf20Sopenharmony_ci	if (!qed_ops) {
41068c2ecf20Sopenharmony_ci		QEDF_ERR(NULL, "Failed to get qed fcoe operations\n");
41078c2ecf20Sopenharmony_ci		goto err1;
41088c2ecf20Sopenharmony_ci	}
41098c2ecf20Sopenharmony_ci
41108c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
41118c2ecf20Sopenharmony_ci	qedf_dbg_init("qedf");
41128c2ecf20Sopenharmony_ci#endif
41138c2ecf20Sopenharmony_ci
41148c2ecf20Sopenharmony_ci	qedf_fc_transport_template =
41158c2ecf20Sopenharmony_ci	    fc_attach_transport(&qedf_fc_transport_fn);
41168c2ecf20Sopenharmony_ci	if (!qedf_fc_transport_template) {
41178c2ecf20Sopenharmony_ci		QEDF_ERR(NULL, "Could not register with FC transport\n");
41188c2ecf20Sopenharmony_ci		goto err2;
41198c2ecf20Sopenharmony_ci	}
41208c2ecf20Sopenharmony_ci
41218c2ecf20Sopenharmony_ci	qedf_fc_vport_transport_template =
41228c2ecf20Sopenharmony_ci		fc_attach_transport(&qedf_fc_vport_transport_fn);
41238c2ecf20Sopenharmony_ci	if (!qedf_fc_vport_transport_template) {
41248c2ecf20Sopenharmony_ci		QEDF_ERR(NULL, "Could not register vport template with FC "
41258c2ecf20Sopenharmony_ci			  "transport\n");
41268c2ecf20Sopenharmony_ci		goto err3;
41278c2ecf20Sopenharmony_ci	}
41288c2ecf20Sopenharmony_ci
41298c2ecf20Sopenharmony_ci	qedf_io_wq = create_workqueue("qedf_io_wq");
41308c2ecf20Sopenharmony_ci	if (!qedf_io_wq) {
41318c2ecf20Sopenharmony_ci		QEDF_ERR(NULL, "Could not create qedf_io_wq.\n");
41328c2ecf20Sopenharmony_ci		goto err4;
41338c2ecf20Sopenharmony_ci	}
41348c2ecf20Sopenharmony_ci
41358c2ecf20Sopenharmony_ci	qedf_cb_ops.get_login_failures = qedf_get_login_failures;
41368c2ecf20Sopenharmony_ci
41378c2ecf20Sopenharmony_ci	ret = pci_register_driver(&qedf_pci_driver);
41388c2ecf20Sopenharmony_ci	if (ret) {
41398c2ecf20Sopenharmony_ci		QEDF_ERR(NULL, "Failed to register driver\n");
41408c2ecf20Sopenharmony_ci		goto err5;
41418c2ecf20Sopenharmony_ci	}
41428c2ecf20Sopenharmony_ci
41438c2ecf20Sopenharmony_ci	return 0;
41448c2ecf20Sopenharmony_ci
41458c2ecf20Sopenharmony_cierr5:
41468c2ecf20Sopenharmony_ci	destroy_workqueue(qedf_io_wq);
41478c2ecf20Sopenharmony_cierr4:
41488c2ecf20Sopenharmony_ci	fc_release_transport(qedf_fc_vport_transport_template);
41498c2ecf20Sopenharmony_cierr3:
41508c2ecf20Sopenharmony_ci	fc_release_transport(qedf_fc_transport_template);
41518c2ecf20Sopenharmony_cierr2:
41528c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
41538c2ecf20Sopenharmony_ci	qedf_dbg_exit();
41548c2ecf20Sopenharmony_ci#endif
41558c2ecf20Sopenharmony_ci	qed_put_fcoe_ops();
41568c2ecf20Sopenharmony_cierr1:
41578c2ecf20Sopenharmony_ci	return -EINVAL;
41588c2ecf20Sopenharmony_ci}
41598c2ecf20Sopenharmony_ci
41608c2ecf20Sopenharmony_cistatic void __exit qedf_cleanup(void)
41618c2ecf20Sopenharmony_ci{
41628c2ecf20Sopenharmony_ci	pci_unregister_driver(&qedf_pci_driver);
41638c2ecf20Sopenharmony_ci
41648c2ecf20Sopenharmony_ci	destroy_workqueue(qedf_io_wq);
41658c2ecf20Sopenharmony_ci
41668c2ecf20Sopenharmony_ci	fc_release_transport(qedf_fc_vport_transport_template);
41678c2ecf20Sopenharmony_ci	fc_release_transport(qedf_fc_transport_template);
41688c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
41698c2ecf20Sopenharmony_ci	qedf_dbg_exit();
41708c2ecf20Sopenharmony_ci#endif
41718c2ecf20Sopenharmony_ci	qed_put_fcoe_ops();
41728c2ecf20Sopenharmony_ci
41738c2ecf20Sopenharmony_ci	kmem_cache_destroy(qedf_io_work_cache);
41748c2ecf20Sopenharmony_ci}
41758c2ecf20Sopenharmony_ci
41768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
41778c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("QLogic FastLinQ 4xxxx FCoE Module");
41788c2ecf20Sopenharmony_ciMODULE_AUTHOR("QLogic Corporation");
41798c2ecf20Sopenharmony_ciMODULE_VERSION(QEDF_VERSION);
41808c2ecf20Sopenharmony_cimodule_init(qedf_init);
41818c2ecf20Sopenharmony_cimodule_exit(qedf_cleanup);
4182