/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
Copyright(c) 1999 - 2014 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Contact Information:
Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*******************************************************************************/
#include "ixgbe.h"
#include <linux/if_ether.h>
#include <linux/gfp.h>
#include <linux/if_vlan.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/fc/fc_fs.h>
#include <scsi/fc/fc_fcoe.h>
#include <scsi/libfc.h>
#include <scsi/libfcoe.h>
/**
* ixgbe_fcoe_clear_ddp - clear the given ddp context
* @ddp: ptr to the ixgbe_fcoe_ddp
*
* Returns : none
*
*/
static inline void ixgbe_fcoe_clear_ddp(struct ixgbe_fcoe_ddp *ddp)
{
ddp->len = 0;
ddp->err = 1;
ddp->udl = NULL;
ddp->udp = 0UL;
ddp->sgl = NULL;
ddp->sgc = 0;
}
/**
* ixgbe_fcoe_ddp_put - free the ddp context for a given xid
* @netdev: the corresponding net_device
* @xid: the xid that corresponding ddp will be freed
*
* This is the implementation of net_device_ops.ndo_fcoe_ddp_done
* and it is expected to be called by ULD, i.e., FCP layer of libfc
* to release the corresponding ddp context when the I/O is done.
*
* Returns : data length already ddp-ed in bytes
*/
int ixgbe_fcoe_ddp_put(struct net_device *netdev, u16 xid)
{
int len;
struct ixgbe_fcoe *fcoe;
struct ixgbe_adapter *adapter;
struct ixgbe_fcoe_ddp *ddp;
struct ixgbe_hw *hw;
u32 fcbuff;
if (!netdev)
return 0;
if (xid >= netdev->fcoe_ddp_xid)
return 0;
adapter = netdev_priv(netdev);
fcoe = &adapter->fcoe;
ddp = &fcoe->ddp[xid];
if (!ddp->udl)
return 0;
hw = &adapter->hw;
len = ddp->len;
/* if no error then skip ddp context invalidation */
if (!ddp->err)
goto skip_ddpinv;
if (hw->mac.type == ixgbe_mac_X550) {
/* X550 does not require DDP FCoE lock */
IXGBE_WRITE_REG(hw, IXGBE_FCDFC(0, xid), 0);
IXGBE_WRITE_REG(hw, IXGBE_FCDFC(3, xid),
(xid | IXGBE_FCFLTRW_WE));
/* program FCBUFF */
IXGBE_WRITE_REG(hw, IXGBE_FCDDC(2, xid), 0);
/* program FCDMARW */
IXGBE_WRITE_REG(hw, IXGBE_FCDDC(3, xid),
(xid | IXGBE_FCDMARW_WE));
/* read FCBUFF to check context invalidated */
IXGBE_WRITE_REG(hw, IXGBE_FCDDC(3, xid),
(xid | IXGBE_FCDMARW_RE));
fcbuff = IXGBE_READ_REG(hw, IXGBE_FCDDC(2, xid));
} else {
/* other hardware requires DDP FCoE lock */
spin_lock_bh(&fcoe->lock);
IXGBE_WRITE_REG(hw, IXGBE_FCFLT, 0);
IXGBE_WRITE_REG(hw, IXGBE_FCFLTRW,
(xid | IXGBE_FCFLTRW_WE));
IXGBE_WRITE_REG(hw, IXGBE_FCBUFF, 0);
IXGBE_WRITE_REG(hw, IXGBE_FCDMARW,
(xid | IXGBE_FCDMARW_WE));
/* guaranteed to be invalidated after 100us */
IXGBE_WRITE_REG(hw, IXGBE_FCDMARW,
(xid | IXGBE_FCDMARW_RE));
fcbuff = IXGBE_READ_REG(hw, IXGBE_FCBUFF);
spin_unlock_bh(&fcoe->lock);
}
if (fcbuff & IXGBE_FCBUFF_VALID)
usleep_range(100, 150);
skip_ddpinv:
if (ddp->sgl)
dma_unmap_sg(&adapter->pdev->dev, ddp->sgl, ddp->sgc,
DMA_FROM_DEVICE);
if (ddp->pool) {
dma_pool_free(ddp->pool, ddp->udl, ddp->udp);
ddp->pool = NULL;
}
ixgbe_fcoe_clear_ddp(ddp);
return len;
}
/**