18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright(c) 1999 - 2018 Intel Corporation. */
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include "ixgbe.h"
58c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
68c2ecf20Sopenharmony_ci#include <linux/gfp.h>
78c2ecf20Sopenharmony_ci#include <linux/if_vlan.h>
88c2ecf20Sopenharmony_ci#include <generated/utsrelease.h>
98c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h>
108c2ecf20Sopenharmony_ci#include <scsi/scsi_device.h>
118c2ecf20Sopenharmony_ci#include <scsi/fc/fc_fs.h>
128c2ecf20Sopenharmony_ci#include <scsi/fc/fc_fcoe.h>
138c2ecf20Sopenharmony_ci#include <scsi/libfc.h>
148c2ecf20Sopenharmony_ci#include <scsi/libfcoe.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/**
178c2ecf20Sopenharmony_ci * ixgbe_fcoe_clear_ddp - clear the given ddp context
188c2ecf20Sopenharmony_ci * @ddp: ptr to the ixgbe_fcoe_ddp
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * Returns : none
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_cistatic inline void ixgbe_fcoe_clear_ddp(struct ixgbe_fcoe_ddp *ddp)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	ddp->len = 0;
268c2ecf20Sopenharmony_ci	ddp->err = 1;
278c2ecf20Sopenharmony_ci	ddp->udl = NULL;
288c2ecf20Sopenharmony_ci	ddp->udp = 0UL;
298c2ecf20Sopenharmony_ci	ddp->sgl = NULL;
308c2ecf20Sopenharmony_ci	ddp->sgc = 0;
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/**
348c2ecf20Sopenharmony_ci * ixgbe_fcoe_ddp_put - free the ddp context for a given xid
358c2ecf20Sopenharmony_ci * @netdev: the corresponding net_device
368c2ecf20Sopenharmony_ci * @xid: the xid that corresponding ddp will be freed
378c2ecf20Sopenharmony_ci *
388c2ecf20Sopenharmony_ci * This is the implementation of net_device_ops.ndo_fcoe_ddp_done
398c2ecf20Sopenharmony_ci * and it is expected to be called by ULD, i.e., FCP layer of libfc
408c2ecf20Sopenharmony_ci * to release the corresponding ddp context when the I/O is done.
418c2ecf20Sopenharmony_ci *
428c2ecf20Sopenharmony_ci * Returns : data length already ddp-ed in bytes
438c2ecf20Sopenharmony_ci */
448c2ecf20Sopenharmony_ciint ixgbe_fcoe_ddp_put(struct net_device *netdev, u16 xid)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	int len;
478c2ecf20Sopenharmony_ci	struct ixgbe_fcoe *fcoe;
488c2ecf20Sopenharmony_ci	struct ixgbe_adapter *adapter;
498c2ecf20Sopenharmony_ci	struct ixgbe_fcoe_ddp *ddp;
508c2ecf20Sopenharmony_ci	struct ixgbe_hw *hw;
518c2ecf20Sopenharmony_ci	u32 fcbuff;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	if (!netdev)
548c2ecf20Sopenharmony_ci		return 0;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	if (xid >= netdev->fcoe_ddp_xid)
578c2ecf20Sopenharmony_ci		return 0;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	adapter = netdev_priv(netdev);
608c2ecf20Sopenharmony_ci	fcoe = &adapter->fcoe;
618c2ecf20Sopenharmony_ci	ddp = &fcoe->ddp[xid];
628c2ecf20Sopenharmony_ci	if (!ddp->udl)
638c2ecf20Sopenharmony_ci		return 0;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	hw = &adapter->hw;
668c2ecf20Sopenharmony_ci	len = ddp->len;
678c2ecf20Sopenharmony_ci	/* if no error then skip ddp context invalidation */
688c2ecf20Sopenharmony_ci	if (!ddp->err)
698c2ecf20Sopenharmony_ci		goto skip_ddpinv;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	if (hw->mac.type == ixgbe_mac_X550) {
728c2ecf20Sopenharmony_ci		/* X550 does not require DDP FCoE lock */
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCDFC(0, xid), 0);
758c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCDFC(3, xid),
768c2ecf20Sopenharmony_ci				(xid | IXGBE_FCFLTRW_WE));
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci		/* program FCBUFF */
798c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCDDC(2, xid), 0);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci		/* program FCDMARW */
828c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCDDC(3, xid),
838c2ecf20Sopenharmony_ci				(xid | IXGBE_FCDMARW_WE));
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci		/* read FCBUFF to check context invalidated */
868c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCDDC(3, xid),
878c2ecf20Sopenharmony_ci				(xid | IXGBE_FCDMARW_RE));
888c2ecf20Sopenharmony_ci		fcbuff = IXGBE_READ_REG(hw, IXGBE_FCDDC(2, xid));
898c2ecf20Sopenharmony_ci	} else {
908c2ecf20Sopenharmony_ci		/* other hardware requires DDP FCoE lock */
918c2ecf20Sopenharmony_ci		spin_lock_bh(&fcoe->lock);
928c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCFLT, 0);
938c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCFLTRW,
948c2ecf20Sopenharmony_ci				(xid | IXGBE_FCFLTRW_WE));
958c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCBUFF, 0);
968c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCDMARW,
978c2ecf20Sopenharmony_ci				(xid | IXGBE_FCDMARW_WE));
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci		/* guaranteed to be invalidated after 100us */
1008c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCDMARW,
1018c2ecf20Sopenharmony_ci				(xid | IXGBE_FCDMARW_RE));
1028c2ecf20Sopenharmony_ci		fcbuff = IXGBE_READ_REG(hw, IXGBE_FCBUFF);
1038c2ecf20Sopenharmony_ci		spin_unlock_bh(&fcoe->lock);
1048c2ecf20Sopenharmony_ci		}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (fcbuff & IXGBE_FCBUFF_VALID)
1078c2ecf20Sopenharmony_ci		usleep_range(100, 150);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ciskip_ddpinv:
1108c2ecf20Sopenharmony_ci	if (ddp->sgl)
1118c2ecf20Sopenharmony_ci		dma_unmap_sg(&adapter->pdev->dev, ddp->sgl, ddp->sgc,
1128c2ecf20Sopenharmony_ci			     DMA_FROM_DEVICE);
1138c2ecf20Sopenharmony_ci	if (ddp->pool) {
1148c2ecf20Sopenharmony_ci		dma_pool_free(ddp->pool, ddp->udl, ddp->udp);
1158c2ecf20Sopenharmony_ci		ddp->pool = NULL;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	ixgbe_fcoe_clear_ddp(ddp);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	return len;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/**
1248c2ecf20Sopenharmony_ci * ixgbe_fcoe_ddp_setup - called to set up ddp context
1258c2ecf20Sopenharmony_ci * @netdev: the corresponding net_device
1268c2ecf20Sopenharmony_ci * @xid: the exchange id requesting ddp
1278c2ecf20Sopenharmony_ci * @sgl: the scatter-gather list for this request
1288c2ecf20Sopenharmony_ci * @sgc: the number of scatter-gather items
1298c2ecf20Sopenharmony_ci * @target_mode: 1 to setup target mode, 0 to setup initiator mode
1308c2ecf20Sopenharmony_ci *
1318c2ecf20Sopenharmony_ci * Returns : 1 for success and 0 for no ddp
1328c2ecf20Sopenharmony_ci */
1338c2ecf20Sopenharmony_cistatic int ixgbe_fcoe_ddp_setup(struct net_device *netdev, u16 xid,
1348c2ecf20Sopenharmony_ci				struct scatterlist *sgl, unsigned int sgc,
1358c2ecf20Sopenharmony_ci				int target_mode)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	struct ixgbe_adapter *adapter;
1388c2ecf20Sopenharmony_ci	struct ixgbe_hw *hw;
1398c2ecf20Sopenharmony_ci	struct ixgbe_fcoe *fcoe;
1408c2ecf20Sopenharmony_ci	struct ixgbe_fcoe_ddp *ddp;
1418c2ecf20Sopenharmony_ci	struct ixgbe_fcoe_ddp_pool *ddp_pool;
1428c2ecf20Sopenharmony_ci	struct scatterlist *sg;
1438c2ecf20Sopenharmony_ci	unsigned int i, j, dmacount;
1448c2ecf20Sopenharmony_ci	unsigned int len;
1458c2ecf20Sopenharmony_ci	static const unsigned int bufflen = IXGBE_FCBUFF_MIN;
1468c2ecf20Sopenharmony_ci	unsigned int firstoff = 0;
1478c2ecf20Sopenharmony_ci	unsigned int lastsize;
1488c2ecf20Sopenharmony_ci	unsigned int thisoff = 0;
1498c2ecf20Sopenharmony_ci	unsigned int thislen = 0;
1508c2ecf20Sopenharmony_ci	u32 fcbuff, fcdmarw, fcfltrw, fcrxctl;
1518c2ecf20Sopenharmony_ci	dma_addr_t addr = 0;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (!netdev || !sgl)
1548c2ecf20Sopenharmony_ci		return 0;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	adapter = netdev_priv(netdev);
1578c2ecf20Sopenharmony_ci	if (xid >= netdev->fcoe_ddp_xid) {
1588c2ecf20Sopenharmony_ci		e_warn(drv, "xid=0x%x out-of-range\n", xid);
1598c2ecf20Sopenharmony_ci		return 0;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	/* no DDP if we are already down or resetting */
1638c2ecf20Sopenharmony_ci	if (test_bit(__IXGBE_DOWN, &adapter->state) ||
1648c2ecf20Sopenharmony_ci	    test_bit(__IXGBE_RESETTING, &adapter->state))
1658c2ecf20Sopenharmony_ci		return 0;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	fcoe = &adapter->fcoe;
1688c2ecf20Sopenharmony_ci	ddp = &fcoe->ddp[xid];
1698c2ecf20Sopenharmony_ci	if (ddp->sgl) {
1708c2ecf20Sopenharmony_ci		e_err(drv, "xid 0x%x w/ non-null sgl=%p nents=%d\n",
1718c2ecf20Sopenharmony_ci		      xid, ddp->sgl, ddp->sgc);
1728c2ecf20Sopenharmony_ci		return 0;
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci	ixgbe_fcoe_clear_ddp(ddp);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (!fcoe->ddp_pool) {
1788c2ecf20Sopenharmony_ci		e_warn(drv, "No ddp_pool resources allocated\n");
1798c2ecf20Sopenharmony_ci		return 0;
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	ddp_pool = per_cpu_ptr(fcoe->ddp_pool, get_cpu());
1838c2ecf20Sopenharmony_ci	if (!ddp_pool->pool) {
1848c2ecf20Sopenharmony_ci		e_warn(drv, "xid=0x%x no ddp pool for fcoe\n", xid);
1858c2ecf20Sopenharmony_ci		goto out_noddp;
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/* setup dma from scsi command sgl */
1898c2ecf20Sopenharmony_ci	dmacount = dma_map_sg(&adapter->pdev->dev, sgl, sgc, DMA_FROM_DEVICE);
1908c2ecf20Sopenharmony_ci	if (dmacount == 0) {
1918c2ecf20Sopenharmony_ci		e_err(drv, "xid 0x%x DMA map error\n", xid);
1928c2ecf20Sopenharmony_ci		goto out_noddp;
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	/* alloc the udl from per cpu ddp pool */
1968c2ecf20Sopenharmony_ci	ddp->udl = dma_pool_alloc(ddp_pool->pool, GFP_ATOMIC, &ddp->udp);
1978c2ecf20Sopenharmony_ci	if (!ddp->udl) {
1988c2ecf20Sopenharmony_ci		e_err(drv, "failed allocated ddp context\n");
1998c2ecf20Sopenharmony_ci		goto out_noddp_unmap;
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci	ddp->pool = ddp_pool->pool;
2028c2ecf20Sopenharmony_ci	ddp->sgl = sgl;
2038c2ecf20Sopenharmony_ci	ddp->sgc = sgc;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	j = 0;
2068c2ecf20Sopenharmony_ci	for_each_sg(sgl, sg, dmacount, i) {
2078c2ecf20Sopenharmony_ci		addr = sg_dma_address(sg);
2088c2ecf20Sopenharmony_ci		len = sg_dma_len(sg);
2098c2ecf20Sopenharmony_ci		while (len) {
2108c2ecf20Sopenharmony_ci			/* max number of buffers allowed in one DDP context */
2118c2ecf20Sopenharmony_ci			if (j >= IXGBE_BUFFCNT_MAX) {
2128c2ecf20Sopenharmony_ci				ddp_pool->noddp++;
2138c2ecf20Sopenharmony_ci				goto out_noddp_free;
2148c2ecf20Sopenharmony_ci			}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci			/* get the offset of length of current buffer */
2178c2ecf20Sopenharmony_ci			thisoff = addr & ((dma_addr_t)bufflen - 1);
2188c2ecf20Sopenharmony_ci			thislen = min((bufflen - thisoff), len);
2198c2ecf20Sopenharmony_ci			/*
2208c2ecf20Sopenharmony_ci			 * all but the 1st buffer (j == 0)
2218c2ecf20Sopenharmony_ci			 * must be aligned on bufflen
2228c2ecf20Sopenharmony_ci			 */
2238c2ecf20Sopenharmony_ci			if ((j != 0) && (thisoff))
2248c2ecf20Sopenharmony_ci				goto out_noddp_free;
2258c2ecf20Sopenharmony_ci			/*
2268c2ecf20Sopenharmony_ci			 * all but the last buffer
2278c2ecf20Sopenharmony_ci			 * ((i == (dmacount - 1)) && (thislen == len))
2288c2ecf20Sopenharmony_ci			 * must end at bufflen
2298c2ecf20Sopenharmony_ci			 */
2308c2ecf20Sopenharmony_ci			if (((i != (dmacount - 1)) || (thislen != len))
2318c2ecf20Sopenharmony_ci			    && ((thislen + thisoff) != bufflen))
2328c2ecf20Sopenharmony_ci				goto out_noddp_free;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci			ddp->udl[j] = (u64)(addr - thisoff);
2358c2ecf20Sopenharmony_ci			/* only the first buffer may have none-zero offset */
2368c2ecf20Sopenharmony_ci			if (j == 0)
2378c2ecf20Sopenharmony_ci				firstoff = thisoff;
2388c2ecf20Sopenharmony_ci			len -= thislen;
2398c2ecf20Sopenharmony_ci			addr += thislen;
2408c2ecf20Sopenharmony_ci			j++;
2418c2ecf20Sopenharmony_ci		}
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci	/* only the last buffer may have non-full bufflen */
2448c2ecf20Sopenharmony_ci	lastsize = thisoff + thislen;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	/*
2478c2ecf20Sopenharmony_ci	 * lastsize can not be buffer len.
2488c2ecf20Sopenharmony_ci	 * If it is then adding another buffer with lastsize = 1.
2498c2ecf20Sopenharmony_ci	 */
2508c2ecf20Sopenharmony_ci	if (lastsize == bufflen) {
2518c2ecf20Sopenharmony_ci		if (j >= IXGBE_BUFFCNT_MAX) {
2528c2ecf20Sopenharmony_ci			ddp_pool->noddp_ext_buff++;
2538c2ecf20Sopenharmony_ci			goto out_noddp_free;
2548c2ecf20Sopenharmony_ci		}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci		ddp->udl[j] = (u64)(fcoe->extra_ddp_buffer_dma);
2578c2ecf20Sopenharmony_ci		j++;
2588c2ecf20Sopenharmony_ci		lastsize = 1;
2598c2ecf20Sopenharmony_ci	}
2608c2ecf20Sopenharmony_ci	put_cpu();
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	fcbuff = (IXGBE_FCBUFF_4KB << IXGBE_FCBUFF_BUFFSIZE_SHIFT);
2638c2ecf20Sopenharmony_ci	fcbuff |= ((j & 0xff) << IXGBE_FCBUFF_BUFFCNT_SHIFT);
2648c2ecf20Sopenharmony_ci	fcbuff |= (firstoff << IXGBE_FCBUFF_OFFSET_SHIFT);
2658c2ecf20Sopenharmony_ci	/* Set WRCONTX bit to allow DDP for target */
2668c2ecf20Sopenharmony_ci	if (target_mode)
2678c2ecf20Sopenharmony_ci		fcbuff |= (IXGBE_FCBUFF_WRCONTX);
2688c2ecf20Sopenharmony_ci	fcbuff |= (IXGBE_FCBUFF_VALID);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	fcdmarw = xid;
2718c2ecf20Sopenharmony_ci	fcdmarw |= IXGBE_FCDMARW_WE;
2728c2ecf20Sopenharmony_ci	fcdmarw |= (lastsize << IXGBE_FCDMARW_LASTSIZE_SHIFT);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	fcfltrw = xid;
2758c2ecf20Sopenharmony_ci	fcfltrw |= IXGBE_FCFLTRW_WE;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	/* program DMA context */
2788c2ecf20Sopenharmony_ci	hw = &adapter->hw;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	/* turn on last frame indication for target mode as FCP_RSPtarget is
2818c2ecf20Sopenharmony_ci	 * supposed to send FCP_RSP when it is done. */
2828c2ecf20Sopenharmony_ci	if (target_mode && !test_bit(__IXGBE_FCOE_TARGET, &fcoe->mode)) {
2838c2ecf20Sopenharmony_ci		set_bit(__IXGBE_FCOE_TARGET, &fcoe->mode);
2848c2ecf20Sopenharmony_ci		fcrxctl = IXGBE_READ_REG(hw, IXGBE_FCRXCTRL);
2858c2ecf20Sopenharmony_ci		fcrxctl |= IXGBE_FCRXCTRL_LASTSEQH;
2868c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCRXCTRL, fcrxctl);
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (hw->mac.type == ixgbe_mac_X550) {
2908c2ecf20Sopenharmony_ci		/* X550 does not require DDP lock */
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCDDC(0, xid),
2938c2ecf20Sopenharmony_ci				ddp->udp & DMA_BIT_MASK(32));
2948c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCDDC(1, xid), (u64)ddp->udp >> 32);
2958c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCDDC(2, xid), fcbuff);
2968c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCDDC(3, xid), fcdmarw);
2978c2ecf20Sopenharmony_ci		/* program filter context */
2988c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCDFC(0, xid), IXGBE_FCFLT_VALID);
2998c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCDFC(1, xid), 0);
3008c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCDFC(3, xid), fcfltrw);
3018c2ecf20Sopenharmony_ci	} else {
3028c2ecf20Sopenharmony_ci		/* DDP lock for indirect DDP context access */
3038c2ecf20Sopenharmony_ci		spin_lock_bh(&fcoe->lock);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCPTRL, ddp->udp & DMA_BIT_MASK(32));
3068c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCPTRH, (u64)ddp->udp >> 32);
3078c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCBUFF, fcbuff);
3088c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCDMARW, fcdmarw);
3098c2ecf20Sopenharmony_ci		/* program filter context */
3108c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCPARAM, 0);
3118c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCFLT, IXGBE_FCFLT_VALID);
3128c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCFLTRW, fcfltrw);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci		spin_unlock_bh(&fcoe->lock);
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	return 1;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ciout_noddp_free:
3208c2ecf20Sopenharmony_ci	dma_pool_free(ddp->pool, ddp->udl, ddp->udp);
3218c2ecf20Sopenharmony_ci	ixgbe_fcoe_clear_ddp(ddp);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ciout_noddp_unmap:
3248c2ecf20Sopenharmony_ci	dma_unmap_sg(&adapter->pdev->dev, sgl, sgc, DMA_FROM_DEVICE);
3258c2ecf20Sopenharmony_ciout_noddp:
3268c2ecf20Sopenharmony_ci	put_cpu();
3278c2ecf20Sopenharmony_ci	return 0;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci/**
3318c2ecf20Sopenharmony_ci * ixgbe_fcoe_ddp_get - called to set up ddp context in initiator mode
3328c2ecf20Sopenharmony_ci * @netdev: the corresponding net_device
3338c2ecf20Sopenharmony_ci * @xid: the exchange id requesting ddp
3348c2ecf20Sopenharmony_ci * @sgl: the scatter-gather list for this request
3358c2ecf20Sopenharmony_ci * @sgc: the number of scatter-gather items
3368c2ecf20Sopenharmony_ci *
3378c2ecf20Sopenharmony_ci * This is the implementation of net_device_ops.ndo_fcoe_ddp_setup
3388c2ecf20Sopenharmony_ci * and is expected to be called from ULD, e.g., FCP layer of libfc
3398c2ecf20Sopenharmony_ci * to set up ddp for the corresponding xid of the given sglist for
3408c2ecf20Sopenharmony_ci * the corresponding I/O.
3418c2ecf20Sopenharmony_ci *
3428c2ecf20Sopenharmony_ci * Returns : 1 for success and 0 for no ddp
3438c2ecf20Sopenharmony_ci */
3448c2ecf20Sopenharmony_ciint ixgbe_fcoe_ddp_get(struct net_device *netdev, u16 xid,
3458c2ecf20Sopenharmony_ci		       struct scatterlist *sgl, unsigned int sgc)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	return ixgbe_fcoe_ddp_setup(netdev, xid, sgl, sgc, 0);
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci/**
3518c2ecf20Sopenharmony_ci * ixgbe_fcoe_ddp_target - called to set up ddp context in target mode
3528c2ecf20Sopenharmony_ci * @netdev: the corresponding net_device
3538c2ecf20Sopenharmony_ci * @xid: the exchange id requesting ddp
3548c2ecf20Sopenharmony_ci * @sgl: the scatter-gather list for this request
3558c2ecf20Sopenharmony_ci * @sgc: the number of scatter-gather items
3568c2ecf20Sopenharmony_ci *
3578c2ecf20Sopenharmony_ci * This is the implementation of net_device_ops.ndo_fcoe_ddp_target
3588c2ecf20Sopenharmony_ci * and is expected to be called from ULD, e.g., FCP layer of libfc
3598c2ecf20Sopenharmony_ci * to set up ddp for the corresponding xid of the given sglist for
3608c2ecf20Sopenharmony_ci * the corresponding I/O. The DDP in target mode is a write I/O request
3618c2ecf20Sopenharmony_ci * from the initiator.
3628c2ecf20Sopenharmony_ci *
3638c2ecf20Sopenharmony_ci * Returns : 1 for success and 0 for no ddp
3648c2ecf20Sopenharmony_ci */
3658c2ecf20Sopenharmony_ciint ixgbe_fcoe_ddp_target(struct net_device *netdev, u16 xid,
3668c2ecf20Sopenharmony_ci			    struct scatterlist *sgl, unsigned int sgc)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	return ixgbe_fcoe_ddp_setup(netdev, xid, sgl, sgc, 1);
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci/**
3728c2ecf20Sopenharmony_ci * ixgbe_fcoe_ddp - check ddp status and mark it done
3738c2ecf20Sopenharmony_ci * @adapter: ixgbe adapter
3748c2ecf20Sopenharmony_ci * @rx_desc: advanced rx descriptor
3758c2ecf20Sopenharmony_ci * @skb: the skb holding the received data
3768c2ecf20Sopenharmony_ci *
3778c2ecf20Sopenharmony_ci * This checks ddp status.
3788c2ecf20Sopenharmony_ci *
3798c2ecf20Sopenharmony_ci * Returns : < 0 indicates an error or not a FCiE ddp, 0 indicates
3808c2ecf20Sopenharmony_ci * not passing the skb to ULD, > 0 indicates is the length of data
3818c2ecf20Sopenharmony_ci * being ddped.
3828c2ecf20Sopenharmony_ci */
3838c2ecf20Sopenharmony_ciint ixgbe_fcoe_ddp(struct ixgbe_adapter *adapter,
3848c2ecf20Sopenharmony_ci		   union ixgbe_adv_rx_desc *rx_desc,
3858c2ecf20Sopenharmony_ci		   struct sk_buff *skb)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	int rc = -EINVAL;
3888c2ecf20Sopenharmony_ci	struct ixgbe_fcoe *fcoe;
3898c2ecf20Sopenharmony_ci	struct ixgbe_fcoe_ddp *ddp;
3908c2ecf20Sopenharmony_ci	struct fc_frame_header *fh;
3918c2ecf20Sopenharmony_ci	struct fcoe_crc_eof *crc;
3928c2ecf20Sopenharmony_ci	__le32 fcerr = ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_ERR_FCERR);
3938c2ecf20Sopenharmony_ci	__le32 ddp_err;
3948c2ecf20Sopenharmony_ci	int ddp_max;
3958c2ecf20Sopenharmony_ci	u32 fctl;
3968c2ecf20Sopenharmony_ci	u16 xid;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	if (fcerr == cpu_to_le32(IXGBE_FCERR_BADCRC))
3998c2ecf20Sopenharmony_ci		skb->ip_summed = CHECKSUM_NONE;
4008c2ecf20Sopenharmony_ci	else
4018c2ecf20Sopenharmony_ci		skb->ip_summed = CHECKSUM_UNNECESSARY;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	if (eth_hdr(skb)->h_proto == htons(ETH_P_8021Q))
4048c2ecf20Sopenharmony_ci		fh = (struct fc_frame_header *)(skb->data +
4058c2ecf20Sopenharmony_ci			sizeof(struct vlan_hdr) + sizeof(struct fcoe_hdr));
4068c2ecf20Sopenharmony_ci	else
4078c2ecf20Sopenharmony_ci		fh = (struct fc_frame_header *)(skb->data +
4088c2ecf20Sopenharmony_ci			sizeof(struct fcoe_hdr));
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	fctl = ntoh24(fh->fh_f_ctl);
4118c2ecf20Sopenharmony_ci	if (fctl & FC_FC_EX_CTX)
4128c2ecf20Sopenharmony_ci		xid =  be16_to_cpu(fh->fh_ox_id);
4138c2ecf20Sopenharmony_ci	else
4148c2ecf20Sopenharmony_ci		xid =  be16_to_cpu(fh->fh_rx_id);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	ddp_max = IXGBE_FCOE_DDP_MAX;
4178c2ecf20Sopenharmony_ci	/* X550 has different DDP Max limit */
4188c2ecf20Sopenharmony_ci	if (adapter->hw.mac.type == ixgbe_mac_X550)
4198c2ecf20Sopenharmony_ci		ddp_max = IXGBE_FCOE_DDP_MAX_X550;
4208c2ecf20Sopenharmony_ci	if (xid >= ddp_max)
4218c2ecf20Sopenharmony_ci		return -EINVAL;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	fcoe = &adapter->fcoe;
4248c2ecf20Sopenharmony_ci	ddp = &fcoe->ddp[xid];
4258c2ecf20Sopenharmony_ci	if (!ddp->udl)
4268c2ecf20Sopenharmony_ci		return -EINVAL;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	ddp_err = ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_ERR_FCEOFE |
4298c2ecf20Sopenharmony_ci					      IXGBE_RXDADV_ERR_FCERR);
4308c2ecf20Sopenharmony_ci	if (ddp_err)
4318c2ecf20Sopenharmony_ci		return -EINVAL;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	switch (ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_STAT_FCSTAT)) {
4348c2ecf20Sopenharmony_ci	/* return 0 to bypass going to ULD for DDPed data */
4358c2ecf20Sopenharmony_ci	case cpu_to_le32(IXGBE_RXDADV_STAT_FCSTAT_DDP):
4368c2ecf20Sopenharmony_ci		/* update length of DDPed data */
4378c2ecf20Sopenharmony_ci		ddp->len = le32_to_cpu(rx_desc->wb.lower.hi_dword.rss);
4388c2ecf20Sopenharmony_ci		rc = 0;
4398c2ecf20Sopenharmony_ci		break;
4408c2ecf20Sopenharmony_ci	/* unmap the sg list when FCPRSP is received */
4418c2ecf20Sopenharmony_ci	case cpu_to_le32(IXGBE_RXDADV_STAT_FCSTAT_FCPRSP):
4428c2ecf20Sopenharmony_ci		dma_unmap_sg(&adapter->pdev->dev, ddp->sgl,
4438c2ecf20Sopenharmony_ci			     ddp->sgc, DMA_FROM_DEVICE);
4448c2ecf20Sopenharmony_ci		ddp->err = (__force u32)ddp_err;
4458c2ecf20Sopenharmony_ci		ddp->sgl = NULL;
4468c2ecf20Sopenharmony_ci		ddp->sgc = 0;
4478c2ecf20Sopenharmony_ci		fallthrough;
4488c2ecf20Sopenharmony_ci	/* if DDP length is present pass it through to ULD */
4498c2ecf20Sopenharmony_ci	case cpu_to_le32(IXGBE_RXDADV_STAT_FCSTAT_NODDP):
4508c2ecf20Sopenharmony_ci		/* update length of DDPed data */
4518c2ecf20Sopenharmony_ci		ddp->len = le32_to_cpu(rx_desc->wb.lower.hi_dword.rss);
4528c2ecf20Sopenharmony_ci		if (ddp->len)
4538c2ecf20Sopenharmony_ci			rc = ddp->len;
4548c2ecf20Sopenharmony_ci		break;
4558c2ecf20Sopenharmony_ci	/* no match will return as an error */
4568c2ecf20Sopenharmony_ci	case cpu_to_le32(IXGBE_RXDADV_STAT_FCSTAT_NOMTCH):
4578c2ecf20Sopenharmony_ci	default:
4588c2ecf20Sopenharmony_ci		break;
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	/* In target mode, check the last data frame of the sequence.
4628c2ecf20Sopenharmony_ci	 * For DDP in target mode, data is already DDPed but the header
4638c2ecf20Sopenharmony_ci	 * indication of the last data frame ould allow is to tell if we
4648c2ecf20Sopenharmony_ci	 * got all the data and the ULP can send FCP_RSP back, as this is
4658c2ecf20Sopenharmony_ci	 * not a full fcoe frame, we fill the trailer here so it won't be
4668c2ecf20Sopenharmony_ci	 * dropped by the ULP stack.
4678c2ecf20Sopenharmony_ci	 */
4688c2ecf20Sopenharmony_ci	if ((fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA) &&
4698c2ecf20Sopenharmony_ci	    (fctl & FC_FC_END_SEQ)) {
4708c2ecf20Sopenharmony_ci		skb_linearize(skb);
4718c2ecf20Sopenharmony_ci		crc = skb_put(skb, sizeof(*crc));
4728c2ecf20Sopenharmony_ci		crc->fcoe_eof = FC_EOF_T;
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	return rc;
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci/**
4798c2ecf20Sopenharmony_ci * ixgbe_fso - ixgbe FCoE Sequence Offload (FSO)
4808c2ecf20Sopenharmony_ci * @tx_ring: tx desc ring
4818c2ecf20Sopenharmony_ci * @first: first tx_buffer structure containing skb, tx_flags, and protocol
4828c2ecf20Sopenharmony_ci * @hdr_len: hdr_len to be returned
4838c2ecf20Sopenharmony_ci *
4848c2ecf20Sopenharmony_ci * This sets up large send offload for FCoE
4858c2ecf20Sopenharmony_ci *
4868c2ecf20Sopenharmony_ci * Returns : 0 indicates success, < 0 for error
4878c2ecf20Sopenharmony_ci */
4888c2ecf20Sopenharmony_ciint ixgbe_fso(struct ixgbe_ring *tx_ring,
4898c2ecf20Sopenharmony_ci	      struct ixgbe_tx_buffer *first,
4908c2ecf20Sopenharmony_ci	      u8 *hdr_len)
4918c2ecf20Sopenharmony_ci{
4928c2ecf20Sopenharmony_ci	struct sk_buff *skb = first->skb;
4938c2ecf20Sopenharmony_ci	struct fc_frame_header *fh;
4948c2ecf20Sopenharmony_ci	u32 vlan_macip_lens;
4958c2ecf20Sopenharmony_ci	u32 fcoe_sof_eof = 0;
4968c2ecf20Sopenharmony_ci	u32 mss_l4len_idx;
4978c2ecf20Sopenharmony_ci	u32 type_tucmd = IXGBE_ADVTXT_TUCMD_FCOE;
4988c2ecf20Sopenharmony_ci	u8 sof, eof;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	if (skb_is_gso(skb) && (skb_shinfo(skb)->gso_type != SKB_GSO_FCOE)) {
5018c2ecf20Sopenharmony_ci		dev_err(tx_ring->dev, "Wrong gso type %d:expecting SKB_GSO_FCOE\n",
5028c2ecf20Sopenharmony_ci			skb_shinfo(skb)->gso_type);
5038c2ecf20Sopenharmony_ci		return -EINVAL;
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	/* resets the header to point fcoe/fc */
5078c2ecf20Sopenharmony_ci	skb_set_network_header(skb, skb->mac_len);
5088c2ecf20Sopenharmony_ci	skb_set_transport_header(skb, skb->mac_len +
5098c2ecf20Sopenharmony_ci				 sizeof(struct fcoe_hdr));
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	/* sets up SOF and ORIS */
5128c2ecf20Sopenharmony_ci	sof = ((struct fcoe_hdr *)skb_network_header(skb))->fcoe_sof;
5138c2ecf20Sopenharmony_ci	switch (sof) {
5148c2ecf20Sopenharmony_ci	case FC_SOF_I2:
5158c2ecf20Sopenharmony_ci		fcoe_sof_eof = IXGBE_ADVTXD_FCOEF_ORIS;
5168c2ecf20Sopenharmony_ci		break;
5178c2ecf20Sopenharmony_ci	case FC_SOF_I3:
5188c2ecf20Sopenharmony_ci		fcoe_sof_eof = IXGBE_ADVTXD_FCOEF_SOF |
5198c2ecf20Sopenharmony_ci			       IXGBE_ADVTXD_FCOEF_ORIS;
5208c2ecf20Sopenharmony_ci		break;
5218c2ecf20Sopenharmony_ci	case FC_SOF_N2:
5228c2ecf20Sopenharmony_ci		break;
5238c2ecf20Sopenharmony_ci	case FC_SOF_N3:
5248c2ecf20Sopenharmony_ci		fcoe_sof_eof = IXGBE_ADVTXD_FCOEF_SOF;
5258c2ecf20Sopenharmony_ci		break;
5268c2ecf20Sopenharmony_ci	default:
5278c2ecf20Sopenharmony_ci		dev_warn(tx_ring->dev, "unknown sof = 0x%x\n", sof);
5288c2ecf20Sopenharmony_ci		return -EINVAL;
5298c2ecf20Sopenharmony_ci	}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	/* the first byte of the last dword is EOF */
5328c2ecf20Sopenharmony_ci	skb_copy_bits(skb, skb->len - 4, &eof, 1);
5338c2ecf20Sopenharmony_ci	/* sets up EOF and ORIE */
5348c2ecf20Sopenharmony_ci	switch (eof) {
5358c2ecf20Sopenharmony_ci	case FC_EOF_N:
5368c2ecf20Sopenharmony_ci		fcoe_sof_eof |= IXGBE_ADVTXD_FCOEF_EOF_N;
5378c2ecf20Sopenharmony_ci		break;
5388c2ecf20Sopenharmony_ci	case FC_EOF_T:
5398c2ecf20Sopenharmony_ci		/* lso needs ORIE */
5408c2ecf20Sopenharmony_ci		if (skb_is_gso(skb))
5418c2ecf20Sopenharmony_ci			fcoe_sof_eof |= IXGBE_ADVTXD_FCOEF_EOF_N |
5428c2ecf20Sopenharmony_ci					IXGBE_ADVTXD_FCOEF_ORIE;
5438c2ecf20Sopenharmony_ci		else
5448c2ecf20Sopenharmony_ci			fcoe_sof_eof |= IXGBE_ADVTXD_FCOEF_EOF_T;
5458c2ecf20Sopenharmony_ci		break;
5468c2ecf20Sopenharmony_ci	case FC_EOF_NI:
5478c2ecf20Sopenharmony_ci		fcoe_sof_eof |= IXGBE_ADVTXD_FCOEF_EOF_NI;
5488c2ecf20Sopenharmony_ci		break;
5498c2ecf20Sopenharmony_ci	case FC_EOF_A:
5508c2ecf20Sopenharmony_ci		fcoe_sof_eof |= IXGBE_ADVTXD_FCOEF_EOF_A;
5518c2ecf20Sopenharmony_ci		break;
5528c2ecf20Sopenharmony_ci	default:
5538c2ecf20Sopenharmony_ci		dev_warn(tx_ring->dev, "unknown eof = 0x%x\n", eof);
5548c2ecf20Sopenharmony_ci		return -EINVAL;
5558c2ecf20Sopenharmony_ci	}
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	/* sets up PARINC indicating data offset */
5588c2ecf20Sopenharmony_ci	fh = (struct fc_frame_header *)skb_transport_header(skb);
5598c2ecf20Sopenharmony_ci	if (fh->fh_f_ctl[2] & FC_FC_REL_OFF)
5608c2ecf20Sopenharmony_ci		fcoe_sof_eof |= IXGBE_ADVTXD_FCOEF_PARINC;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	/* include trailer in headlen as it is replicated per frame */
5638c2ecf20Sopenharmony_ci	*hdr_len = sizeof(struct fcoe_crc_eof);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	/* hdr_len includes fc_hdr if FCoE LSO is enabled */
5668c2ecf20Sopenharmony_ci	if (skb_is_gso(skb)) {
5678c2ecf20Sopenharmony_ci		*hdr_len += skb_transport_offset(skb) +
5688c2ecf20Sopenharmony_ci			    sizeof(struct fc_frame_header);
5698c2ecf20Sopenharmony_ci		/* update gso_segs and bytecount */
5708c2ecf20Sopenharmony_ci		first->gso_segs = DIV_ROUND_UP(skb->len - *hdr_len,
5718c2ecf20Sopenharmony_ci					       skb_shinfo(skb)->gso_size);
5728c2ecf20Sopenharmony_ci		first->bytecount += (first->gso_segs - 1) * *hdr_len;
5738c2ecf20Sopenharmony_ci		first->tx_flags |= IXGBE_TX_FLAGS_TSO;
5748c2ecf20Sopenharmony_ci		/* Hardware expects L4T to be RSV for FCoE TSO */
5758c2ecf20Sopenharmony_ci		type_tucmd |= IXGBE_ADVTXD_TUCMD_L4T_RSV;
5768c2ecf20Sopenharmony_ci	}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	/* set flag indicating FCOE to ixgbe_tx_map call */
5798c2ecf20Sopenharmony_ci	first->tx_flags |= IXGBE_TX_FLAGS_FCOE | IXGBE_TX_FLAGS_CC;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	/* mss_l4len_id: use 0 for FSO as TSO, no need for L4LEN */
5828c2ecf20Sopenharmony_ci	mss_l4len_idx = skb_shinfo(skb)->gso_size << IXGBE_ADVTXD_MSS_SHIFT;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	/* vlan_macip_lens: HEADLEN, MACLEN, VLAN tag */
5858c2ecf20Sopenharmony_ci	vlan_macip_lens = skb_transport_offset(skb) +
5868c2ecf20Sopenharmony_ci			  sizeof(struct fc_frame_header);
5878c2ecf20Sopenharmony_ci	vlan_macip_lens |= (skb_transport_offset(skb) - 4)
5888c2ecf20Sopenharmony_ci			   << IXGBE_ADVTXD_MACLEN_SHIFT;
5898c2ecf20Sopenharmony_ci	vlan_macip_lens |= first->tx_flags & IXGBE_TX_FLAGS_VLAN_MASK;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	/* write context desc */
5928c2ecf20Sopenharmony_ci	ixgbe_tx_ctxtdesc(tx_ring, vlan_macip_lens, fcoe_sof_eof,
5938c2ecf20Sopenharmony_ci			  type_tucmd, mss_l4len_idx);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	return 0;
5968c2ecf20Sopenharmony_ci}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_cistatic void ixgbe_fcoe_dma_pool_free(struct ixgbe_fcoe *fcoe, unsigned int cpu)
5998c2ecf20Sopenharmony_ci{
6008c2ecf20Sopenharmony_ci	struct ixgbe_fcoe_ddp_pool *ddp_pool;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	ddp_pool = per_cpu_ptr(fcoe->ddp_pool, cpu);
6038c2ecf20Sopenharmony_ci	dma_pool_destroy(ddp_pool->pool);
6048c2ecf20Sopenharmony_ci	ddp_pool->pool = NULL;
6058c2ecf20Sopenharmony_ci}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_cistatic int ixgbe_fcoe_dma_pool_alloc(struct ixgbe_fcoe *fcoe,
6088c2ecf20Sopenharmony_ci				     struct device *dev,
6098c2ecf20Sopenharmony_ci				     unsigned int cpu)
6108c2ecf20Sopenharmony_ci{
6118c2ecf20Sopenharmony_ci	struct ixgbe_fcoe_ddp_pool *ddp_pool;
6128c2ecf20Sopenharmony_ci	struct dma_pool *pool;
6138c2ecf20Sopenharmony_ci	char pool_name[32];
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	snprintf(pool_name, 32, "ixgbe_fcoe_ddp_%u", cpu);
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	pool = dma_pool_create(pool_name, dev, IXGBE_FCPTR_MAX,
6188c2ecf20Sopenharmony_ci			       IXGBE_FCPTR_ALIGN, PAGE_SIZE);
6198c2ecf20Sopenharmony_ci	if (!pool)
6208c2ecf20Sopenharmony_ci		return -ENOMEM;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	ddp_pool = per_cpu_ptr(fcoe->ddp_pool, cpu);
6238c2ecf20Sopenharmony_ci	ddp_pool->pool = pool;
6248c2ecf20Sopenharmony_ci	ddp_pool->noddp = 0;
6258c2ecf20Sopenharmony_ci	ddp_pool->noddp_ext_buff = 0;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	return 0;
6288c2ecf20Sopenharmony_ci}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci/**
6318c2ecf20Sopenharmony_ci * ixgbe_configure_fcoe - configures registers for fcoe at start
6328c2ecf20Sopenharmony_ci * @adapter: ptr to ixgbe adapter
6338c2ecf20Sopenharmony_ci *
6348c2ecf20Sopenharmony_ci * This sets up FCoE related registers
6358c2ecf20Sopenharmony_ci *
6368c2ecf20Sopenharmony_ci * Returns : none
6378c2ecf20Sopenharmony_ci */
6388c2ecf20Sopenharmony_civoid ixgbe_configure_fcoe(struct ixgbe_adapter *adapter)
6398c2ecf20Sopenharmony_ci{
6408c2ecf20Sopenharmony_ci	struct ixgbe_ring_feature *fcoe = &adapter->ring_feature[RING_F_FCOE];
6418c2ecf20Sopenharmony_ci	struct ixgbe_hw *hw = &adapter->hw;
6428c2ecf20Sopenharmony_ci	int i, fcoe_q, fcoe_i, fcoe_q_h = 0;
6438c2ecf20Sopenharmony_ci	int fcreta_size;
6448c2ecf20Sopenharmony_ci	u32 etqf;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	/* Minimal functionality for FCoE requires at least CRC offloads */
6478c2ecf20Sopenharmony_ci	if (!(adapter->netdev->features & NETIF_F_FCOE_CRC))
6488c2ecf20Sopenharmony_ci		return;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	/* Enable L2 EtherType filter for FCoE, needed for FCoE CRC and DDP */
6518c2ecf20Sopenharmony_ci	etqf = ETH_P_FCOE | IXGBE_ETQF_FCOE | IXGBE_ETQF_FILTER_EN;
6528c2ecf20Sopenharmony_ci	if (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED) {
6538c2ecf20Sopenharmony_ci		etqf |= IXGBE_ETQF_POOL_ENABLE;
6548c2ecf20Sopenharmony_ci		etqf |= VMDQ_P(0) << IXGBE_ETQF_POOL_SHIFT;
6558c2ecf20Sopenharmony_ci	}
6568c2ecf20Sopenharmony_ci	IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_FCOE), etqf);
6578c2ecf20Sopenharmony_ci	IXGBE_WRITE_REG(hw, IXGBE_ETQS(IXGBE_ETQF_FILTER_FCOE), 0);
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	/* leave registers un-configured if FCoE is disabled */
6608c2ecf20Sopenharmony_ci	if (!(adapter->flags & IXGBE_FLAG_FCOE_ENABLED))
6618c2ecf20Sopenharmony_ci		return;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	/* Use one or more Rx queues for FCoE by redirection table */
6648c2ecf20Sopenharmony_ci	fcreta_size = IXGBE_FCRETA_SIZE;
6658c2ecf20Sopenharmony_ci	if (adapter->hw.mac.type == ixgbe_mac_X550)
6668c2ecf20Sopenharmony_ci		fcreta_size = IXGBE_FCRETA_SIZE_X550;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	for (i = 0; i < fcreta_size; i++) {
6698c2ecf20Sopenharmony_ci		if (adapter->hw.mac.type == ixgbe_mac_X550) {
6708c2ecf20Sopenharmony_ci			int fcoe_i_h = fcoe->offset + ((i + fcreta_size) %
6718c2ecf20Sopenharmony_ci							fcoe->indices);
6728c2ecf20Sopenharmony_ci			fcoe_q_h = adapter->rx_ring[fcoe_i_h]->reg_idx;
6738c2ecf20Sopenharmony_ci			fcoe_q_h = (fcoe_q_h << IXGBE_FCRETA_ENTRY_HIGH_SHIFT) &
6748c2ecf20Sopenharmony_ci				   IXGBE_FCRETA_ENTRY_HIGH_MASK;
6758c2ecf20Sopenharmony_ci		}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci		fcoe_i = fcoe->offset + (i % fcoe->indices);
6788c2ecf20Sopenharmony_ci		fcoe_i &= IXGBE_FCRETA_ENTRY_MASK;
6798c2ecf20Sopenharmony_ci		fcoe_q = adapter->rx_ring[fcoe_i]->reg_idx;
6808c2ecf20Sopenharmony_ci		fcoe_q |= fcoe_q_h;
6818c2ecf20Sopenharmony_ci		IXGBE_WRITE_REG(hw, IXGBE_FCRETA(i), fcoe_q);
6828c2ecf20Sopenharmony_ci	}
6838c2ecf20Sopenharmony_ci	IXGBE_WRITE_REG(hw, IXGBE_FCRECTL, IXGBE_FCRECTL_ENA);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	/* Enable L2 EtherType filter for FIP */
6868c2ecf20Sopenharmony_ci	etqf = ETH_P_FIP | IXGBE_ETQF_FILTER_EN;
6878c2ecf20Sopenharmony_ci	if (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED) {
6888c2ecf20Sopenharmony_ci		etqf |= IXGBE_ETQF_POOL_ENABLE;
6898c2ecf20Sopenharmony_ci		etqf |= VMDQ_P(0) << IXGBE_ETQF_POOL_SHIFT;
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci	IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_FIP), etqf);
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	/* Send FIP frames to the first FCoE queue */
6948c2ecf20Sopenharmony_ci	fcoe_q = adapter->rx_ring[fcoe->offset]->reg_idx;
6958c2ecf20Sopenharmony_ci	IXGBE_WRITE_REG(hw, IXGBE_ETQS(IXGBE_ETQF_FILTER_FIP),
6968c2ecf20Sopenharmony_ci			IXGBE_ETQS_QUEUE_EN |
6978c2ecf20Sopenharmony_ci			(fcoe_q << IXGBE_ETQS_RX_QUEUE_SHIFT));
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	/* Configure FCoE Rx control */
7008c2ecf20Sopenharmony_ci	IXGBE_WRITE_REG(hw, IXGBE_FCRXCTRL,
7018c2ecf20Sopenharmony_ci			IXGBE_FCRXCTRL_FCCRCBO |
7028c2ecf20Sopenharmony_ci			(FC_FCOE_VER << IXGBE_FCRXCTRL_FCOEVER_SHIFT));
7038c2ecf20Sopenharmony_ci}
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci/**
7068c2ecf20Sopenharmony_ci * ixgbe_free_fcoe_ddp_resources - release all fcoe ddp context resources
7078c2ecf20Sopenharmony_ci * @adapter : ixgbe adapter
7088c2ecf20Sopenharmony_ci *
7098c2ecf20Sopenharmony_ci * Cleans up outstanding ddp context resources
7108c2ecf20Sopenharmony_ci *
7118c2ecf20Sopenharmony_ci * Returns : none
7128c2ecf20Sopenharmony_ci */
7138c2ecf20Sopenharmony_civoid ixgbe_free_fcoe_ddp_resources(struct ixgbe_adapter *adapter)
7148c2ecf20Sopenharmony_ci{
7158c2ecf20Sopenharmony_ci	struct ixgbe_fcoe *fcoe = &adapter->fcoe;
7168c2ecf20Sopenharmony_ci	int cpu, i, ddp_max;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	/* do nothing if no DDP pools were allocated */
7198c2ecf20Sopenharmony_ci	if (!fcoe->ddp_pool)
7208c2ecf20Sopenharmony_ci		return;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	ddp_max = IXGBE_FCOE_DDP_MAX;
7238c2ecf20Sopenharmony_ci	/* X550 has different DDP Max limit */
7248c2ecf20Sopenharmony_ci	if (adapter->hw.mac.type == ixgbe_mac_X550)
7258c2ecf20Sopenharmony_ci		ddp_max = IXGBE_FCOE_DDP_MAX_X550;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	for (i = 0; i < ddp_max; i++)
7288c2ecf20Sopenharmony_ci		ixgbe_fcoe_ddp_put(adapter->netdev, i);
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu)
7318c2ecf20Sopenharmony_ci		ixgbe_fcoe_dma_pool_free(fcoe, cpu);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	dma_unmap_single(&adapter->pdev->dev,
7348c2ecf20Sopenharmony_ci			 fcoe->extra_ddp_buffer_dma,
7358c2ecf20Sopenharmony_ci			 IXGBE_FCBUFF_MIN,
7368c2ecf20Sopenharmony_ci			 DMA_FROM_DEVICE);
7378c2ecf20Sopenharmony_ci	kfree(fcoe->extra_ddp_buffer);
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	fcoe->extra_ddp_buffer = NULL;
7408c2ecf20Sopenharmony_ci	fcoe->extra_ddp_buffer_dma = 0;
7418c2ecf20Sopenharmony_ci}
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci/**
7448c2ecf20Sopenharmony_ci * ixgbe_setup_fcoe_ddp_resources - setup all fcoe ddp context resources
7458c2ecf20Sopenharmony_ci * @adapter: ixgbe adapter
7468c2ecf20Sopenharmony_ci *
7478c2ecf20Sopenharmony_ci * Sets up ddp context resouces
7488c2ecf20Sopenharmony_ci *
7498c2ecf20Sopenharmony_ci * Returns : 0 indicates success or -EINVAL on failure
7508c2ecf20Sopenharmony_ci */
7518c2ecf20Sopenharmony_ciint ixgbe_setup_fcoe_ddp_resources(struct ixgbe_adapter *adapter)
7528c2ecf20Sopenharmony_ci{
7538c2ecf20Sopenharmony_ci	struct ixgbe_fcoe *fcoe = &adapter->fcoe;
7548c2ecf20Sopenharmony_ci	struct device *dev = &adapter->pdev->dev;
7558c2ecf20Sopenharmony_ci	void *buffer;
7568c2ecf20Sopenharmony_ci	dma_addr_t dma;
7578c2ecf20Sopenharmony_ci	unsigned int cpu;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	/* do nothing if no DDP pools were allocated */
7608c2ecf20Sopenharmony_ci	if (!fcoe->ddp_pool)
7618c2ecf20Sopenharmony_ci		return 0;
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	/* Extra buffer to be shared by all DDPs for HW work around */
7648c2ecf20Sopenharmony_ci	buffer = kmalloc(IXGBE_FCBUFF_MIN, GFP_KERNEL);
7658c2ecf20Sopenharmony_ci	if (!buffer)
7668c2ecf20Sopenharmony_ci		return -ENOMEM;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	dma = dma_map_single(dev, buffer, IXGBE_FCBUFF_MIN, DMA_FROM_DEVICE);
7698c2ecf20Sopenharmony_ci	if (dma_mapping_error(dev, dma)) {
7708c2ecf20Sopenharmony_ci		e_err(drv, "failed to map extra DDP buffer\n");
7718c2ecf20Sopenharmony_ci		kfree(buffer);
7728c2ecf20Sopenharmony_ci		return -ENOMEM;
7738c2ecf20Sopenharmony_ci	}
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	fcoe->extra_ddp_buffer = buffer;
7768c2ecf20Sopenharmony_ci	fcoe->extra_ddp_buffer_dma = dma;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	/* allocate pci pool for each cpu */
7798c2ecf20Sopenharmony_ci	for_each_possible_cpu(cpu) {
7808c2ecf20Sopenharmony_ci		int err = ixgbe_fcoe_dma_pool_alloc(fcoe, dev, cpu);
7818c2ecf20Sopenharmony_ci		if (!err)
7828c2ecf20Sopenharmony_ci			continue;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci		e_err(drv, "failed to alloc DDP pool on cpu:%d\n", cpu);
7858c2ecf20Sopenharmony_ci		ixgbe_free_fcoe_ddp_resources(adapter);
7868c2ecf20Sopenharmony_ci		return -ENOMEM;
7878c2ecf20Sopenharmony_ci	}
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	return 0;
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_cistatic int ixgbe_fcoe_ddp_enable(struct ixgbe_adapter *adapter)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	struct ixgbe_fcoe *fcoe = &adapter->fcoe;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	if (!(adapter->flags & IXGBE_FLAG_FCOE_CAPABLE))
7978c2ecf20Sopenharmony_ci		return -EINVAL;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	fcoe->ddp_pool = alloc_percpu(struct ixgbe_fcoe_ddp_pool);
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	if (!fcoe->ddp_pool) {
8028c2ecf20Sopenharmony_ci		e_err(drv, "failed to allocate percpu DDP resources\n");
8038c2ecf20Sopenharmony_ci		return -ENOMEM;
8048c2ecf20Sopenharmony_ci	}
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	adapter->netdev->fcoe_ddp_xid = IXGBE_FCOE_DDP_MAX - 1;
8078c2ecf20Sopenharmony_ci	/* X550 has different DDP Max limit */
8088c2ecf20Sopenharmony_ci	if (adapter->hw.mac.type == ixgbe_mac_X550)
8098c2ecf20Sopenharmony_ci		adapter->netdev->fcoe_ddp_xid = IXGBE_FCOE_DDP_MAX_X550 - 1;
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	return 0;
8128c2ecf20Sopenharmony_ci}
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_cistatic void ixgbe_fcoe_ddp_disable(struct ixgbe_adapter *adapter)
8158c2ecf20Sopenharmony_ci{
8168c2ecf20Sopenharmony_ci	struct ixgbe_fcoe *fcoe = &adapter->fcoe;
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	adapter->netdev->fcoe_ddp_xid = 0;
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	if (!fcoe->ddp_pool)
8218c2ecf20Sopenharmony_ci		return;
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	free_percpu(fcoe->ddp_pool);
8248c2ecf20Sopenharmony_ci	fcoe->ddp_pool = NULL;
8258c2ecf20Sopenharmony_ci}
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci/**
8288c2ecf20Sopenharmony_ci * ixgbe_fcoe_enable - turn on FCoE offload feature
8298c2ecf20Sopenharmony_ci * @netdev: the corresponding netdev
8308c2ecf20Sopenharmony_ci *
8318c2ecf20Sopenharmony_ci * Turns on FCoE offload feature in 82599.
8328c2ecf20Sopenharmony_ci *
8338c2ecf20Sopenharmony_ci * Returns : 0 indicates success or -EINVAL on failure
8348c2ecf20Sopenharmony_ci */
8358c2ecf20Sopenharmony_ciint ixgbe_fcoe_enable(struct net_device *netdev)
8368c2ecf20Sopenharmony_ci{
8378c2ecf20Sopenharmony_ci	struct ixgbe_adapter *adapter = netdev_priv(netdev);
8388c2ecf20Sopenharmony_ci	struct ixgbe_fcoe *fcoe = &adapter->fcoe;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	atomic_inc(&fcoe->refcnt);
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	if (!(adapter->flags & IXGBE_FLAG_FCOE_CAPABLE))
8438c2ecf20Sopenharmony_ci		return -EINVAL;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	if (adapter->flags & IXGBE_FLAG_FCOE_ENABLED)
8468c2ecf20Sopenharmony_ci		return -EINVAL;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	e_info(drv, "Enabling FCoE offload features.\n");
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	if (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED)
8518c2ecf20Sopenharmony_ci		e_warn(probe, "Enabling FCoE on PF will disable legacy VFs\n");
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	if (netif_running(netdev))
8548c2ecf20Sopenharmony_ci		netdev->netdev_ops->ndo_stop(netdev);
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	/* Allocate per CPU memory to track DDP pools */
8578c2ecf20Sopenharmony_ci	ixgbe_fcoe_ddp_enable(adapter);
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	/* enable FCoE and notify stack */
8608c2ecf20Sopenharmony_ci	adapter->flags |= IXGBE_FLAG_FCOE_ENABLED;
8618c2ecf20Sopenharmony_ci	netdev->features |= NETIF_F_FCOE_MTU;
8628c2ecf20Sopenharmony_ci	netdev_features_change(netdev);
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	/* release existing queues and reallocate them */
8658c2ecf20Sopenharmony_ci	ixgbe_clear_interrupt_scheme(adapter);
8668c2ecf20Sopenharmony_ci	ixgbe_init_interrupt_scheme(adapter);
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	if (netif_running(netdev))
8698c2ecf20Sopenharmony_ci		netdev->netdev_ops->ndo_open(netdev);
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	return 0;
8728c2ecf20Sopenharmony_ci}
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci/**
8758c2ecf20Sopenharmony_ci * ixgbe_fcoe_disable - turn off FCoE offload feature
8768c2ecf20Sopenharmony_ci * @netdev: the corresponding netdev
8778c2ecf20Sopenharmony_ci *
8788c2ecf20Sopenharmony_ci * Turns off FCoE offload feature in 82599.
8798c2ecf20Sopenharmony_ci *
8808c2ecf20Sopenharmony_ci * Returns : 0 indicates success or -EINVAL on failure
8818c2ecf20Sopenharmony_ci */
8828c2ecf20Sopenharmony_ciint ixgbe_fcoe_disable(struct net_device *netdev)
8838c2ecf20Sopenharmony_ci{
8848c2ecf20Sopenharmony_ci	struct ixgbe_adapter *adapter = netdev_priv(netdev);
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	if (!atomic_dec_and_test(&adapter->fcoe.refcnt))
8878c2ecf20Sopenharmony_ci		return -EINVAL;
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	if (!(adapter->flags & IXGBE_FLAG_FCOE_ENABLED))
8908c2ecf20Sopenharmony_ci		return -EINVAL;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	e_info(drv, "Disabling FCoE offload features.\n");
8938c2ecf20Sopenharmony_ci	if (netif_running(netdev))
8948c2ecf20Sopenharmony_ci		netdev->netdev_ops->ndo_stop(netdev);
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	/* Free per CPU memory to track DDP pools */
8978c2ecf20Sopenharmony_ci	ixgbe_fcoe_ddp_disable(adapter);
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	/* disable FCoE and notify stack */
9008c2ecf20Sopenharmony_ci	adapter->flags &= ~IXGBE_FLAG_FCOE_ENABLED;
9018c2ecf20Sopenharmony_ci	netdev->features &= ~NETIF_F_FCOE_MTU;
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	netdev_features_change(netdev);
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	/* release existing queues and reallocate them */
9068c2ecf20Sopenharmony_ci	ixgbe_clear_interrupt_scheme(adapter);
9078c2ecf20Sopenharmony_ci	ixgbe_init_interrupt_scheme(adapter);
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	if (netif_running(netdev))
9108c2ecf20Sopenharmony_ci		netdev->netdev_ops->ndo_open(netdev);
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	return 0;
9138c2ecf20Sopenharmony_ci}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci/**
9168c2ecf20Sopenharmony_ci * ixgbe_fcoe_get_wwn - get world wide name for the node or the port
9178c2ecf20Sopenharmony_ci * @netdev : ixgbe adapter
9188c2ecf20Sopenharmony_ci * @wwn : the world wide name
9198c2ecf20Sopenharmony_ci * @type: the type of world wide name
9208c2ecf20Sopenharmony_ci *
9218c2ecf20Sopenharmony_ci * Returns the node or port world wide name if both the prefix and the san
9228c2ecf20Sopenharmony_ci * mac address are valid, then the wwn is formed based on the NAA-2 for
9238c2ecf20Sopenharmony_ci * IEEE Extended name identifier (ref. to T10 FC-LS Spec., Sec. 15.3).
9248c2ecf20Sopenharmony_ci *
9258c2ecf20Sopenharmony_ci * Returns : 0 on success
9268c2ecf20Sopenharmony_ci */
9278c2ecf20Sopenharmony_ciint ixgbe_fcoe_get_wwn(struct net_device *netdev, u64 *wwn, int type)
9288c2ecf20Sopenharmony_ci{
9298c2ecf20Sopenharmony_ci	u16 prefix = 0xffff;
9308c2ecf20Sopenharmony_ci	struct ixgbe_adapter *adapter = netdev_priv(netdev);
9318c2ecf20Sopenharmony_ci	struct ixgbe_mac_info *mac = &adapter->hw.mac;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	switch (type) {
9348c2ecf20Sopenharmony_ci	case NETDEV_FCOE_WWNN:
9358c2ecf20Sopenharmony_ci		prefix = mac->wwnn_prefix;
9368c2ecf20Sopenharmony_ci		break;
9378c2ecf20Sopenharmony_ci	case NETDEV_FCOE_WWPN:
9388c2ecf20Sopenharmony_ci		prefix = mac->wwpn_prefix;
9398c2ecf20Sopenharmony_ci		break;
9408c2ecf20Sopenharmony_ci	default:
9418c2ecf20Sopenharmony_ci		break;
9428c2ecf20Sopenharmony_ci	}
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	if ((prefix != 0xffff) &&
9458c2ecf20Sopenharmony_ci	    is_valid_ether_addr(mac->san_addr)) {
9468c2ecf20Sopenharmony_ci		*wwn = ((u64) prefix << 48) |
9478c2ecf20Sopenharmony_ci		       ((u64) mac->san_addr[0] << 40) |
9488c2ecf20Sopenharmony_ci		       ((u64) mac->san_addr[1] << 32) |
9498c2ecf20Sopenharmony_ci		       ((u64) mac->san_addr[2] << 24) |
9508c2ecf20Sopenharmony_ci		       ((u64) mac->san_addr[3] << 16) |
9518c2ecf20Sopenharmony_ci		       ((u64) mac->san_addr[4] << 8)  |
9528c2ecf20Sopenharmony_ci		       ((u64) mac->san_addr[5]);
9538c2ecf20Sopenharmony_ci		return 0;
9548c2ecf20Sopenharmony_ci	}
9558c2ecf20Sopenharmony_ci	return -EINVAL;
9568c2ecf20Sopenharmony_ci}
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci/**
9598c2ecf20Sopenharmony_ci * ixgbe_fcoe_get_hbainfo - get FCoE HBA information
9608c2ecf20Sopenharmony_ci * @netdev : ixgbe adapter
9618c2ecf20Sopenharmony_ci * @info : HBA information
9628c2ecf20Sopenharmony_ci *
9638c2ecf20Sopenharmony_ci * Returns ixgbe HBA information
9648c2ecf20Sopenharmony_ci *
9658c2ecf20Sopenharmony_ci * Returns : 0 on success
9668c2ecf20Sopenharmony_ci */
9678c2ecf20Sopenharmony_ciint ixgbe_fcoe_get_hbainfo(struct net_device *netdev,
9688c2ecf20Sopenharmony_ci			   struct netdev_fcoe_hbainfo *info)
9698c2ecf20Sopenharmony_ci{
9708c2ecf20Sopenharmony_ci	struct ixgbe_adapter *adapter = netdev_priv(netdev);
9718c2ecf20Sopenharmony_ci	struct ixgbe_hw *hw = &adapter->hw;
9728c2ecf20Sopenharmony_ci	u64 dsn;
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	if (!info)
9758c2ecf20Sopenharmony_ci		return -EINVAL;
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	/* Don't return information on unsupported devices */
9788c2ecf20Sopenharmony_ci	if (!(adapter->flags & IXGBE_FLAG_FCOE_ENABLED))
9798c2ecf20Sopenharmony_ci		return -EINVAL;
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	/* Manufacturer */
9828c2ecf20Sopenharmony_ci	snprintf(info->manufacturer, sizeof(info->manufacturer),
9838c2ecf20Sopenharmony_ci		 "Intel Corporation");
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	/* Serial Number */
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	/* Get the PCI-e Device Serial Number Capability */
9888c2ecf20Sopenharmony_ci	dsn = pci_get_dsn(adapter->pdev);
9898c2ecf20Sopenharmony_ci	if (dsn)
9908c2ecf20Sopenharmony_ci		snprintf(info->serial_number, sizeof(info->serial_number),
9918c2ecf20Sopenharmony_ci			 "%016llX", dsn);
9928c2ecf20Sopenharmony_ci	else
9938c2ecf20Sopenharmony_ci		snprintf(info->serial_number, sizeof(info->serial_number),
9948c2ecf20Sopenharmony_ci			 "Unknown");
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	/* Hardware Version */
9978c2ecf20Sopenharmony_ci	snprintf(info->hardware_version,
9988c2ecf20Sopenharmony_ci		 sizeof(info->hardware_version),
9998c2ecf20Sopenharmony_ci		 "Rev %d", hw->revision_id);
10008c2ecf20Sopenharmony_ci	/* Driver Name/Version */
10018c2ecf20Sopenharmony_ci	snprintf(info->driver_version,
10028c2ecf20Sopenharmony_ci		 sizeof(info->driver_version),
10038c2ecf20Sopenharmony_ci		 "%s v%s",
10048c2ecf20Sopenharmony_ci		 ixgbe_driver_name,
10058c2ecf20Sopenharmony_ci		 UTS_RELEASE);
10068c2ecf20Sopenharmony_ci	/* Firmware Version */
10078c2ecf20Sopenharmony_ci	strlcpy(info->firmware_version, adapter->eeprom_id,
10088c2ecf20Sopenharmony_ci		sizeof(info->firmware_version));
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	/* Model */
10118c2ecf20Sopenharmony_ci	if (hw->mac.type == ixgbe_mac_82599EB) {
10128c2ecf20Sopenharmony_ci		snprintf(info->model,
10138c2ecf20Sopenharmony_ci			 sizeof(info->model),
10148c2ecf20Sopenharmony_ci			 "Intel 82599");
10158c2ecf20Sopenharmony_ci	} else if (hw->mac.type == ixgbe_mac_X550) {
10168c2ecf20Sopenharmony_ci		snprintf(info->model,
10178c2ecf20Sopenharmony_ci			 sizeof(info->model),
10188c2ecf20Sopenharmony_ci			 "Intel X550");
10198c2ecf20Sopenharmony_ci	} else {
10208c2ecf20Sopenharmony_ci		snprintf(info->model,
10218c2ecf20Sopenharmony_ci			 sizeof(info->model),
10228c2ecf20Sopenharmony_ci			 "Intel X540");
10238c2ecf20Sopenharmony_ci	}
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	/* Model Description */
10268c2ecf20Sopenharmony_ci	snprintf(info->model_description,
10278c2ecf20Sopenharmony_ci		 sizeof(info->model_description),
10288c2ecf20Sopenharmony_ci		 "%s",
10298c2ecf20Sopenharmony_ci		 ixgbe_default_device_descr);
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	return 0;
10328c2ecf20Sopenharmony_ci}
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci/**
10358c2ecf20Sopenharmony_ci * ixgbe_fcoe_get_tc - get the current TC that fcoe is mapped to
10368c2ecf20Sopenharmony_ci * @adapter: pointer to the device adapter structure
10378c2ecf20Sopenharmony_ci *
10388c2ecf20Sopenharmony_ci * Return : TC that FCoE is mapped to
10398c2ecf20Sopenharmony_ci */
10408c2ecf20Sopenharmony_ciu8 ixgbe_fcoe_get_tc(struct ixgbe_adapter *adapter)
10418c2ecf20Sopenharmony_ci{
10428c2ecf20Sopenharmony_ci#ifdef CONFIG_IXGBE_DCB
10438c2ecf20Sopenharmony_ci	return netdev_get_prio_tc_map(adapter->netdev, adapter->fcoe.up);
10448c2ecf20Sopenharmony_ci#else
10458c2ecf20Sopenharmony_ci	return 0;
10468c2ecf20Sopenharmony_ci#endif
10478c2ecf20Sopenharmony_ci}
1048