// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 1999 - 2018 Intel Corporation. */
#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;
}
/**
* ixgbe_fcoe_ddp_setup - called to set up ddp context
* @netdev: the corresponding net_device
* @xid: the exchange id requesting ddp
* @sgl: the scatter-gather list for this request
* @sgc: the number of scatter-gather items
* @target_mode: 1 to setup target mode, 0 to setup initiator mode
*
* Returns : 1 for success and 0 for no ddp
*/
static int ixgbe_fcoe_ddp_setup(struct net_device *netdev, u16 xid,
struct scatterlist *sgl, unsigned int sgc,
int target_mode)
{
struct ixgbe_adapter *adapter;
struct ixgbe_hw *hw;
struct ixgbe_fcoe *fcoe;
struct ixgbe_fcoe