blob: 18fb9c61d7bacfd19319a664d2287786938ecec7 [file] [log] [blame]
/*
* This file is part of the Chelsio T4 Ethernet driver for Linux.
*
* Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bitmap.h>
#include <linux/crc32.h>
#include <linux/ctype.h>
#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/etherdevice.h>
#include <linux/firmware.h>
#include <linux/if.h>
#include <linux/if_vlan.h>
#include <linux/init.h>
#include <linux/log2.h>
#include <linux/mdio.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mutex.h>
#include <linux/netdevice.h>
#include <linux/pci.h>
#include <linux/aer.h>
#include <linux/rtnetlink.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/sockios.h>
#include <linux/vmalloc.h>
#include <linux/workqueue.h>
#include <net/neighbour.h>
#include <net/netevent.h>
#include <net/addrconf.h>
#include <asm/uaccess.h>
#include "cxgb4.h"
#include "t4_regs.h"
#include "t4_msg.h"
#include "t4fw_api.h"
#include "cxgb4_dcb.h"
#include "l2t.h"
#include <../drivers/net/bonding/bonding.h>
#ifdef DRV_VERSION
#undef DRV_VERSION
#endif
#define DRV_VERSION "2.0.0-ko"
#define DRV_DESC "Chelsio T4/T5 Network Driver"
/*
* Max interrupt hold-off timer value in us. Queues fall back to this value
* under extreme memory pressure so it's largish to give the system time to
* recover.
*/
#define MAX_SGE_TIMERVAL 200U
enum {
/*
* Physical Function provisioning constants.
*/
PFRES_NVI = 4, /* # of Virtual Interfaces */
PFRES_NETHCTRL = 128, /* # of EQs used for ETH or CTRL Qs */
PFRES_NIQFLINT = 128, /* # of ingress Qs/w Free List(s)/intr
*/
PFRES_NEQ = 256, /* # of egress queues */
PFRES_NIQ = 0, /* # of ingress queues */
PFRES_TC = 0, /* PCI-E traffic class */
PFRES_NEXACTF = 128, /* # of exact MPS filters */
PFRES_R_CAPS = FW_CMD_CAP_PF,
PFRES_WX_CAPS = FW_CMD_CAP_PF,
#ifdef CONFIG_PCI_IOV
/*
* Virtual Function provisioning constants. We need two extra Ingress
* Queues with Interrupt capability to serve as the VF's Firmware
* Event Queue and Forwarded Interrupt Queue (when using MSI mode) --
* neither will have Free Lists associated with them). For each
* Ethernet/Control Egress Queue and for each Free List, we need an
* Egress Context.
*/
VFRES_NPORTS = 1, /* # of "ports" per VF */
VFRES_NQSETS = 2, /* # of "Queue Sets" per VF */
VFRES_NVI = VFRES_NPORTS, /* # of Virtual Interfaces */
VFRES_NETHCTRL = VFRES_NQSETS, /* # of EQs used for ETH or CTRL Qs */
VFRES_NIQFLINT = VFRES_NQSETS+2,/* # of ingress Qs/w Free List(s)/intr */
VFRES_NEQ = VFRES_NQSETS*2, /* # of egress queues */
VFRES_NIQ = 0, /* # of non-fl/int ingress queues */
VFRES_TC = 0, /* PCI-E traffic class */
VFRES_NEXACTF = 16, /* # of exact MPS filters */
VFRES_R_CAPS = FW_CMD_CAP_DMAQ|FW_CMD_CAP_VF|FW_CMD_CAP_PORT,
VFRES_WX_CAPS = FW_CMD_CAP_DMAQ|FW_CMD_CAP_VF,
#endif
};
/*
* Provide a Port Access Rights Mask for the specified PF/VF. This is very
* static and likely not to be useful in the long run. We really need to
* implement some form of persistent configuration which the firmware
* controls.
*/
static unsigned int pfvfres_pmask(struct adapter *adapter,
unsigned int pf, unsigned int vf)
{
unsigned int portn, portvec;
/*
* Give PF's access to all of the ports.
*/
if (vf == 0)
return FW_PFVF_CMD_PMASK_MASK;
/*
* For VFs, we'll assign them access to the ports based purely on the
* PF. We assign active ports in order, wrapping around if there are
* fewer active ports than PFs: e.g. active port[pf % nports].
* Unfortunately the adapter's port_info structs haven't been
* initialized yet so we have to compute this.
*/
if (adapter->params.nports == 0)
return 0;
portn = pf % adapter->params.nports;
portvec = adapter->params.portvec;
for (;;) {
/*
* Isolate the lowest set bit in the port vector. If we're at
* the port number that we want, return that as the pmask.
* otherwise mask that bit out of the port vector and
* decrement our port number ...
*/
unsigned int pmask = portvec ^ (portvec & (portvec-1));
if (portn == 0)
return pmask;
portn--;
portvec &= ~pmask;
}
/*NOTREACHED*/
}
enum {
MAX_TXQ_ENTRIES = 16384,
MAX_CTRL_TXQ_ENTRIES = 1024,
MAX_RSPQ_ENTRIES = 16384,
MAX_RX_BUFFERS = 16384,
MIN_TXQ_ENTRIES = 32,
MIN_CTRL_TXQ_ENTRIES = 32,
MIN_RSPQ_ENTRIES = 128,
MIN_FL_ENTRIES = 16
};
/* Host shadow copy of ingress filter entry. This is in host native format
* and doesn't match the ordering or bit order, etc. of the hardware of the
* firmware command. The use of bit-field structure elements is purely to
* remind ourselves of the field size limitations and save memory in the case
* where the filter table is large.
*/
struct filter_entry {
/* Administrative fields for filter.
*/
u32 valid:1; /* filter allocated and valid */
u32 locked:1; /* filter is administratively locked */
u32 pending:1; /* filter action is pending firmware reply */
u32 smtidx:8; /* Source MAC Table index for smac */
struct l2t_entry *l2t; /* Layer Two Table entry for dmac */
/* The filter itself. Most of this is a straight copy of information
* provided by the extended ioctl(). Some fields are translated to
* internal forms -- for instance the Ingress Queue ID passed in from
* the ioctl() is translated into the Absolute Ingress Queue ID.
*/
struct ch_filter_specification fs;
};
#define DFLT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | \
NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP |\
NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR)
#define CH_DEVICE(devid, data) { PCI_VDEVICE(CHELSIO, devid), (data) }
static const struct pci_device_id cxgb4_pci_tbl[] = {
CH_DEVICE(0xa000, 0), /* PE10K */
CH_DEVICE(0x4001, -1),
CH_DEVICE(0x4002, -1),
CH_DEVICE(0x4003, -1),
CH_DEVICE(0x4004, -1),
CH_DEVICE(0x4005, -1),
CH_DEVICE(0x4006, -1),
CH_DEVICE(0x4007, -1),
CH_DEVICE(0x4008, -1),
CH_DEVICE(0x4009, -1),
CH_DEVICE(0x400a, -1),
CH_DEVICE(0x400d, -1),
CH_DEVICE(0x400e, -1),
CH_DEVICE(0x4080, -1),
CH_DEVICE(0x4081, -1),
CH_DEVICE(0x4082, -1),
CH_DEVICE(0x4083, -1),
CH_DEVICE(0x4084, -1),
CH_DEVICE(0x4085, -1),
CH_DEVICE(0x4086, -1),
CH_DEVICE(0x4087, -1),
CH_DEVICE(0x4088, -1),
CH_DEVICE(0x4401, 4),
CH_DEVICE(0x4402, 4),
CH_DEVICE(0x4403, 4),
CH_DEVICE(0x4404, 4),
CH_DEVICE(0x4405, 4),
CH_DEVICE(0x4406, 4),
CH_DEVICE(0x4407, 4),
CH_DEVICE(0x4408, 4),
CH_DEVICE(0x4409, 4),
CH_DEVICE(0x440a, 4),
CH_DEVICE(0x440d, 4),
CH_DEVICE(0x440e, 4),
CH_DEVICE(0x4480, 4),
CH_DEVICE(0x4481, 4),
CH_DEVICE(0x4482, 4),
CH_DEVICE(0x4483, 4),
CH_DEVICE(0x4484, 4),
CH_DEVICE(0x4485, 4),
CH_DEVICE(0x4486, 4),
CH_DEVICE(0x4487, 4),
CH_DEVICE(0x4488, 4),
CH_DEVICE(0x5001, 4),
CH_DEVICE(0x5002, 4),
CH_DEVICE(0x5003, 4),
CH_DEVICE(0x5004, 4),
CH_DEVICE(0x5005, 4),
CH_DEVICE(0x5006, 4),
CH_DEVICE(0x5007, 4),
CH_DEVICE(0x5008, 4),
CH_DEVICE(0x5009, 4),
CH_DEVICE(0x500A, 4),
CH_DEVICE(0x500B, 4),
CH_DEVICE(0x500C, 4),
CH_DEVICE(0x500D, 4),
CH_DEVICE(0x500E, 4),
CH_DEVICE(0x500F, 4),
CH_DEVICE(0x5010, 4),
CH_DEVICE(0x5011, 4),
CH_DEVICE(0x5012, 4),
CH_DEVICE(0x5013, 4),
CH_DEVICE(0x5014, 4),
CH_DEVICE(0x5015, 4),
CH_DEVICE(0x5080, 4),
CH_DEVICE(0x5081, 4),
CH_DEVICE(0x5082, 4),
CH_DEVICE(0x5083, 4),
CH_DEVICE(0x5084, 4),
CH_DEVICE(0x5085, 4),
CH_DEVICE(0x5401, 4),
CH_DEVICE(0x5402, 4),
CH_DEVICE(0x5403, 4),
CH_DEVICE(0x5404, 4),
CH_DEVICE(0x5405, 4),
CH_DEVICE(0x5406, 4),
CH_DEVICE(0x5407, 4),
CH_DEVICE(0x5408, 4),
CH_DEVICE(0x5409, 4),
CH_DEVICE(0x540A, 4),
CH_DEVICE(0x540B, 4),
CH_DEVICE(0x540C, 4),
CH_DEVICE(0x540D, 4),
CH_DEVICE(0x540E, 4),
CH_DEVICE(0x540F, 4),
CH_DEVICE(0x5410, 4),
CH_DEVICE(0x5411, 4),
CH_DEVICE(0x5412, 4),
CH_DEVICE(0x5413, 4),
CH_DEVICE(0x5414, 4),
CH_DEVICE(0x5415, 4),
CH_DEVICE(0x5480, 4),
CH_DEVICE(0x5481, 4),
CH_DEVICE(0x5482, 4),
CH_DEVICE(0x5483, 4),
CH_DEVICE(0x5484, 4),
CH_DEVICE(0x5485, 4),
{ 0, }
};
#define FW4_FNAME "cxgb4/t4fw.bin"
#define FW5_FNAME "cxgb4/t5fw.bin"
#define FW4_CFNAME "cxgb4/t4-config.txt"
#define FW5_CFNAME "cxgb4/t5-config.txt"
MODULE_DESCRIPTION(DRV_DESC);
MODULE_AUTHOR("Chelsio Communications");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_VERSION(DRV_VERSION);
MODULE_DEVICE_TABLE(pci, cxgb4_pci_tbl);
MODULE_FIRMWARE(FW4_FNAME);
MODULE_FIRMWARE(FW5_FNAME);
/*
* Normally we're willing to become the firmware's Master PF but will be happy
* if another PF has already become the Master and initialized the adapter.
* Setting "force_init" will cause this driver to forcibly establish itself as
* the Master PF and initialize the adapter.
*/
static uint force_init;
module_param(force_init, uint, 0644);
MODULE_PARM_DESC(force_init, "Forcibly become Master PF and initialize adapter");
/*
* Normally if the firmware we connect to has Configuration File support, we
* use that and only fall back to the old Driver-based initialization if the
* Configuration File fails for some reason. If force_old_init is set, then
* we'll always use the old Driver-based initialization sequence.
*/
static uint force_old_init;
module_param(force_old_init, uint, 0644);
MODULE_PARM_DESC(force_old_init, "Force old initialization sequence");
static int dflt_msg_enable = DFLT_MSG_ENABLE;
module_param(dflt_msg_enable, int, 0644);
MODULE_PARM_DESC(dflt_msg_enable, "Chelsio T4 default message enable bitmap");
/*
* The driver uses the best interrupt scheme available on a platform in the
* order MSI-X, MSI, legacy INTx interrupts. This parameter determines which
* of these schemes the driver may consider as follows:
*
* msi = 2: choose from among all three options
* msi = 1: only consider MSI and INTx interrupts
* msi = 0: force INTx interrupts
*/
static int msi = 2;
module_param(msi, int, 0644);
MODULE_PARM_DESC(msi, "whether to use INTx (0), MSI (1) or MSI-X (2)");
/*
* Queue interrupt hold-off timer values. Queues default to the first of these
* upon creation.
*/
static unsigned int intr_holdoff[SGE_NTIMERS - 1] = { 5, 10, 20, 50, 100 };
module_param_array(intr_holdoff, uint, NULL, 0644);
MODULE_PARM_DESC(intr_holdoff, "values for queue interrupt hold-off timers "
"0..4 in microseconds");
static unsigned int intr_cnt[SGE_NCOUNTERS - 1] = { 4, 8, 16 };
module_param_array(intr_cnt, uint, NULL, 0644);
MODULE_PARM_DESC(intr_cnt,
"thresholds 1..3 for queue interrupt packet counters");
/*
* Normally we tell the chip to deliver Ingress Packets into our DMA buffers
* offset by 2 bytes in order to have the IP headers line up on 4-byte
* boundaries. This is a requirement for many architectures which will throw
* a machine check fault if an attempt is made to access one of the 4-byte IP
* header fields on a non-4-byte boundary. And it's a major performance issue
* even on some architectures which allow it like some implementations of the
* x86 ISA. However, some architectures don't mind this and for some very
* edge-case performance sensitive applications (like forwarding large volumes
* of small packets), setting this DMA offset to 0 will decrease the number of
* PCI-E Bus transfers enough to measurably affect performance.
*/
static int rx_dma_offset = 2;
static bool vf_acls;
#ifdef CONFIG_PCI_IOV
module_param(vf_acls, bool, 0644);
MODULE_PARM_DESC(vf_acls, "if set enable virtualization L2 ACL enforcement");
/* Configure the number of PCI-E Virtual Function which are to be instantiated
* on SR-IOV Capable Physical Functions.
*/
static unsigned int num_vf[NUM_OF_PF_WITH_SRIOV];
module_param_array(num_vf, uint, NULL, 0644);
MODULE_PARM_DESC(num_vf, "number of VFs for each of PFs 0-3");
#endif
/* TX Queue select used to determine what algorithm to use for selecting TX
* queue. Select between the kernel provided function (select_queue=0) or user
* cxgb_select_queue function (select_queue=1)
*
* Default: select_queue=0
*/
static int select_queue;
module_param(select_queue, int, 0644);
MODULE_PARM_DESC(select_queue,
"Select between kernel provided method of selecting or driver method of selecting TX queue. Default is kernel method.");
/*
* The filter TCAM has a fixed portion and a variable portion. The fixed
* portion can match on source/destination IP IPv4/IPv6 addresses and TCP/UDP
* ports. The variable portion is 36 bits which can include things like Exact
* Match MAC Index (9 bits), Ether Type (16 bits), IP Protocol (8 bits),
* [Inner] VLAN Tag (17 bits), etc. which, if all were somehow selected, would
* far exceed the 36-bit budget for this "compressed" header portion of the
* filter. Thus, we have a scarce resource which must be carefully managed.
*
* By default we set this up to mostly match the set of filter matching
* capabilities of T3 but with accommodations for some of T4's more
* interesting features:
*
* { IP Fragment (1), MPS Match Type (3), IP Protocol (8),
* [Inner] VLAN (17), Port (3), FCoE (1) }
*/
enum {
TP_VLAN_PRI_MAP_DEFAULT = HW_TPL_FR_MT_PR_IV_P_FC,
TP_VLAN_PRI_MAP_FIRST = FCOE_SHIFT,
TP_VLAN_PRI_MAP_LAST = FRAGMENTATION_SHIFT,
};
static unsigned int tp_vlan_pri_map = TP_VLAN_PRI_MAP_DEFAULT;
module_param(tp_vlan_pri_map, uint, 0644);
MODULE_PARM_DESC(tp_vlan_pri_map, "global compressed filter configuration");
static struct dentry *cxgb4_debugfs_root;
static LIST_HEAD(adapter_list);
static DEFINE_MUTEX(uld_mutex);
/* Adapter list to be accessed from atomic context */
static LIST_HEAD(adap_rcu_list);
static DEFINE_SPINLOCK(adap_rcu_lock);
static struct cxgb4_uld_info ulds[CXGB4_ULD_MAX];
static const char *uld_str[] = { "RDMA", "iSCSI" };
static void link_report(struct net_device *dev)
{
if (!netif_carrier_ok(dev))
netdev_info(dev, "link down\n");
else {
static const char *fc[] = { "no", "Rx", "Tx", "Tx/Rx" };
const char *s = "10Mbps";
const struct port_info *p = netdev_priv(dev);
switch (p->link_cfg.speed) {
case 10000:
s = "10Gbps";
break;
case 1000:
s = "1000Mbps";
break;
case 100:
s = "100Mbps";
break;
case 40000:
s = "40Gbps";
break;
}
netdev_info(dev, "link up, %s, full-duplex, %s PAUSE\n", s,
fc[p->link_cfg.fc]);
}
}
#ifdef CONFIG_CHELSIO_T4_DCB
/* Set up/tear down Data Center Bridging Priority mapping for a net device. */
static void dcb_tx_queue_prio_enable(struct net_device *dev, int enable)
{
struct port_info *pi = netdev_priv(dev);
struct adapter *adap = pi->adapter;
struct sge_eth_txq *txq = &adap->sge.ethtxq[pi->first_qset];
int i;
/* We use a simple mapping of Port TX Queue Index to DCB
* Priority when we're enabling DCB.
*/
for (i = 0; i < pi->nqsets; i++, txq++) {
u32 name, value;
int err;
name = (FW_PARAMS_MNEM(FW_PARAMS_MNEM_DMAQ) |
FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_DMAQ_EQ_DCBPRIO_ETH) |
FW_PARAMS_PARAM_YZ(txq->q.cntxt_id));
value = enable ? i : 0xffffffff;
/* Since we can be called while atomic (from "interrupt
* level") we need to issue the Set Parameters Commannd
* without sleeping (timeout < 0).
*/
err = t4_set_params_nosleep(adap, adap->mbox, adap->fn, 0, 1,
&name, &value);
if (err)
dev_err(adap->pdev_dev,
"Can't %s DCB Priority on port %d, TX Queue %d: err=%d\n",
enable ? "set" : "unset", pi->port_id, i, -err);
else
txq->dcb_prio = value;
}
}
#endif /* CONFIG_CHELSIO_T4_DCB */
void t4_os_link_changed(struct adapter *adapter, int port_id, int link_stat)
{
struct net_device *dev = adapter->port[port_id];
/* Skip changes from disabled ports. */
if (netif_running(dev) && link_stat != netif_carrier_ok(dev)) {
if (link_stat)
netif_carrier_on(dev);
else {
#ifdef CONFIG_CHELSIO_T4_DCB
cxgb4_dcb_state_init(dev);
dcb_tx_queue_prio_enable(dev, false);
#endif /* CONFIG_CHELSIO_T4_DCB */
netif_carrier_off(dev);
}
link_report(dev);
}
}
void t4_os_portmod_changed(const struct adapter *adap, int port_id)
{
static const char *mod_str[] = {
NULL, "LR", "SR", "ER", "passive DA", "active DA", "LRM"
};
const struct net_device *dev = adap->port[port_id];
const struct port_info *pi = netdev_priv(dev);
if (pi->mod_type == FW_PORT_MOD_TYPE_NONE)
netdev_info(dev, "port module unplugged\n");
else if (pi->mod_type < ARRAY_SIZE(mod_str))
netdev_info(dev, "%s module inserted\n", mod_str[pi->mod_type]);
}
/*
* Configure the exact and hash address filters to handle a port's multicast
* and secondary unicast MAC addresses.
*/
static int set_addr_filters(const struct net_device *dev, bool sleep)
{
u64 mhash = 0;
u64 uhash = 0;
bool free = true;
u16 filt_idx[7];
const u8 *addr[7];
int ret, naddr = 0;
const struct netdev_hw_addr *ha;
int uc_cnt = netdev_uc_count(dev);
int mc_cnt = netdev_mc_count(dev);
const struct port_info *pi = netdev_priv(dev);
unsigned int mb = pi->adapter->fn;
/* first do the secondary unicast addresses */
netdev_for_each_uc_addr(ha, dev) {
addr[naddr++] = ha->addr;
if (--uc_cnt == 0 || naddr >= ARRAY_SIZE(addr)) {
ret = t4_alloc_mac_filt(pi->adapter, mb, pi->viid, free,
naddr, addr, filt_idx, &uhash, sleep);
if (ret < 0)
return ret;
free = false;
naddr = 0;
}
}
/* next set up the multicast addresses */
netdev_for_each_mc_addr(ha, dev) {
addr[naddr++] = ha->addr;
if (--mc_cnt == 0 || naddr >= ARRAY_SIZE(addr)) {
ret = t4_alloc_mac_filt(pi->adapter, mb, pi->viid, free,
naddr, addr, filt_idx, &mhash, sleep);
if (ret < 0)
return ret;
free = false;
naddr = 0;
}
}
return t4_set_addr_hash(pi->adapter, mb, pi->viid, uhash != 0,
uhash | mhash, sleep);
}
int dbfifo_int_thresh = 10; /* 10 == 640 entry threshold */
module_param(dbfifo_int_thresh, int, 0644);
MODULE_PARM_DESC(dbfifo_int_thresh, "doorbell fifo interrupt threshold");
/*
* usecs to sleep while draining the dbfifo
*/
static int dbfifo_drain_delay = 1000;
module_param(dbfifo_drain_delay, int, 0644);
MODULE_PARM_DESC(dbfifo_drain_delay,
"usecs to sleep while draining the dbfifo");
/*
* Set Rx properties of a port, such as promiscruity, address filters, and MTU.
* If @mtu is -1 it is left unchanged.
*/
static int set_rxmode(struct net_device *dev, int mtu, bool sleep_ok)
{
int ret;
struct port_info *pi = netdev_priv(dev);
ret = set_addr_filters(dev, sleep_ok);
if (ret == 0)
ret = t4_set_rxmode(pi->adapter, pi->adapter->fn, pi->viid, mtu,
(dev->flags & IFF_PROMISC) ? 1 : 0,
(dev->flags & IFF_ALLMULTI) ? 1 : 0, 1, -1,
sleep_ok);
return ret;
}
/**
* link_start - enable a port
* @dev: the port to enable
*
* Performs the MAC and PHY actions needed to enable a port.
*/
static int link_start(struct net_device *dev)
{
int ret;
struct port_info *pi = netdev_priv(dev);
unsigned int mb = pi->adapter->fn;
/*
* We do not set address filters and promiscuity here, the stack does
* that step explicitly.
*/
ret = t4_set_rxmode(pi->adapter, mb, pi->viid, dev->mtu, -1, -1, -1,
!!(dev->features & NETIF_F_HW_VLAN_CTAG_RX), true);
if (ret == 0) {
ret = t4_change_mac(pi->adapter, mb, pi->viid,
pi->xact_addr_filt, dev->dev_addr, true,
true);
if (ret >= 0) {
pi->xact_addr_filt = ret;
ret = 0;
}
}
if (ret == 0)
ret = t4_link_start(pi->adapter, mb, pi->tx_chan,
&pi->link_cfg);
if (ret == 0) {
local_bh_disable();
ret = t4_enable_vi_params(pi->adapter, mb, pi->viid, true,
true, CXGB4_DCB_ENABLED);
local_bh_enable();
}
return ret;
}
int cxgb4_dcb_enabled(const struct net_device *dev)
{
#ifdef CONFIG_CHELSIO_T4_DCB
struct port_info *pi = netdev_priv(dev);
return pi->dcb.state == CXGB4_DCB_STATE_FW_ALLSYNCED;
#else
return 0;
#endif
}
EXPORT_SYMBOL(cxgb4_dcb_enabled);
#ifdef CONFIG_CHELSIO_T4_DCB
/* Handle a Data Center Bridging update message from the firmware. */
static void dcb_rpl(struct adapter *adap, const struct fw_port_cmd *pcmd)
{
int port = FW_PORT_CMD_PORTID_GET(ntohl(pcmd->op_to_portid));
struct net_device *dev = adap->port[port];
int old_dcb_enabled = cxgb4_dcb_enabled(dev);
int new_dcb_enabled;
cxgb4_dcb_handle_fw_update(adap, pcmd);
new_dcb_enabled = cxgb4_dcb_enabled(dev);
/* If the DCB has become enabled or disabled on the port then we're
* going to need to set up/tear down DCB Priority parameters for the
* TX Queues associated with the port.
*/
if (new_dcb_enabled != old_dcb_enabled)
dcb_tx_queue_prio_enable(dev, new_dcb_enabled);
}
#endif /* CONFIG_CHELSIO_T4_DCB */
/* Clear a filter and release any of its resources that we own. This also
* clears the filter's "pending" status.
*/
static void clear_filter(struct adapter *adap, struct filter_entry *f)
{
/* If the new or old filter have loopback rewriteing rules then we'll
* need to free any existing Layer Two Table (L2T) entries of the old
* filter rule. The firmware will handle freeing up any Source MAC
* Table (SMT) entries used for rewriting Source MAC Addresses in
* loopback rules.
*/
if (f->l2t)
cxgb4_l2t_release(f->l2t);
/* The zeroing of the filter rule below clears the filter valid,
* pending, locked flags, l2t pointer, etc. so it's all we need for
* this operation.
*/
memset(f, 0, sizeof(*f));
}
/* Handle a filter write/deletion reply.
*/
static void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl)
{
unsigned int idx = GET_TID(rpl);
unsigned int nidx = idx - adap->tids.ftid_base;
unsigned int ret;
struct filter_entry *f;
if (idx >= adap->tids.ftid_base && nidx <
(adap->tids.nftids + adap->tids.nsftids)) {
idx = nidx;
ret = GET_TCB_COOKIE(rpl->cookie);
f = &adap->tids.ftid_tab[idx];
if (ret == FW_FILTER_WR_FLT_DELETED) {
/* Clear the filter when we get confirmation from the
* hardware that the filter has been deleted.
*/
clear_filter(adap, f);
} else if (ret == FW_FILTER_WR_SMT_TBL_FULL) {
dev_err(adap->pdev_dev, "filter %u setup failed due to full SMT\n",
idx);
clear_filter(adap, f);
} else if (ret == FW_FILTER_WR_FLT_ADDED) {
f->smtidx = (be64_to_cpu(rpl->oldval) >> 24) & 0xff;
f->pending = 0; /* asynchronous setup completed */
f->valid = 1;
} else {
/* Something went wrong. Issue a warning about the
* problem and clear everything out.
*/
dev_err(adap->pdev_dev, "filter %u setup failed with error %u\n",
idx, ret);
clear_filter(adap, f);
}
}
}
/* Response queue handler for the FW event queue.
*/
static int fwevtq_handler(struct sge_rspq *q, const __be64 *rsp,
const struct pkt_gl *gl)
{
u8 opcode = ((const struct rss_header *)rsp)->opcode;
rsp++; /* skip RSS header */
/* FW can send EGR_UPDATEs encapsulated in a CPL_FW4_MSG.
*/
if (unlikely(opcode == CPL_FW4_MSG &&
((const struct cpl_fw4_msg *)rsp)->type == FW_TYPE_RSSCPL)) {
rsp++;
opcode = ((const struct rss_header *)rsp)->opcode;
rsp++;
if (opcode != CPL_SGE_EGR_UPDATE) {
dev_err(q->adap->pdev_dev, "unexpected FW4/CPL %#x on FW event queue\n"
, opcode);
goto out;
}
}
if (likely(opcode == CPL_SGE_EGR_UPDATE)) {
const struct cpl_sge_egr_update *p = (void *)rsp;
unsigned int qid = EGR_QID(ntohl(p->opcode_qid));
struct sge_txq *txq;
txq = q->adap->sge.egr_map[qid - q->adap->sge.egr_start];
txq->restarts++;
if ((u8 *)txq < (u8 *)q->adap->sge.ofldtxq) {
struct sge_eth_txq *eq;
eq = container_of(txq, struct sge_eth_txq, q);
netif_tx_wake_queue(eq->txq);
} else {
struct sge_ofld_txq *oq;
oq = container_of(txq, struct sge_ofld_txq, q);
tasklet_schedule(&oq->qresume_tsk);
}
} else if (opcode == CPL_FW6_MSG || opcode == CPL_FW4_MSG) {
const struct cpl_fw6_msg *p = (void *)rsp;
#ifdef CONFIG_CHELSIO_T4_DCB
const struct fw_port_cmd *pcmd = (const void *)p->data;
unsigned int cmd = FW_CMD_OP_GET(ntohl(pcmd->op_to_portid));
unsigned int action =
FW_PORT_CMD_ACTION_GET(ntohl(pcmd->action_to_len16));
if (cmd == FW_PORT_CMD &&
action == FW_PORT_ACTION_GET_PORT_INFO) {
int port = FW_PORT_CMD_PORTID_GET(
be32_to_cpu(pcmd->op_to_portid));
struct net_device *dev = q->adap->port[port];
int state_input = ((pcmd->u.info.dcbxdis_pkd &
FW_PORT_CMD_DCBXDIS)
? CXGB4_DCB_INPUT_FW_DISABLED
: CXGB4_DCB_INPUT_FW_ENABLED);
cxgb4_dcb_state_fsm(dev, state_input);
}
if (cmd == FW_PORT_CMD &&
action == FW_PORT_ACTION_L2_DCB_CFG)
dcb_rpl(q->adap, pcmd);
else
#endif
if (p->type == 0)
t4_handle_fw_rpl(q->adap, p->data);
} else if (opcode == CPL_L2T_WRITE_RPL) {
const struct cpl_l2t_write_rpl *p = (void *)rsp;
do_l2t_write_rpl(q->adap, p);
} else if (opcode == CPL_SET_TCB_RPL) {
const struct cpl_set_tcb_rpl *p = (void *)rsp;
filter_rpl(q->adap, p);
} else
dev_err(q->adap->pdev_dev,
"unexpected CPL %#x on FW event queue\n", opcode);
out:
return 0;
}
/**
* uldrx_handler - response queue handler for ULD queues
* @q: the response queue that received the packet
* @rsp: the response queue descriptor holding the offload message
* @gl: the gather list of packet fragments
*
* Deliver an ingress offload packet to a ULD. All processing is done by
* the ULD, we just maintain statistics.
*/
static int uldrx_handler(struct sge_rspq *q, const __be64 *rsp,
const struct pkt_gl *gl)
{
struct sge_ofld_rxq *rxq = container_of(q, struct sge_ofld_rxq, rspq);
/* FW can send CPLs encapsulated in a CPL_FW4_MSG.
*/
if (((const struct rss_header *)rsp)->opcode == CPL_FW4_MSG &&
((const struct cpl_fw4_msg *)(rsp + 1))->type == FW_TYPE_RSSCPL)
rsp += 2;
if (ulds[q->uld].rx_handler(q->adap->uld_handle[q->uld], rsp, gl)) {
rxq->stats.nomem++;
return -1;
}
if (gl == NULL)
rxq->stats.imm++;
else if (gl == CXGB4_MSG_AN)
rxq->stats.an++;
else
rxq->stats.pkts++;
return 0;
}
static void disable_msi(struct adapter *adapter)
{
if (adapter->flags & USING_MSIX) {
pci_disable_msix(adapter->pdev);
adapter->flags &= ~USING_MSIX;
} else if (adapter->flags & USING_MSI) {
pci_disable_msi(adapter->pdev);
adapter->flags &= ~USING_MSI;
}
}
/*
* Interrupt handler for non-data events used with MSI-X.
*/
static irqreturn_t t4_nondata_intr(int irq, void *cookie)
{
struct adapter *adap = cookie;
u32 v = t4_read_reg(adap, MYPF_REG(PL_PF_INT_CAUSE));
if (v & PFSW) {
adap->swintr = 1;
t4_write_reg(adap, MYPF_REG(PL_PF_INT_CAUSE), v);
}
t4_slow_intr_handler(adap);
return IRQ_HANDLED;
}
/*
* Name the MSI-X interrupts.
*/
static void name_msix_vecs(struct adapter *adap)
{
int i, j, msi_idx = 2, n = sizeof(adap->msix_info[0].desc);
/* non-data interrupts */
snprintf(adap->msix_info[0].desc, n, "%s", adap->port[0]->name);
/* FW events */
snprintf(adap->msix_info[1].desc, n, "%s-FWeventq",
adap->port[0]->name);
/* Ethernet queues */
for_each_port(adap, j) {
struct net_device *d = adap->port[j];
const struct port_info *pi = netdev_priv(d);
for (i = 0; i < pi->nqsets; i++, msi_idx++)
snprintf(adap->msix_info[msi_idx].desc, n, "%s-Rx%d",
d->name, i);
}
/* offload queues */
for_each_ofldrxq(&adap->sge, i)
snprintf(adap->msix_info[msi_idx++].desc, n, "%s-ofld%d",
adap->port[0]->name, i);
for_each_rdmarxq(&adap->sge, i)
snprintf(adap->msix_info[msi_idx++].desc, n, "%s-rdma%d",
adap->port[0]->name, i);
for_each_rdmaciq(&adap->sge, i)
snprintf(adap->msix_info[msi_idx++].desc, n, "%s-rdma-ciq%d",
adap->port[0]->name, i);
}
static int request_msix_queue_irqs(struct adapter *adap)
{
struct sge *s = &adap->sge;
int err, ethqidx, ofldqidx = 0, rdmaqidx = 0, rdmaciqqidx = 0;
int msi_index = 2;
err = request_irq(adap->msix_info[1].vec, t4_sge_intr_msix, 0,
adap->msix_info[1].desc, &s->fw_evtq);
if (err)
return err;
for_each_ethrxq(s, ethqidx) {
err = request_irq(adap->msix_info[msi_index].vec,
t4_sge_intr_msix, 0,
adap->msix_info[msi_index].desc,
&s->ethrxq[ethqidx].rspq);
if (err)
goto unwind;
msi_index++;
}
for_each_ofldrxq(s, ofldqidx) {
err = request_irq(adap->msix_info[msi_index].vec,
t4_sge_intr_msix, 0,
adap->msix_info[msi_index].desc,
&s->ofldrxq[ofldqidx].rspq);
if (err)
goto unwind;
msi_index++;
}
for_each_rdmarxq(s, rdmaqidx) {
err = request_irq(adap->msix_info[msi_index].vec,
t4_sge_intr_msix, 0,
adap->msix_info[msi_index].desc,
&s->rdmarxq[rdmaqidx].rspq);
if (err)
goto unwind;
msi_index++;
}
for_each_rdmaciq(s, rdmaciqqidx) {
err = request_irq(adap->msix_info[msi_index].vec,
t4_sge_intr_msix, 0,
adap->msix_info[msi_index].desc,
&s->rdmaciq[rdmaciqqidx].rspq);
if (err)
goto unwind;
msi_index++;
}
return 0;
unwind:
while (--rdmaciqqidx >= 0)
free_irq(adap->msix_info[--msi_index].vec,
&s->rdmaciq[rdmaciqqidx].rspq);
while (--rdmaqidx >= 0)
free_irq(adap->msix_info[--msi_index].vec,
&s->rdmarxq[rdmaqidx].rspq);
while (--ofldqidx >= 0)
free_irq(adap->msix_info[--msi_index].vec,
&s->ofldrxq[ofldqidx].rspq);
while (--ethqidx >= 0)
free_irq(adap->msix_info[--msi_index].vec,
&s->ethrxq[ethqidx].rspq);
free_irq(adap->msix_info[1].vec, &s->fw_evtq);
return err;
}
static void free_msix_queue_irqs(struct adapter *adap)
{
int i, msi_index = 2;
struct sge *s = &adap->sge;
free_irq(adap->msix_info[1].vec, &s->fw_evtq);
for_each_ethrxq(s, i)
free_irq(adap->msix_info[msi_index++].vec, &s->ethrxq[i].rspq);
for_each_ofldrxq(s, i)
free_irq(adap->msix_info[msi_index++].vec, &s->ofldrxq[i].rspq);
for_each_rdmarxq(s, i)
free_irq(adap->msix_info[msi_index++].vec, &s->rdmarxq[i].rspq);
for_each_rdmaciq(s, i)
free_irq(adap->msix_info[msi_index++].vec, &s->rdmaciq[i].rspq);
}
/**
* write_rss - write the RSS table for a given port
* @pi: the port
* @queues: array of queue indices for RSS
*
* Sets up the portion of the HW RSS table for the port's VI to distribute
* packets to the Rx queues in @queues.
*/
static int write_rss(const struct port_info *pi, const u16 *queues)
{
u16 *rss;
int i, err;
const struct sge_eth_rxq *q = &pi->adapter->sge.ethrxq[pi->first_qset];
rss = kmalloc(pi->rss_size * sizeof(u16), GFP_KERNEL);
if (!rss)
return -ENOMEM;
/* map the queue indices to queue ids */
for (i = 0; i < pi->rss_size; i++, queues++)
rss[i] = q[*queues].rspq.abs_id;
err = t4_config_rss_range(pi->adapter, pi->adapter->fn, pi->viid, 0,
pi->rss_size, rss, pi->rss_size);
kfree(rss);
return err;
}
/**
* setup_rss - configure RSS
* @adap: the adapter
*
* Sets up RSS for each port.
*/
static int setup_rss(struct adapter *adap)
{
int i, err;
for_each_port(adap, i) {
const struct port_info *pi = adap2pinfo(adap, i);
err = write_rss(pi, pi->rss);
if (err)
return err;
}
return 0;
}
/*
* Return the channel of the ingress queue with the given qid.
*/
static unsigned int rxq_to_chan(const struct sge *p, unsigned int qid)
{
qid -= p->ingr_start;
return netdev2pinfo(p->ingr_map[qid]->netdev)->tx_chan;
}
/*
* Wait until all NAPI handlers are descheduled.
*/
static void quiesce_rx(struct adapter *adap)
{
int i;
for (i = 0; i < ARRAY_SIZE(adap->sge.ingr_map); i++) {
struct sge_rspq *q = adap->sge.ingr_map[i];
if (q && q->handler)
napi_disable(&q->napi);
}
}
/*
* Enable NAPI scheduling and interrupt generation for all Rx queues.
*/
static void enable_rx(struct adapter *adap)
{
int i;
for (i = 0; i < ARRAY_SIZE(adap->sge.ingr_map); i++) {
struct sge_rspq *q = adap->sge.ingr_map[i];
if (!q)
continue;
if (q->handler)
napi_enable(&q->napi);
/* 0-increment GTS to start the timer and enable interrupts */
t4_write_reg(adap, MYPF_REG(SGE_PF_GTS),
SEINTARM(q->intr_params) |
INGRESSQID(q->cntxt_id));
}
}
/**
* setup_sge_queues - configure SGE Tx/Rx/response queues
* @adap: the adapter
*
* Determines how many sets of SGE queues to use and initializes them.
* We support multiple queue sets per port if we have MSI-X, otherwise
* just one queue set per port.
*/
static int setup_sge_queues(struct adapter *adap)
{
int err, msi_idx, i, j;
struct sge *s = &adap->sge;
bitmap_zero(s->starving_fl, MAX_EGRQ);
bitmap_zero(s->txq_maperr, MAX_EGRQ);
if (adap->flags & USING_MSIX)
msi_idx = 1; /* vector 0 is for non-queue interrupts */
else {
err = t4_sge_alloc_rxq(adap, &s->intrq, false, adap->port[0], 0,
NULL, NULL);
if (err)
return err;
msi_idx = -((int)s->intrq.abs_id + 1);
}
err = t4_sge_alloc_rxq(adap, &s->fw_evtq, true, adap->port[0],
msi_idx, NULL, fwevtq_handler);
if (err) {
freeout: t4_free_sge_resources(adap);
return err;
}
for_each_port(adap, i) {
struct net_device *dev = adap->port[i];
struct port_info *pi = netdev_priv(dev);
struct sge_eth_rxq *q = &s->ethrxq[pi->first_qset];
struct sge_eth_txq *t = &s->ethtxq[pi->first_qset];
for (j = 0; j < pi->nqsets; j++, q++) {
if (msi_idx > 0)
msi_idx++;
err = t4_sge_alloc_rxq(adap, &q->rspq, false, dev,
msi_idx, &q->fl,
t4_ethrx_handler);
if (err)
goto freeout;
q->rspq.idx = j;
memset(&q->stats, 0, sizeof(q->stats));
}
for (j = 0; j < pi->nqsets; j++, t++) {
err = t4_sge_alloc_eth_txq(adap, t, dev,
netdev_get_tx_queue(dev, j),
s->fw_evtq.cntxt_id);
if (err)
goto freeout;
}
}
j = s->ofldqsets / adap->params.nports; /* ofld queues per channel */
for_each_ofldrxq(s, i) {
struct sge_ofld_rxq *q = &s->ofldrxq[i];
struct net_device *dev = adap->port[i / j];
if (msi_idx > 0)
msi_idx++;
err = t4_sge_alloc_rxq(adap, &q->rspq, false, dev, msi_idx,
q->fl.size ? &q->fl : NULL,
uldrx_handler);
if (err)
goto freeout;
memset(&q->stats, 0, sizeof(q->stats));
s->ofld_rxq[i] = q->rspq.abs_id;
err = t4_sge_alloc_ofld_txq(adap, &s->ofldtxq[i], dev,
s->fw_evtq.cntxt_id);
if (err)
goto freeout;
}
for_each_rdmarxq(s, i) {
struct sge_ofld_rxq *q = &s->rdmarxq[i];
if (msi_idx > 0)
msi_idx++;
err = t4_sge_alloc_rxq(adap, &q->rspq, false, adap->port[i],
msi_idx, q->fl.size ? &q->fl : NULL,
uldrx_handler);
if (err)
goto freeout;
memset(&q->stats, 0, sizeof(q->stats));
s->rdma_rxq[i] = q->rspq.abs_id;
}
for_each_rdmaciq(s, i) {
struct sge_ofld_rxq *q = &s->rdmaciq[i];
if (msi_idx > 0)
msi_idx++;
err = t4_sge_alloc_rxq(adap, &q->rspq, false, adap->port[i],
msi_idx, q->fl.size ? &q->fl : NULL,
uldrx_handler);
if (err)
goto freeout;
memset(&q->stats, 0, sizeof(q->stats));
s->rdma_ciq[i] = q->rspq.abs_id;
}
for_each_port(adap, i) {
/*
* Note that ->rdmarxq[i].rspq.cntxt_id below is 0 if we don't
* have RDMA queues, and that's the right value.
*/
err = t4_sge_alloc_ctrl_txq(adap, &s->ctrlq[i], adap->port[i],
s->fw_evtq.cntxt_id,
s->rdmarxq[i].rspq.cntxt_id);
if (err)
goto freeout;
}
t4_write_reg(adap, MPS_TRC_RSS_CONTROL,
RSSCONTROL(netdev2pinfo(adap->port[0])->tx_chan) |
QUEUENUMBER(s->ethrxq[0].rspq.abs_id));
return 0;
}
/*
* Allocate a chunk of memory using kmalloc or, if that fails, vmalloc.
* The allocated memory is cleared.
*/
void *t4_alloc_mem(size_t size)
{
void *p = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
if (!p)
p = vzalloc(size);
return p;
}
/*
* Free memory allocated through alloc_mem().
*/
static void t4_free_mem(void *addr)
{
if (is_vmalloc_addr(addr))
vfree(addr);
else
kfree(addr);
}
/* Send a Work Request to write the filter at a specified index. We construct
* a Firmware Filter Work Request to have the work done and put the indicated
* filter into "pending" mode which will prevent any further actions against
* it till we get a reply from the firmware on the completion status of the
* request.
*/
static int set_filter_wr(struct adapter *adapter, int fidx)
{
struct filter_entry *f = &adapter->tids.ftid_tab[fidx];
struct sk_buff *skb;
struct fw_filter_wr *fwr;
unsigned int ftid;
/* If the new filter requires loopback Destination MAC and/or VLAN
* rewriting then we need to allocate a Layer 2 Table (L2T) entry for
* the filter.
*/
if (f->fs.newdmac || f->fs.newvlan) {
/* allocate L2T entry for new filter */
f->l2t = t4_l2t_alloc_switching(adapter->l2t);
if (f->l2t == NULL)
return -EAGAIN;
if (t4_l2t_set_switching(adapter, f->l2t, f->fs.vlan,
f->fs.eport, f->fs.dmac)) {
cxgb4_l2t_release(f->l2t);
f->l2t = NULL;
return -ENOMEM;
}
}
ftid = adapter->tids.ftid_base + fidx;
skb = alloc_skb(sizeof(*fwr), GFP_KERNEL | __GFP_NOFAIL);
fwr = (struct fw_filter_wr *)__skb_put(skb, sizeof(*fwr));
memset(fwr, 0, sizeof(*fwr));
/* It would be nice to put most of the following in t4_hw.c but most
* of the work is translating the cxgbtool ch_filter_specification
* into the Work Request and the definition of that structure is
* currently in cxgbtool.h which isn't appropriate to pull into the
* common code. We may eventually try to come up with a more neutral
* filter specification structure but for now it's easiest to simply
* put this fairly direct code in line ...
*/
fwr->op_pkd = htonl(FW_WR_OP(FW_FILTER_WR));
fwr->len16_pkd = htonl(FW_WR_LEN16(sizeof(*fwr)/16));
fwr->tid_to_iq =
htonl(V_FW_FILTER_WR_TID(ftid) |
V_FW_FILTER_WR_RQTYPE(f->fs.type) |
V_FW_FILTER_WR_NOREPLY(0) |
V_FW_FILTER_WR_IQ(f->fs.iq));
fwr->del_filter_to_l2tix =
htonl(V_FW_FILTER_WR_RPTTID(f->fs.rpttid) |
V_FW_FILTER_WR_DROP(f->fs.action == FILTER_DROP) |
V_FW_FILTER_WR_DIRSTEER(f->fs.dirsteer) |
V_FW_FILTER_WR_MASKHASH(f->fs.maskhash) |
V_FW_FILTER_WR_DIRSTEERHASH(f->fs.dirsteerhash) |
V_FW_FILTER_WR_LPBK(f->fs.action == FILTER_SWITCH) |
V_FW_FILTER_WR_DMAC(f->fs.newdmac) |
V_FW_FILTER_WR_SMAC(f->fs.newsmac) |
V_FW_FILTER_WR_INSVLAN(f->fs.newvlan == VLAN_INSERT ||
f->fs.newvlan == VLAN_REWRITE) |
V_FW_FILTER_WR_RMVLAN(f->fs.newvlan == VLAN_REMOVE ||
f->fs.newvlan == VLAN_REWRITE) |
V_FW_FILTER_WR_HITCNTS(f->fs.hitcnts) |
V_FW_FILTER_WR_TXCHAN(f->fs.eport) |
V_FW_FILTER_WR_PRIO(f->fs.prio) |
V_FW_FILTER_WR_L2TIX(f->l2t ? f->l2t->idx : 0));
fwr->ethtype = htons(f->fs.val.ethtype);
fwr->ethtypem = htons(f->fs.mask.ethtype);
fwr->frag_to_ovlan_vldm =
(V_FW_FILTER_WR_FRAG(f->fs.val.frag) |
V_FW_FILTER_WR_FRAGM(f->fs.mask.frag) |
V_FW_FILTER_WR_IVLAN_VLD(f->fs.val.ivlan_vld) |
V_FW_FILTER_WR_OVLAN_VLD(f->fs.val.ovlan_vld) |
V_FW_FILTER_WR_IVLAN_VLDM(f->fs.mask.ivlan_vld) |
V_FW_FILTER_WR_OVLAN_VLDM(f->fs.mask.ovlan_vld));
fwr->smac_sel = 0;
fwr->rx_chan_rx_rpl_iq =
htons(V_FW_FILTER_WR_RX_CHAN(0) |
V_FW_FILTER_WR_RX_RPL_IQ(adapter->sge.fw_evtq.abs_id));
fwr->maci_to_matchtypem =
htonl(V_FW_FILTER_WR_MACI(f->fs.val.macidx) |
V_FW_FILTER_WR_MACIM(f->fs.mask.macidx) |
V_FW_FILTER_WR_FCOE(f->fs.val.fcoe) |
V_FW_FILTER_WR_FCOEM(f->fs.mask.fcoe) |
V_FW_FILTER_WR_PORT(f->fs.val.iport) |
V_FW_FILTER_WR_PORTM(f->fs.mask.iport) |
V_FW_FILTER_WR_MATCHTYPE(f->fs.val.matchtype) |
V_FW_FILTER_WR_MATCHTYPEM(f->fs.mask.matchtype));
fwr->ptcl = f->fs.val.proto;
fwr->ptclm = f->fs.mask.proto;
fwr->ttyp = f->fs.val.tos;
fwr->ttypm = f->fs.mask.tos;
fwr->ivlan = htons(f->fs.val.ivlan);
fwr->ivlanm = htons(f->fs.mask.ivlan);
fwr->ovlan = htons(f->fs.val.ovlan);
fwr->ovlanm = htons(f->fs.mask.ovlan);
memcpy(fwr->lip, f->fs.val.lip, sizeof(fwr->lip));
memcpy(fwr->lipm, f->fs.mask.lip, sizeof(fwr->lipm));
memcpy(fwr->fip, f->fs.val.fip, sizeof(fwr->fip));
memcpy(fwr->fipm, f->fs.mask.fip, sizeof(fwr->fipm));
fwr->lp = htons(f->fs.val.lport);
fwr->lpm = htons(f->fs.mask.lport);
fwr->fp = htons(f->fs.val.fport);
fwr->fpm = htons(f->fs.mask.fport);
if (f->fs.newsmac)
memcpy(fwr->sma, f->fs.smac, sizeof(fwr->sma));
/* Mark the filter as "pending" and ship off the Filter Work Request.
* When we get the Work Request Reply we'll clear the pending status.
*/
f->pending = 1;
set_wr_txq(skb, CPL_PRIORITY_CONTROL, f->fs.val.iport & 0x3);
t4_ofld_send(adapter, skb);
return 0;
}
/* Delete the filter at a specified index.
*/
static int del_filter_wr(struct adapter *adapter, int fidx)
{
struct filter_entry *f = &adapter->tids.ftid_tab[fidx];
struct sk_buff *skb;
struct fw_filter_wr *fwr;
unsigned int len, ftid;
len = sizeof(*fwr);
ftid = adapter->tids.ftid_base + fidx;
skb = alloc_skb(len, GFP_KERNEL | __GFP_NOFAIL);
fwr = (struct fw_filter_wr *)__skb_put(skb, len);
t4_mk_filtdelwr(ftid, fwr, adapter->sge.fw_evtq.abs_id);
/* Mark the filter as "pending" and ship off the Filter Work Request.
* When we get the Work Request Reply we'll clear the pending status.
*/
f->pending = 1;
t4_mgmt_tx(adapter, skb);
return 0;
}
static u16 cxgb_select_queue(struct net_device *dev, struct sk_buff *skb,
void *accel_priv, select_queue_fallback_t fallback)
{
int txq;
#ifdef CONFIG_CHELSIO_T4_DCB
/* If a Data Center Bridging has been successfully negotiated on this
* link then we'll use the skb's priority to map it to a TX Queue.
* The skb's priority is determined via the VLAN Tag Priority Code
* Point field.
*/
if (cxgb4_dcb_enabled(dev)) {
u16 vlan_tci;
int err;
err = vlan_get_tag(skb, &vlan_tci);
if (unlikely(err)) {
if (net_ratelimit())
netdev_warn(dev,
"TX Packet without VLAN Tag on DCB Link\n");
txq = 0;
} else {
txq = (vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
}
return txq;
}
#endif /* CONFIG_CHELSIO_T4_DCB */
if (select_queue) {
txq = (skb_rx_queue_recorded(skb)
? skb_get_rx_queue(skb)
: smp_processor_id());
while (unlikely(txq >= dev->real_num_tx_queues))
txq -= dev->real_num_tx_queues;
return txq;
}
return fallback(dev, skb) % dev->real_num_tx_queues;
}
static inline int is_offload(const struct adapter *adap)
{
return adap->params.offload;
}
/*
* Implementation of ethtool operations.
*/
static u32 get_msglevel(struct net_device *dev)
{
return netdev2adap(dev)->msg_enable;
}
static void set_msglevel(struct net_device *dev, u32 val)
{
netdev2adap(dev)->msg_enable = val;
}
static char stats_strings[][ETH_GSTRING_LEN] = {
"TxOctetsOK ",
"TxFramesOK ",
"TxBroadcastFrames ",
"TxMulticastFrames ",
"TxUnicastFrames ",
"TxErrorFrames ",
"TxFrames64 ",
"TxFrames65To127 ",
"TxFrames128To255 ",
"TxFrames256To511 ",
"TxFrames512To1023 ",
"TxFrames1024To1518 ",
"TxFrames1519ToMax ",
"TxFramesDropped ",
"TxPauseFrames ",
"TxPPP0Frames ",
"TxPPP1Frames ",
"TxPPP2Frames ",
"TxPPP3Frames ",
"TxPPP4Frames ",
"TxPPP5Frames ",
"TxPPP6Frames ",
"TxPPP7Frames ",
"RxOctetsOK ",
"RxFramesOK ",
"RxBroadcastFrames ",
"RxMulticastFrames ",
"RxUnicastFrames ",
"RxFramesTooLong ",
"RxJabberErrors ",
"RxFCSErrors ",
"RxLengthErrors ",
"RxSymbolErrors ",
"RxRuntFrames ",
"RxFrames64 ",
"RxFrames65To127 ",
"RxFrames128To255 ",
"RxFrames256To511 ",
"RxFrames512To1023 ",
"RxFrames1024To1518 ",
"RxFrames1519ToMax ",
"RxPauseFrames ",
"RxPPP0Frames ",
"RxPPP1Frames ",
"RxPPP2Frames ",
"RxPPP3Frames ",
"RxPPP4Frames ",
"RxPPP5Frames ",
"RxPPP6Frames ",
"RxPPP7Frames ",
"RxBG0FramesDropped ",
"RxBG1FramesDropped ",
"RxBG2FramesDropped ",
"RxBG3FramesDropped ",
"RxBG0FramesTrunc ",
"RxBG1FramesTrunc ",
"RxBG2FramesTrunc ",
"RxBG3FramesTrunc ",
"TSO ",
"TxCsumOffload ",
"RxCsumGood ",
"VLANextractions ",
"VLANinsertions ",
"GROpackets ",
"GROmerged ",
"WriteCoalSuccess ",
"WriteCoalFail ",
};
static int get_sset_count(struct net_device *dev, int sset)
{
switch (sset) {
case ETH_SS_STATS:
return ARRAY_SIZE(stats_strings);
default:
return -EOPNOTSUPP;
}
}
#define T4_REGMAP_SIZE (160 * 1024)
#define T5_REGMAP_SIZE (332 * 1024)
static int get_regs_len(struct net_device *dev)
{
struct adapter *adap = netdev2adap(dev);
if (is_t4(adap->params.chip))
return T4_REGMAP_SIZE;
else
return T5_REGMAP_SIZE;
}
static int get_eeprom_len(struct net_device *dev)
{
return EEPROMSIZE;
}
static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
struct adapter *adapter = netdev2adap(dev);
strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
strlcpy(info->version, DRV_VERSION, sizeof(info->version));
strlcpy(info->bus_info, pci_name(adapter->pdev),
sizeof(info->bus_info));
if (adapter->params.fw_vers)
snprintf(info->fw_version, sizeof(info->fw_version),
"%u.%u.%u.%u, TP %u.%u.%u.%u",
FW_HDR_FW_VER_MAJOR_GET(adapter->params.fw_vers),
FW_HDR_FW_VER_MINOR_GET(adapter->params.fw_vers),
FW_HDR_FW_VER_MICRO_GET(adapter->params.fw_vers),
FW_HDR_FW_VER_BUILD_GET(adapter->params.fw_vers),
FW_HDR_FW_VER_MAJOR_GET(adapter->params.tp_vers),
FW_HDR_FW_VER_MINOR_GET(adapter->params.tp_vers),
FW_HDR_FW_VER_MICRO_GET(adapter->params.tp_vers),
FW_HDR_FW_VER_BUILD_GET(adapter->params.tp_vers));
}
static void get_strings(struct net_device *dev, u32 stringset, u8 *data)
{
if (stringset == ETH_SS_STATS)
memcpy(data, stats_strings, sizeof(stats_strings));
}
/*
* port stats maintained per queue of the port. They should be in the same
* order as in stats_strings above.
*/
struct queue_port_stats {
u64 tso;
u64 tx_csum;
u64 rx_csum;
u64 vlan_ex;
u64 vlan_ins;
u64 gro_pkts;
u64 gro_merged;
};
static void collect_sge_port_stats(const struct adapter *adap,
const struct port_info *p, struct queue_port_stats *s)
{
int i;
const struct sge_eth_txq *tx = &adap->sge.ethtxq[p->first_qset];
const struct sge_eth_rxq *rx = &adap->sge.ethrxq[p->first_qset];
memset(s, 0, sizeof(*s));
for (i = 0; i < p->nqsets; i++, rx++, tx++) {
s->tso += tx->tso;
s->tx_csum += tx->tx_cso;
s->rx_csum += rx->stats.rx_cso;
s->vlan_ex += rx->stats.vlan_ex;
s->vlan_ins += tx->vlan_ins;
s->gro_pkts += rx->stats.lro_pkts;
s->gro_merged += rx->stats.lro_merged;
}
}
static void get_stats(struct net_device *dev, struct ethtool_stats *stats,
u64 *data)
{
struct port_info *pi = netdev_priv(dev);
struct adapter *adapter = pi->adapter;
u32 val1, val2;
t4_get_port_stats(adapter, pi->tx_chan, (struct port_stats *)data);
data += sizeof(struct port_stats) / sizeof(u64);
collect_sge_port_stats(adapter, pi, (struct queue_port_stats *)data);
data += sizeof(struct queue_port_stats) / sizeof(u64);
if (!is_t4(adapter->params.chip)) {
t4_write_reg(adapter, SGE_STAT_CFG, STATSOURCE_T5(7));
val1 = t4_read_reg(adapter, SGE_STAT_TOTAL);
val2 = t4_read_reg(adapter, SGE_STAT_MATCH);
*data = val1 - val2;
data++;
*data = val2;
data++;
} else {
memset(data, 0, 2 * sizeof(u64));
*data += 2;
}
}
/*
* Return a version number to identify the type of adapter. The scheme is:
* - bits 0..9: chip version
* - bits 10..15: chip revision
* - bits 16..23: register dump version
*/
static inline unsigned int mk_adap_vers(const struct adapter *ap)
{
return CHELSIO_CHIP_VERSION(ap->params.chip) |
(CHELSIO_CHIP_RELEASE(ap->params.chip) << 10) | (1 << 16);
}
static void reg_block_dump(struct adapter *ap, void *buf, unsigned int start,
unsigned int end)
{
u32 *p = buf + start;
for ( ; start <= end; start += sizeof(u32))
*p++ = t4_read_reg(ap, start);
}
static void get_regs(struct net_device *dev, struct ethtool_regs *regs,
void *buf)
{
static const unsigned int t4_reg_ranges[] = {
0x1008, 0x1108,
0x1180, 0x11b4,
0x11fc, 0x123c,
0x1300, 0x173c,
0x1800, 0x18fc,
0x3000, 0x30d8,
0x30e0, 0x5924,
0x5960, 0x59d4,
0x5a00, 0x5af8,
0x6000, 0x6098,
0x6100, 0x6150,
0x6200, 0x6208,
0x6240, 0x6248,
0x6280, 0x6338,
0x6370, 0x638c,
0x6400, 0x643c,
0x6500, 0x6524,
0x6a00, 0x6a38,
0x6a60, 0x6a78,
0x6b00, 0x6b84,
0x6bf0, 0x6c84,
0x6cf0, 0x6d84,
0x6df0, 0x6e84,
0x6ef0, 0x6f84,
0x6ff0, 0x7084,
0x70f0, 0x7184,
0x71f0, 0x7284,
0x72f0, 0x7384,
0x73f0, 0x7450,
0x7500, 0x7530,
0x7600, 0x761c,
0x7680, 0x76cc,
0x7700, 0x7798,
0x77c0, 0x77fc,
0x7900, 0x79fc,
0x7b00, 0x7c38,
0x7d00, 0x7efc,
0x8dc0, 0x8e1c,
0x8e30, 0x8e78,
0x8ea0, 0x8f6c,
0x8fc0, 0x9074,
0x90fc, 0x90fc,
0x9400, 0x9458,
0x9600, 0x96bc,
0x9800, 0x9808,
0x9820, 0x983c,
0x9850, 0x9864,
0x9c00, 0x9c6c,
0x9c80, 0x9cec,
0x9d00, 0x9d6c,
0x9d80, 0x9dec,
0x9e00, 0x9e6c,
0x9e80, 0x9eec,
0x9f00, 0x9f6c,
0x9f80, 0x9fec,
0xd004, 0xd03c,
0xdfc0, 0xdfe0,
0xe000, 0xea7c,
0xf000, 0x11190,
0x19040, 0x1906c,
0x19078, 0x19080,
0x1908c, 0x19124,
0x19150, 0x191b0,
0x191d0, 0x191e8,
0x19238, 0x1924c,
0x193f8, 0x19474,
0x19490, 0x194f8,
0x19800, 0x19f30,
0x1a000, 0x1a06c,
0x1a0b0, 0x1a120,
0x1a128, 0x1a138,
0x1a190, 0x1a1c4,
0x1a1fc, 0x1a1fc,
0x1e040, 0x1e04c,
0x1e284, 0x1e28c,
0x1e2c0, 0x1e2c0,
0x1e2e0, 0x1e2e0,
0x1e300, 0x1e384,
0x1e3c0, 0x1e3c8,
0x1e440, 0x1e44c,
0x1e684, 0x1e68c,
0x1e6c0, 0x1e6c0,
0x1e6e0, 0x1e6e0,
0x1e700, 0x1e784,
0x1e7c0, 0x1e7c8,
0x1e840, 0x1e84c,
0x1ea84, 0x1ea8c,
0x1eac0, 0x1eac0,
0x1eae0, 0x1eae0,
0x1eb00, 0x1eb84,
0x1ebc0, 0x1ebc8,
0x1ec40, 0x1ec4c,
0x1ee84, 0x1ee8c,
0x1eec0, 0x1eec0,
0x1eee0, 0x1eee0,
0x1ef00, 0x1ef84,
0x1efc0, 0x1efc8,
0x1f040, 0x1f04c,
0x1f284, 0x1f28c,
0x1f2c0, 0x1f2c0,
0x1f2e0, 0x1f2e0,
0x1f300, 0x1f384,
0x1f3c0, 0x1f3c8,
0x1f440, 0x1f44c,
0x1f684, 0x1f68c,
0x1f6c0, 0x1f6c0,
0x1f6e0, 0x1f6e0,
0x1f700, 0x1f784,
0x1f7c0, 0x1f7c8,
0x1f840, 0x1f84c,
0x1fa84, 0x1fa8c,
0x1fac0, 0x1fac0,
0x1fae0, 0x1fae0,
0x1fb00, 0x1fb84,
0x1fbc0, 0x1fbc8,
0x1fc40, 0x1fc4c,
0x1fe84, 0x1fe8c,
0x1fec0, 0x1fec0,
0x1fee0, 0x1fee0,
0x1ff00, 0x1ff84,
0x1ffc0, 0x1ffc8,
0x20000, 0x2002c,
0x20100, 0x2013c,
0x20190, 0x201c8,
0x20200, 0x20318,
0x20400, 0x20528,
0x20540, 0x20614,
0x21000, 0x21040,
0x2104c, 0x21060,
0x210c0, 0x210ec,
0x21200, 0x21268,
0x21270, 0x21284,
0x212fc, 0x21388,
0x21400, 0x21404,
0x21500, 0x21518,
0x2152c, 0x2153c,
0x21550, 0x21554,
0x21600, 0x21600,
0x21608, 0x21628,
0x21630, 0x2163c,
0x21700, 0x2171c,
0x21780, 0x2178c,
0x21800, 0x21c38,
0x21c80, 0x21d7c,
0x21e00, 0x21e04,
0x22000, 0x2202c,
0x22100, 0x2213c,
0x22190, 0x221c8,
0x22200, 0x22318,
0x22400, 0x22528,
0x22540, 0x22614,
0x23000, 0x23040,
0x2304c, 0x23060,
0x230c0, 0x230ec,
0x23200, 0x23268,
0x23270, 0x23284,
0x232fc, 0x23388,
0x23400, 0x23404,
0x23500, 0x23518,
0x2352c, 0x2353c,
0x23550, 0x23554,
0x23600, 0x23600,
0x23608, 0x23628,
0x23630, 0x2363c,
0x23700, 0x2371c,
0x23780, 0x2378c,
0x23800, 0x23c38,
0x23c80, 0x23d7c,
0x23e00, 0x23e04,
0x24000, 0x2402c,
0x24100, 0x2413c,
0x24190, 0x241c8,
0x24200, 0x24318,
0x24400, 0x24528,
0x24540, 0x24614,
0x25000, 0x25040,
0x2504c, 0x25060,
0x250c0, 0x250ec,
0x25200, 0x25268,
0x25270, 0x25284,
0x252fc, 0x25388,
0x25400, 0x25404,
0x25500, 0x25518,
0x2552c, 0x2553c,
0x25550, 0x25554,
0x25600, 0x25600,
0x25608, 0x25628,
0x25630, 0x2563c,
0x25700, 0x2571c,
0x25780, 0x2578c,
0x25800, 0x25c38,
0x25c80, 0x25d7c,
0x25e00, 0x25e04,
0x26000, 0x2602c,
0x26100, 0x2613c,
0x26190, 0x261c8,
0x26200, 0x26318,
0x26400, 0x26528,
0x26540, 0x26614,
0x27000, 0x27040,
0x2704c, 0x27060,
0x270c0, 0x270ec,
0x27200, 0x27268,
0x27270, 0x27284,
0x272fc, 0x27388,
0x27400, 0x27404,
0x27500, 0x27518,
0x2752c, 0x2753c,
0x27550, 0x27554,
0x27600, 0x27600,
0x27608, 0x27628,
0x27630, 0x2763c,
0x27700, 0x2771c,
0x27780, 0x2778c,
0x27800, 0x27c38,
0x27c80, 0x27d7c,
0x27e00, 0x27e04
};
static const unsigned int t5_reg_ranges[] = {
0x1008, 0x1148,
0x1180, 0x11b4,
0x11fc, 0x123c,
0x1280, 0x173c,
0x1800, 0x18fc,
0x3000, 0x3028,
0x3060, 0x30d8,
0x30e0, 0x30fc,
0x3140, 0x357c,
0x35a8, 0x35cc,
0x35ec, 0x35ec,
0x3600, 0x5624,
0x56cc, 0x575c,
0x580c, 0x5814,
0x5890, 0x58bc,
0x5940, 0x59dc,
0x59fc, 0x5a18,
0x5a60, 0x5a9c,
0x5b9c, 0x5bfc,
0x6000, 0x6040,
0x6058, 0x614c,
0x7700, 0x7798,
0x77c0, 0x78fc,
0x7b00, 0x7c54,
0x7d00, 0x7efc,
0x8dc0, 0x8de0,
0x8df8, 0x8e84,
0x8ea0, 0x8f84,
0x8fc0, 0x90f8,
0x9400, 0x9470,
0x9600, 0x96f4,
0x9800, 0x9808,
0x9820, 0x983c,
0x9850, 0x9864,
0x9c00, 0x9c6c,
0x9c80, 0x9cec,
0x9d00, 0x9d6c,
0x9d80, 0x9dec,
0x9e00, 0x9e6c,
0x9e80, 0x9eec,
0x9f00, 0x9f6c,
0x9f80, 0xa020,
0xd004, 0xd03c,
0xdfc0, 0xdfe0,
0xe000, 0x11088,
0x1109c, 0x1117c,
0x11190, 0x11204,
0x19040, 0x1906c,
0x19078, 0x19080,
0x1908c, 0x19124,
0x19150, 0x191b0,
0x191d0, 0x191e8,
0x19238, 0x19290,
0x193f8, 0x19474,
0x19490, 0x194cc,
0x194f0, 0x194f8,
0x19c00, 0x19c60,
0x19c94, 0x19e10,
0x19e50, 0x19f34,
0x19f40, 0x19f50,
0x19f90, 0x19fe4,
0x1a000, 0x1a06c,
0x1a0b0, 0x1a120,
0x1a128, 0x1a138,
0x1a190, 0x1a1c4,
0x1a1fc, 0x1a1fc,
0x1e008, 0x1e00c,
0x1e040, 0x1e04c,
0x1e284, 0x1e290,
0x1e2c0, 0x1e2c0,
0x1e2e0, 0x1e2e0,
0x1e300, 0x1e384,
0x1e3c0, 0x1e3c8,
0x1e408, 0x1e40c,
0x1e440, 0x1e44c,
0x1e684, 0x1e690,
0x1e6c0, 0x1e6c0,
0x1e6e0, 0x1e6e0,
0x1e700, 0x1e784,
0x1e7c0, 0x1e7c8,
0x1e808, 0x1e80c,
0x1e840, 0x1e84c,
0x1ea84, 0x1ea90,
0x1eac0, 0x1eac0,
0x1eae0, 0x1eae0,
0x1eb00, 0x1eb84,
0x1ebc0, 0x1ebc8,
0x1ec08, 0x1ec0c,
0x1ec40, 0x1ec4c,
0x1ee84, 0x1ee90,
0x1eec0, 0x1eec0,
0x1eee0, 0x1eee0,
0x1ef00, 0x1ef84,
0x1efc0, 0x1efc8,
0x1f008, 0x1f00c,
0x1f040, 0x1f04c,
0x1f284, 0x1f290,
0x1f2c0, 0x1f2c0,
0x1f2e0, 0x1f2e0,
0x1f300, 0x1f384,
0x1f3c0, 0x1f3c8,
0x1f408, 0x1f40c,
0x1f440, 0x1f44c,
0x1f684, 0x1f690,
0x1f6c0, 0x1f6c0,
0x1f6e0, 0x1f6e0,
0x1f700, 0x1f784,
0x1f7c0, 0x1f7c8,
0x1f808, 0x1f80c,
0x1f840, 0x1f84c,
0x1fa84, 0x1fa90,
0x1fac0, 0x1fac0,
0x1fae0, 0x1fae0,
0x1fb00, 0x1fb84,
0x1fbc0, 0x1fbc8,
0x1fc08, 0x1fc0c,
0x1fc40, 0x1fc4c,
0x1fe84, 0x1fe90,
0x1fec0, 0x1fec0,
0x1fee0, 0x1fee0,
0x1ff00, 0x1ff84,
0x1ffc0, 0x1ffc8,
0x30000, 0x30030,
0x30100, 0x30144,
0x30190, 0x301d0,
0x30200, 0x30318,
0x30400, 0x3052c,
0x30540, 0x3061c,
0x30800, 0x30834,
0x308c0, 0x30908,
0x30910, 0x309ac,
0x30a00, 0x30a04,
0x30a0c, 0x30a2c,
0x30a44, 0x30a50,
0x30a74, 0x30c24,
0x30d08, 0x30d14,
0x30d1c, 0x30d20,
0x30d3c, 0x30d50,
0x31200, 0x3120c,
0x31220, 0x31220,
0x31240, 0x31240,
0x31600, 0x31600,
0x31608, 0x3160c,
0x31a00, 0x31a1c,
0x31e04, 0x31e20,
0x31e38, 0x31e3c,
0x31e80, 0x31e80,
0x31e88, 0x31ea8,
0x31eb0, 0x31eb4,
0x31ec8, 0x31ed4,
0x31fb8, 0x32004,
0x32208, 0x3223c,
0x32600, 0x32630,
0x32a00, 0x32abc,
0x32b00, 0x32b70,
0x33000, 0x33048,
0x33060, 0x3309c,
0x330f0, 0x33148,
0x33160, 0x3319c,
0x331f0, 0x332e4,
0x332f8, 0x333e4,
0x333f8, 0x33448,
0x33460, 0x3349c,
0x334f0, 0x33548,
0x33560, 0x3359c,
0x335f0, 0x336e4,
0x336f8, 0x337e4,
0x337f8, 0x337fc,
0x33814, 0x33814,
0x3382c, 0x3382c,
0x33880, 0x3388c,
0x338e8, 0x338ec,
0x33900, 0x33948,
0x33960, 0x3399c,
0x339f0, 0x33ae4,
0x33af8, 0x33b10,
0x33b28, 0x33b28,
0x33b3c, 0x33b50,
0x33bf0, 0x33c10,
0x33c28, 0x33c28,
0x33c3c, 0x33c50,
0x33cf0, 0x33cfc,
0x34000, 0x34030,
0x34100, 0x34144,
0x34190, 0x341d0,
0x34200, 0x34318,
0x34400, 0x3452c,
0x34540, 0x3461c,
0x34800, 0x34834,
0x348c0, 0x34908,
0x34910, 0x349ac,
0x34a00, 0x34a04,
0x34a0c, 0x34a2c,
0x34a44, 0x34a50,
0x34a74, 0x34c24,
0x34d08, 0x34d14,
0x34d1c, 0x34d20,
0x34d3c, 0x34d50,
0x35200, 0x3520c,
0x35220, 0x35220,
0x35240, 0x35240,
0x35600, 0x35600,
0x35608, 0x3560c,
0x35a00, 0x35a1c,
0x35e04, 0x35e20,
0x35e38, 0x35e3c,
0x35e80, 0x35e80,
0x35e88, 0x35ea8,
0x35eb0, 0x35eb4,
0x35ec8, 0x35ed4,
0x35fb8, 0x36004,
0x36208, 0x3623c,
0x36600, 0x36630,
0x36a00, 0x36abc,
0x36b00, 0x36b70,
0x37000, 0x37048,
0x37060, 0x3709c,
0x370f0, 0x37148,
0x37160, 0x3719c,
0x371f0, 0x372e4,
0x372f8, 0x373e4,
0x373f8, 0x37448,
0x37460, 0x3749c,
0x374f0, 0x37548,
0x37560, 0x3759c,
0x375f0, 0x376e4,
0x376f8, 0x377e4,
0x377f8, 0x377fc,
0x37814, 0x37814,
0x3782c, 0x3782c,
0x37880, 0x3788c,
0x378e8, 0x378ec,
0x37900, 0x37948,
0x37960, 0x3799c,
0x379f0, 0x37ae4,
0x37af8, 0x37b10,
0x37b28, 0x37b28,
0x37b3c, 0x37b50,
0x37bf0, 0x37c10,
0x37c28, 0x37c28,
0x37c3c, 0x37c50,
0x37cf0, 0x37cfc,
0x38000, 0x38030,
0x38100, 0x38144,
0x38190, 0x381d0,
0x38200, 0x38318,
0x38400, 0x3852c,
0x38540, 0x3861c,
0x38800, 0x38834,
0x388c0, 0x38908,
0x38910, 0x389ac,
0x38a00, 0x38a04,
0x38a0c, 0x38a2c,
0x38a44, 0x38a50,
0x38a74, 0x38c24,
0x38d08, 0x38d14,
0x38d1c, 0x38d20,
0x38d3c, 0x38d50,
0x39200, 0x3920c,
0x39220, 0x39220,
0x39240, 0x39240,
0x39600, 0x39600,
0x39608, 0x3960c,
0x39a00, 0x39a1c,
0x39e04, 0x39e20,
0x39e38, 0x39e3c,
0x39e80, 0x39e80,
0x39e88, 0x39ea8,
0x39eb0, 0x39eb4,
0x39ec8, 0x39ed4,
0x39fb8, 0x3a004,
0x3a208, 0x3a23c,
0x3a600, 0x3a630,
0x3aa00, 0x3aabc,
0x3ab00, 0x3ab70,
0x3b000, 0x3b048,
0x3b060, 0x3b09c,
0x3b0f0, 0x3b148,
0x3b160, 0x3b19c,
0x3b1f0, 0x3b2e4,
0x3b2f8, 0x3b3e4,
0x3b3f8, 0x3b448,
0x3b460, 0x3b49c,
0x3b4f0, 0x3b548,
0x3b560, 0x3b59c,
0x3b5f0, 0x3b6e4,
0x3b6f8, 0x3b7e4,
0x3b7f8, 0x3b7fc,
0x3b814, 0x3b814,
0x3b82c, 0x3b82c,
0x3b880, 0x3b88c,
0x3b8e8, 0x3b8ec,
0x3b900, 0x3b948,
0x3b960, 0x3b99c,
0x3b9f0, 0x3bae4,
0x3baf8, 0x3bb10,
0x3bb28, 0x3bb28,
0x3bb3c, 0x3bb50,
0x3bbf0, 0x3bc10,
0x3bc28, 0x3bc28,
0x3bc3c, 0x3bc50,
0x3bcf0, 0x3bcfc,
0x3c000, 0x3c030,
0x3c100, 0x3c144,
0x3c190, 0x3c1d0,
0x3c200, 0x3c318,
0x3c400, 0x3c52c,
0x3c540, 0x3c61c,
0x3c800, 0x3c834,
0x3c8c0, 0x3c908,
0x3c910, 0x3c9ac,
0x3ca00, 0x3ca04,
0x3ca0c, 0x3ca2c,
0x3ca44, 0x3ca50,
0x3ca74, 0x3cc24,
0x3cd08, 0x3cd14,
0x3cd1c, 0x3cd20,
0x3cd3c, 0x3cd50,
0x3d200, 0x3d20c,
0x3d220, 0x3d220,
0x3d240, 0x3d240,
0x3d600, 0x3d600,
0x3d608, 0x3d60c,
0x3da00, 0x3da1c,
0x3de04, 0x3de20,
0x3de38, 0x3de3c,
0x3de80, 0x3de80,
0x3de88, 0x3dea8,
0x3deb0, 0x3deb4,
0x3dec8, 0x3ded4,
0x3dfb8, 0x3e004,
0x3e208, 0x3e23c,
0x3e600, 0x3e630,
0x3ea00, 0x3eabc,
0x3eb00, 0x3eb70,
0x3f000, 0x3f048,
0x3f060, 0x3f09c,
0x3f0f0, 0x3f148,
0x3f160, 0x3f19c,
0x3f1f0, 0x3f2e4,
0x3f2f8, 0x3f3e4,
0x3f3f8, 0x3f448,
0x3f460, 0x3f49c,
0x3f4f0, 0x3f548,
0x3f560, 0x3f59c,
0x3f5f0, 0x3f6e4,
0x3f6f8, 0x3f7e4,
0x3f7f8, 0x3f7fc,
0x3f814, 0x3f814,
0x3f82c, 0x3f82c,
0x3f880, 0x3f88c,
0x3f8e8, 0x3f8ec,
0x3f900, 0x3f948,
0x3f960, 0x3f99c,
0x3f9f0, 0x3fae4,
0x3faf8, 0x3fb10,
0x3fb28, 0x3fb28,
0x3fb3c, 0x3fb50,
0x3fbf0, 0x3fc10,
0x3fc28, 0x3fc28,
0x3fc3c, 0x3fc50,
0x3fcf0, 0x3fcfc,
0x40000, 0x4000c,
0x40040, 0x40068,
0x40080, 0x40144,
0x40180, 0x4018c,
0x40200, 0x40298,
0x402ac, 0x4033c,
0x403f8, 0x403fc,
0x41304, 0x413c4,
0x41400, 0x4141c,
0x41480, 0x414d0,
0x44000, 0x44078,
0x440c0, 0x44278,
0x442c0, 0x44478,
0x444c0, 0x44678,
0x446c0, 0x44878,
0x448c0, 0x449fc,
0x45000, 0x45068,
0x45080, 0x45084,
0x450a0, 0x450b0,
0x45200, 0x45268,
0x45280, 0x45284,
0x452a0, 0x452b0,
0x460c0, 0x460e4,
0x47000, 0x4708c,
0x47200, 0x47250,
0x47400, 0x47420,
0x47600, 0x47618,
0x47800, 0x47814,
0x48000, 0x4800c,
0x48040, 0x48068,
0x48080, 0x48144,
0x48180, 0x4818c,
0x48200, 0x48298,
0x482ac, 0x4833c,
0x483f8, 0x483fc,
0x49304, 0x493c4,
0x49400, 0x4941c,
0x49480, 0x494d0,
0x4c000, 0x4c078,
0x4c0c0, 0x4c278,
0x4c2c0, 0x4c478,
0x4c4c0, 0x4c678,
0x4c6c0, 0x4c878,
0x4c8c0, 0x4c9fc,
0x4d000, 0x4d068,
0x4d080, 0x4d084,
0x4d0a0, 0x4d0b0,
0x4d200, 0x4d268,
0x4d280, 0x4d284,
0x4d2a0, 0x4d2b0,
0x4e0c0, 0x4e0e4,
0x4f000, 0x4f08c,
0x4f200, 0x4f250,
0x4f400, 0x4f420,
0x4f600, 0x4f618,
0x4f800, 0x4f814,
0x50000, 0x500cc,
0x50400, 0x50400,
0x50800, 0x508cc,
0x50c00, 0x50c00,
0x51000, 0x5101c,
0x51300, 0x51308,
};
int i;
struct adapter *ap = netdev2adap(dev);
static const unsigned int *reg_ranges;
int arr_size = 0, buf_size = 0;
if (is_t4(ap->params.chip)) {
reg_ranges = &t4_reg_ranges[0];
arr_size = ARRAY_SIZE(t4_reg_ranges);
buf_size = T4_REGMAP_SIZE;
} else {
reg_ranges = &t5_reg_ranges[0];
arr_size = ARRAY_SIZE(t5_reg_ranges);
buf_size = T5_REGMAP_SIZE;
}
regs->version = mk_adap_vers(ap);
memset(buf, 0, buf_size);
for (i = 0; i < arr_size; i += 2)
reg_block_dump(ap, buf, reg_ranges[i], reg_ranges[i + 1]);
}
static int restart_autoneg(struct net_device *dev)
{
struct port_info *p = netdev_priv(dev);
if (!netif_running(dev))
return -EAGAIN;
if (p->link_cfg.autoneg != AUTONEG_ENABLE)
return -EINVAL;
t4_restart_aneg(p->adapter, p->adapter->fn, p->tx_chan);
return 0;
}
static int identify_port(struct net_device *dev,
enum ethtool_phys_id_state state)
{
unsigned int val;
struct adapter *adap = netdev2adap(dev);
if (state == ETHTOOL_ID_ACTIVE)
val = 0xffff;
else if (state == ETHTOOL_ID_INACTIVE)
val = 0;
else
return -EINVAL;
return t4_identify_port(adap, adap->fn, netdev2pinfo(dev)->viid, val);
}
static unsigned int from_fw_linkcaps(unsigned int type, unsigned int caps)
{
unsigned int v = 0;
if (type == FW_PORT_TYPE_BT_SGMII || type == FW_PORT_TYPE_BT_XFI ||
type == FW_PORT_TYPE_BT_XAUI) {
v |= SUPPORTED_TP;
if (caps & FW_PORT_CAP_SPEED_100M)
v |= SUPPORTED_100baseT_Full;
if (caps & FW_PORT_CAP_SPEED_1G)
v |= SUPPORTED_1000baseT_Full;
if (caps & FW_PORT_CAP_SPEED_10G)
v |= SUPPORTED_10000baseT_Full;
} else if (type == FW_PORT_TYPE_KX4 || type == FW_PORT_TYPE_KX) {
v |= SUPPORTED_Backplane;
if (caps & FW_PORT_CAP_SPEED_1G)
v |= SUPPORTED_1000baseKX_Full;
if (caps & FW_PORT_CAP_SPEED_10G)
v |= SUPPORTED_10000baseKX4_Full;
} else if (type == FW_PORT_TYPE_KR)
v |= SUPPORTED_Backplane | SUPPORTED_10000baseKR_Full;
else if (type == FW_PORT_TYPE_BP_AP)
v |= SUPPORTED_Backplane | SUPPORTED_10000baseR_FEC |
SUPPORTED_10000baseKR_Full | SUPPORTED_1000baseKX_Full;
else if (type == FW_PORT_TYPE_BP4_AP)
v |= SUPPORTED_Backplane | SUPPORTED_10000baseR_FEC |
SUPPORTED_10000baseKR_Full | SUPPORTED_1000baseKX_Full |
SUPPORTED_10000baseKX4_Full;
else if (type == FW_PORT_TYPE_FIBER_XFI ||
type == FW_PORT_TYPE_FIBER_XAUI || type == FW_PORT_TYPE_SFP)
v |= SUPPORTED_FIBRE;
else if (type == FW_PORT_TYPE_BP40_BA)
v |= SUPPORTED_40000baseSR4_Full;
if (caps & FW_PORT_CAP_ANEG)
v |= SUPPORTED_Autoneg;
return v;
}
static unsigned int to_fw_linkcaps(unsigned int caps)
{
unsigned int v = 0;
if (caps & ADVERTISED_100baseT_Full)
v |= FW_PORT_CAP_SPEED_100M;
if (caps & ADVERTISED_1000baseT_Full)
v |= FW_PORT_CAP_SPEED_1G;
if (caps & ADVERTISED_10000baseT_Full)
v |= FW_PORT_CAP_SPEED_10G;
if (caps & ADVERTISED_40000baseSR4_Full)
v |= FW_PORT_CAP_SPEED_40G;
return v;
}
static int get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
const struct port_info *p = netdev_priv(dev);
if (p->port_type == FW_PORT_TYPE_BT_SGMII ||
p->port_type == FW_PORT_TYPE_BT_XFI ||
p->port_type == FW_PORT_TYPE_BT_XAUI)
cmd->port = PORT_TP;
else if (p->port_type == FW_PORT_TYPE_FIBER_XFI ||
p->port_type == FW_PORT_TYPE_FIBER_XAUI)
cmd->port = PORT_FIBRE;
else if (p->port_type == FW_PORT_TYPE_SFP ||
p->port_type == FW_PORT_TYPE_QSFP_10G ||
p->port_type == FW_PORT_TYPE_QSFP) {
if (p->mod_type == FW_PORT_MOD_TYPE_LR ||
p->mod_type == FW_PORT_MOD_TYPE_SR ||
p->mod_type == FW_PORT_MOD_TYPE_ER ||
p->mod_type == FW_PORT_MOD_TYPE_LRM)
cmd->port = PORT_FIBRE;
else if (p->mod_type == FW_PORT_MOD_TYPE_TWINAX_PASSIVE ||
p->mod_type == FW_PORT_MOD_TYPE_TWINAX_ACTIVE)
cmd->port = PORT_DA;
else
cmd->port = PORT_OTHER;
} else
cmd->port = PORT_OTHER;
if (p->mdio_addr >= 0) {
cmd->phy_address = p->mdio_addr;
cmd->transceiver = XCVR_EXTERNAL;
cmd->mdio_support = p->port_type == FW_PORT_TYPE_BT_SGMII ?
MDIO_SUPPORTS_C22 : MDIO_SUPPORTS_C45;
} else {
cmd->phy_address = 0; /* not really, but no better option */
cmd->transceiver = XCVR_INTERNAL;
cmd->mdio_support = 0;
}
cmd->supported = from_fw_linkcaps(p->port_type, p->link_cfg.supported);
cmd->advertising = from_fw_linkcaps(p->port_type,
p->link_cfg.advertising);
ethtool_cmd_speed_set(cmd,
netif_carrier_ok(dev) ? p->link_cfg.speed : 0);
cmd->duplex = DUPLEX_FULL;
cmd->autoneg = p->link_cfg.autoneg;
cmd->maxtxpkt = 0;
cmd->maxrxpkt = 0;
return 0;
}
static unsigned int speed_to_caps(int speed)
{
if (speed == 100)
return FW_PORT_CAP_SPEED_100M;
if (speed == 1000)
return FW_PORT_CAP_SPEED_1G;
if (speed == 10000)
return FW_PORT_CAP_SPEED_10G;
if (speed == 40000)
return FW_PORT_CAP_SPEED_40G;
return 0;
}
static int set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
unsigned int cap;
struct port_info *p = netdev_priv(dev);
struct link_config *lc = &p->link_cfg;
u32 speed = ethtool_cmd_speed(cmd);
if (cmd->duplex != DUPLEX_FULL) /* only full-duplex supported */
return -EINVAL;
if (!(lc->supported & FW_PORT_CAP_ANEG)) {
/*
* PHY offers a single speed. See if that's what's
* being requested.
*/
if (cmd->autoneg == AUTONEG_DISABLE &&
(lc->supported & speed_to_caps(speed)))
return 0;
return -EINVAL;
}
if (cmd->autoneg == AUTONEG_DISABLE) {
cap = speed_to_caps(speed);
if (!(lc->supported & cap) ||
(speed == 1000) ||
(speed == 10000) ||
(speed == 40000))
return -EINVAL;
lc->requested_speed = cap;
lc->advertising = 0;
} else {
cap = to_fw_linkcaps(cmd->advertising);
if (!(lc->supported & cap))
return -EINVAL;
lc->requested_speed = 0;
lc->advertising = cap | FW_PORT_CAP_ANEG;
}
lc->autoneg = cmd->autoneg;
if (netif_running(dev))
return t4_link_start(p->adapter, p->adapter->fn, p->tx_chan,
lc);
return 0;
}
static void get_pauseparam(struct net_device *dev,
struct ethtool_pauseparam *epause)
{
struct port_info *p = netdev_priv(dev);
epause->autoneg = (p->link_cfg.requested_fc & PAUSE_AUTONEG) != 0;
epause->rx_pause = (p->link_cfg.fc & PAUSE_RX) != 0;
epause->tx_pause = (p->link_cfg.fc & PAUSE_TX) != 0;
}
static int set_pauseparam(struct net_device *dev,
struct ethtool_pauseparam *epause)
{
struct port_info *p = netdev_priv(dev);
struct link_config *lc = &p->link_cfg;
if (epause->autoneg == AUTONEG_DISABLE)
lc->requested_fc = 0;
else if (lc->supported & FW_PORT_CAP_ANEG)
lc->requested_fc = PAUSE_AUTONEG;
else
return -EINVAL;
if (epause->rx_pause)
lc->requested_fc |= PAUSE_RX;
if (epause->tx_pause)
lc->requested_fc |= PAUSE_TX;
if (netif_running(dev))
return t4_link_start(p->adapter, p->adapter->fn, p->tx_chan,
lc);
return 0;
}
static void get_sge_param(struct net_device *dev, struct ethtool_ringparam *e)
{
const struct port_info *pi = netdev_priv(dev);
const struct sge *s = &pi->adapter->sge;
e->rx_max_pending = MAX_RX_BUFFERS;
e->rx_mini_max_pending = MAX_RSPQ_ENTRIES;
e->rx_jumbo_max_pending = 0;
e->tx_max_pending = MAX_TXQ_ENTRIES;
e->rx_pending = s->ethrxq[pi->first_qset].fl.size - 8;
e->rx_mini_pending = s->ethrxq[pi->first_qset].rspq.size;
e->rx_jumbo_pending = 0;
e->tx_pending = s->ethtxq[pi->first_qset].q.size;
}
static int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e)
{
int i;
const struct port_info *pi = netdev_priv(dev);
struct adapter *adapter = pi->adapter;
struct sge *s = &adapter->sge;
if (e->rx_pending > MAX_RX_BUFFERS || e->rx_jumbo_pending ||
e->tx_pending > MAX_TXQ_ENTRIES ||
e->rx_mini_pending > MAX_RSPQ_ENTRIES ||
e->rx_mini_pending < MIN_RSPQ_ENTRIES ||
e->rx_pending < MIN_FL_ENTRIES || e->tx_pending < MIN_TXQ_ENTRIES)
return -EINVAL;
if (adapter->flags & FULL_INIT_DONE)
return -EBUSY;
for (i = 0; i < pi->nqsets; ++i) {
s->ethtxq[pi->first_qset + i].q.size = e->tx_pending;
s->ethrxq[pi->first_qset + i].fl.size = e->rx_pending + 8;
s->ethrxq[pi->first_qset + i].rspq.size = e->rx_mini_pending;
}
return 0;
}
static int closest_timer(const struct sge *s, int time)
{
int i,<