162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * This software is available to you under a choice of one of two
562306a36Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
662306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
762306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the
862306a36Sopenharmony_ci * OpenIB.org BSD license below:
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
1162306a36Sopenharmony_ci *     without modification, are permitted provided that the following
1262306a36Sopenharmony_ci *     conditions are met:
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *      - Redistributions of source code must retain the above
1562306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
1662306a36Sopenharmony_ci *        disclaimer.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
1962306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
2062306a36Sopenharmony_ci *        disclaimer in the documentation and/or other materials
2162306a36Sopenharmony_ci *        provided with the distribution.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2462306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2562306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2662306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
2762306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
2862306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
2962306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3062306a36Sopenharmony_ci * SOFTWARE.
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include <linux/module.h>
3662306a36Sopenharmony_ci#include <linux/init.h>
3762306a36Sopenharmony_ci#include <linux/pci.h>
3862306a36Sopenharmony_ci#include <linux/dma-mapping.h>
3962306a36Sopenharmony_ci#include <linux/netdevice.h>
4062306a36Sopenharmony_ci#include <linux/etherdevice.h>
4162306a36Sopenharmony_ci#include <linux/if_vlan.h>
4262306a36Sopenharmony_ci#include <linux/mdio.h>
4362306a36Sopenharmony_ci#include <linux/sockios.h>
4462306a36Sopenharmony_ci#include <linux/workqueue.h>
4562306a36Sopenharmony_ci#include <linux/proc_fs.h>
4662306a36Sopenharmony_ci#include <linux/rtnetlink.h>
4762306a36Sopenharmony_ci#include <linux/firmware.h>
4862306a36Sopenharmony_ci#include <linux/log2.h>
4962306a36Sopenharmony_ci#include <linux/stringify.h>
5062306a36Sopenharmony_ci#include <linux/sched.h>
5162306a36Sopenharmony_ci#include <linux/slab.h>
5262306a36Sopenharmony_ci#include <linux/uaccess.h>
5362306a36Sopenharmony_ci#include <linux/nospec.h>
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#include "common.h"
5662306a36Sopenharmony_ci#include "cxgb3_ioctl.h"
5762306a36Sopenharmony_ci#include "regs.h"
5862306a36Sopenharmony_ci#include "cxgb3_offload.h"
5962306a36Sopenharmony_ci#include "version.h"
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#include "cxgb3_ctl_defs.h"
6262306a36Sopenharmony_ci#include "t3_cpl.h"
6362306a36Sopenharmony_ci#include "firmware_exports.h"
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cienum {
6662306a36Sopenharmony_ci	MAX_TXQ_ENTRIES = 16384,
6762306a36Sopenharmony_ci	MAX_CTRL_TXQ_ENTRIES = 1024,
6862306a36Sopenharmony_ci	MAX_RSPQ_ENTRIES = 16384,
6962306a36Sopenharmony_ci	MAX_RX_BUFFERS = 16384,
7062306a36Sopenharmony_ci	MAX_RX_JUMBO_BUFFERS = 16384,
7162306a36Sopenharmony_ci	MIN_TXQ_ENTRIES = 4,
7262306a36Sopenharmony_ci	MIN_CTRL_TXQ_ENTRIES = 4,
7362306a36Sopenharmony_ci	MIN_RSPQ_ENTRIES = 32,
7462306a36Sopenharmony_ci	MIN_FL_ENTRIES = 32
7562306a36Sopenharmony_ci};
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#define PORT_MASK ((1 << MAX_NPORTS) - 1)
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci#define DFLT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | \
8062306a36Sopenharmony_ci			 NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP |\
8162306a36Sopenharmony_ci			 NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR)
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci#define EEPROM_MAGIC 0x38E2F10C
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci#define CH_DEVICE(devid, idx) \
8662306a36Sopenharmony_ci	{ PCI_VENDOR_ID_CHELSIO, devid, PCI_ANY_ID, PCI_ANY_ID, 0, 0, idx }
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic const struct pci_device_id cxgb3_pci_tbl[] = {
8962306a36Sopenharmony_ci	CH_DEVICE(0x20, 0),	/* PE9000 */
9062306a36Sopenharmony_ci	CH_DEVICE(0x21, 1),	/* T302E */
9162306a36Sopenharmony_ci	CH_DEVICE(0x22, 2),	/* T310E */
9262306a36Sopenharmony_ci	CH_DEVICE(0x23, 3),	/* T320X */
9362306a36Sopenharmony_ci	CH_DEVICE(0x24, 1),	/* T302X */
9462306a36Sopenharmony_ci	CH_DEVICE(0x25, 3),	/* T320E */
9562306a36Sopenharmony_ci	CH_DEVICE(0x26, 2),	/* T310X */
9662306a36Sopenharmony_ci	CH_DEVICE(0x30, 2),	/* T3B10 */
9762306a36Sopenharmony_ci	CH_DEVICE(0x31, 3),	/* T3B20 */
9862306a36Sopenharmony_ci	CH_DEVICE(0x32, 1),	/* T3B02 */
9962306a36Sopenharmony_ci	CH_DEVICE(0x35, 6),	/* T3C20-derived T3C10 */
10062306a36Sopenharmony_ci	CH_DEVICE(0x36, 3),	/* S320E-CR */
10162306a36Sopenharmony_ci	CH_DEVICE(0x37, 7),	/* N320E-G2 */
10262306a36Sopenharmony_ci	{0,}
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESC);
10662306a36Sopenharmony_ciMODULE_AUTHOR("Chelsio Communications");
10762306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
10862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cxgb3_pci_tbl);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic int dflt_msg_enable = DFLT_MSG_ENABLE;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cimodule_param(dflt_msg_enable, int, 0644);
11362306a36Sopenharmony_ciMODULE_PARM_DESC(dflt_msg_enable, "Chelsio T3 default message enable bitmap");
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/*
11662306a36Sopenharmony_ci * The driver uses the best interrupt scheme available on a platform in the
11762306a36Sopenharmony_ci * order MSI-X, MSI, legacy pin interrupts.  This parameter determines which
11862306a36Sopenharmony_ci * of these schemes the driver may consider as follows:
11962306a36Sopenharmony_ci *
12062306a36Sopenharmony_ci * msi = 2: choose from among all three options
12162306a36Sopenharmony_ci * msi = 1: only consider MSI and pin interrupts
12262306a36Sopenharmony_ci * msi = 0: force pin interrupts
12362306a36Sopenharmony_ci */
12462306a36Sopenharmony_cistatic int msi = 2;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cimodule_param(msi, int, 0644);
12762306a36Sopenharmony_ciMODULE_PARM_DESC(msi, "whether to use MSI or MSI-X");
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/*
13062306a36Sopenharmony_ci * The driver enables offload as a default.
13162306a36Sopenharmony_ci * To disable it, use ofld_disable = 1.
13262306a36Sopenharmony_ci */
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic int ofld_disable = 0;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cimodule_param(ofld_disable, int, 0644);
13762306a36Sopenharmony_ciMODULE_PARM_DESC(ofld_disable, "whether to enable offload at init time or not");
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/*
14062306a36Sopenharmony_ci * We have work elements that we need to cancel when an interface is taken
14162306a36Sopenharmony_ci * down.  Normally the work elements would be executed by keventd but that
14262306a36Sopenharmony_ci * can deadlock because of linkwatch.  If our close method takes the rtnl
14362306a36Sopenharmony_ci * lock and linkwatch is ahead of our work elements in keventd, linkwatch
14462306a36Sopenharmony_ci * will block keventd as it needs the rtnl lock, and we'll deadlock waiting
14562306a36Sopenharmony_ci * for our work to complete.  Get our own work queue to solve this.
14662306a36Sopenharmony_ci */
14762306a36Sopenharmony_cistruct workqueue_struct *cxgb3_wq;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci/**
15062306a36Sopenharmony_ci *	link_report - show link status and link speed/duplex
15162306a36Sopenharmony_ci *	@dev: the port whose settings are to be reported
15262306a36Sopenharmony_ci *
15362306a36Sopenharmony_ci *	Shows the link status, speed, and duplex of a port.
15462306a36Sopenharmony_ci */
15562306a36Sopenharmony_cistatic void link_report(struct net_device *dev)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	if (!netif_carrier_ok(dev))
15862306a36Sopenharmony_ci		netdev_info(dev, "link down\n");
15962306a36Sopenharmony_ci	else {
16062306a36Sopenharmony_ci		const char *s = "10Mbps";
16162306a36Sopenharmony_ci		const struct port_info *p = netdev_priv(dev);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci		switch (p->link_config.speed) {
16462306a36Sopenharmony_ci		case SPEED_10000:
16562306a36Sopenharmony_ci			s = "10Gbps";
16662306a36Sopenharmony_ci			break;
16762306a36Sopenharmony_ci		case SPEED_1000:
16862306a36Sopenharmony_ci			s = "1000Mbps";
16962306a36Sopenharmony_ci			break;
17062306a36Sopenharmony_ci		case SPEED_100:
17162306a36Sopenharmony_ci			s = "100Mbps";
17262306a36Sopenharmony_ci			break;
17362306a36Sopenharmony_ci		}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci		netdev_info(dev, "link up, %s, %s-duplex\n",
17662306a36Sopenharmony_ci			    s, p->link_config.duplex == DUPLEX_FULL
17762306a36Sopenharmony_ci			    ? "full" : "half");
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic void enable_tx_fifo_drain(struct adapter *adapter,
18262306a36Sopenharmony_ci				 struct port_info *pi)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	t3_set_reg_field(adapter, A_XGM_TXFIFO_CFG + pi->mac.offset, 0,
18562306a36Sopenharmony_ci			 F_ENDROPPKT);
18662306a36Sopenharmony_ci	t3_write_reg(adapter, A_XGM_RX_CTRL + pi->mac.offset, 0);
18762306a36Sopenharmony_ci	t3_write_reg(adapter, A_XGM_TX_CTRL + pi->mac.offset, F_TXEN);
18862306a36Sopenharmony_ci	t3_write_reg(adapter, A_XGM_RX_CTRL + pi->mac.offset, F_RXEN);
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic void disable_tx_fifo_drain(struct adapter *adapter,
19262306a36Sopenharmony_ci				  struct port_info *pi)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	t3_set_reg_field(adapter, A_XGM_TXFIFO_CFG + pi->mac.offset,
19562306a36Sopenharmony_ci			 F_ENDROPPKT, 0);
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_civoid t3_os_link_fault(struct adapter *adap, int port_id, int state)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	struct net_device *dev = adap->port[port_id];
20162306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (state == netif_carrier_ok(dev))
20462306a36Sopenharmony_ci		return;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	if (state) {
20762306a36Sopenharmony_ci		struct cmac *mac = &pi->mac;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci		netif_carrier_on(dev);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci		disable_tx_fifo_drain(adap, pi);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci		/* Clear local faults */
21462306a36Sopenharmony_ci		t3_xgm_intr_disable(adap, pi->port_id);
21562306a36Sopenharmony_ci		t3_read_reg(adap, A_XGM_INT_STATUS +
21662306a36Sopenharmony_ci				    pi->mac.offset);
21762306a36Sopenharmony_ci		t3_write_reg(adap,
21862306a36Sopenharmony_ci			     A_XGM_INT_CAUSE + pi->mac.offset,
21962306a36Sopenharmony_ci			     F_XGM_INT);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci		t3_set_reg_field(adap,
22262306a36Sopenharmony_ci				 A_XGM_INT_ENABLE +
22362306a36Sopenharmony_ci				 pi->mac.offset,
22462306a36Sopenharmony_ci				 F_XGM_INT, F_XGM_INT);
22562306a36Sopenharmony_ci		t3_xgm_intr_enable(adap, pi->port_id);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci		t3_mac_enable(mac, MAC_DIRECTION_TX);
22862306a36Sopenharmony_ci	} else {
22962306a36Sopenharmony_ci		netif_carrier_off(dev);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci		/* Flush TX FIFO */
23262306a36Sopenharmony_ci		enable_tx_fifo_drain(adap, pi);
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci	link_report(dev);
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci/**
23862306a36Sopenharmony_ci *	t3_os_link_changed - handle link status changes
23962306a36Sopenharmony_ci *	@adapter: the adapter associated with the link change
24062306a36Sopenharmony_ci *	@port_id: the port index whose limk status has changed
24162306a36Sopenharmony_ci *	@link_stat: the new status of the link
24262306a36Sopenharmony_ci *	@speed: the new speed setting
24362306a36Sopenharmony_ci *	@duplex: the new duplex setting
24462306a36Sopenharmony_ci *	@pause: the new flow-control setting
24562306a36Sopenharmony_ci *
24662306a36Sopenharmony_ci *	This is the OS-dependent handler for link status changes.  The OS
24762306a36Sopenharmony_ci *	neutral handler takes care of most of the processing for these events,
24862306a36Sopenharmony_ci *	then calls this handler for any OS-specific processing.
24962306a36Sopenharmony_ci */
25062306a36Sopenharmony_civoid t3_os_link_changed(struct adapter *adapter, int port_id, int link_stat,
25162306a36Sopenharmony_ci			int speed, int duplex, int pause)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct net_device *dev = adapter->port[port_id];
25462306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
25562306a36Sopenharmony_ci	struct cmac *mac = &pi->mac;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	/* Skip changes from disabled ports. */
25862306a36Sopenharmony_ci	if (!netif_running(dev))
25962306a36Sopenharmony_ci		return;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	if (link_stat != netif_carrier_ok(dev)) {
26262306a36Sopenharmony_ci		if (link_stat) {
26362306a36Sopenharmony_ci			disable_tx_fifo_drain(adapter, pi);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci			t3_mac_enable(mac, MAC_DIRECTION_RX);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci			/* Clear local faults */
26862306a36Sopenharmony_ci			t3_xgm_intr_disable(adapter, pi->port_id);
26962306a36Sopenharmony_ci			t3_read_reg(adapter, A_XGM_INT_STATUS +
27062306a36Sopenharmony_ci				    pi->mac.offset);
27162306a36Sopenharmony_ci			t3_write_reg(adapter,
27262306a36Sopenharmony_ci				     A_XGM_INT_CAUSE + pi->mac.offset,
27362306a36Sopenharmony_ci				     F_XGM_INT);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci			t3_set_reg_field(adapter,
27662306a36Sopenharmony_ci					 A_XGM_INT_ENABLE + pi->mac.offset,
27762306a36Sopenharmony_ci					 F_XGM_INT, F_XGM_INT);
27862306a36Sopenharmony_ci			t3_xgm_intr_enable(adapter, pi->port_id);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci			netif_carrier_on(dev);
28162306a36Sopenharmony_ci		} else {
28262306a36Sopenharmony_ci			netif_carrier_off(dev);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci			t3_xgm_intr_disable(adapter, pi->port_id);
28562306a36Sopenharmony_ci			t3_read_reg(adapter, A_XGM_INT_STATUS + pi->mac.offset);
28662306a36Sopenharmony_ci			t3_set_reg_field(adapter,
28762306a36Sopenharmony_ci					 A_XGM_INT_ENABLE + pi->mac.offset,
28862306a36Sopenharmony_ci					 F_XGM_INT, 0);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci			if (is_10G(adapter))
29162306a36Sopenharmony_ci				pi->phy.ops->power_down(&pi->phy, 1);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci			t3_read_reg(adapter, A_XGM_INT_STATUS + pi->mac.offset);
29462306a36Sopenharmony_ci			t3_mac_disable(mac, MAC_DIRECTION_RX);
29562306a36Sopenharmony_ci			t3_link_start(&pi->phy, mac, &pi->link_config);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci			/* Flush TX FIFO */
29862306a36Sopenharmony_ci			enable_tx_fifo_drain(adapter, pi);
29962306a36Sopenharmony_ci		}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci		link_report(dev);
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci/**
30662306a36Sopenharmony_ci *	t3_os_phymod_changed - handle PHY module changes
30762306a36Sopenharmony_ci *	@adap: the adapter associated with the link change
30862306a36Sopenharmony_ci *	@port_id: the port index whose limk status has changed
30962306a36Sopenharmony_ci *
31062306a36Sopenharmony_ci *	This is the OS-dependent handler for PHY module changes.  It is
31162306a36Sopenharmony_ci *	invoked when a PHY module is removed or inserted for any OS-specific
31262306a36Sopenharmony_ci *	processing.
31362306a36Sopenharmony_ci */
31462306a36Sopenharmony_civoid t3_os_phymod_changed(struct adapter *adap, int port_id)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	static const char *mod_str[] = {
31762306a36Sopenharmony_ci		NULL, "SR", "LR", "LRM", "TWINAX", "TWINAX", "unknown"
31862306a36Sopenharmony_ci	};
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	const struct net_device *dev = adap->port[port_id];
32162306a36Sopenharmony_ci	const struct port_info *pi = netdev_priv(dev);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	if (pi->phy.modtype == phy_modtype_none)
32462306a36Sopenharmony_ci		netdev_info(dev, "PHY module unplugged\n");
32562306a36Sopenharmony_ci	else
32662306a36Sopenharmony_ci		netdev_info(dev, "%s PHY module inserted\n",
32762306a36Sopenharmony_ci			    mod_str[pi->phy.modtype]);
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic void cxgb_set_rxmode(struct net_device *dev)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	t3_mac_set_rx_mode(&pi->mac, dev);
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci/**
33862306a36Sopenharmony_ci *	link_start - enable a port
33962306a36Sopenharmony_ci *	@dev: the device to enable
34062306a36Sopenharmony_ci *
34162306a36Sopenharmony_ci *	Performs the MAC and PHY actions needed to enable a port.
34262306a36Sopenharmony_ci */
34362306a36Sopenharmony_cistatic void link_start(struct net_device *dev)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
34662306a36Sopenharmony_ci	struct cmac *mac = &pi->mac;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	t3_mac_reset(mac);
34962306a36Sopenharmony_ci	t3_mac_set_num_ucast(mac, MAX_MAC_IDX);
35062306a36Sopenharmony_ci	t3_mac_set_mtu(mac, dev->mtu);
35162306a36Sopenharmony_ci	t3_mac_set_address(mac, LAN_MAC_IDX, dev->dev_addr);
35262306a36Sopenharmony_ci	t3_mac_set_address(mac, SAN_MAC_IDX, pi->iscsic.mac_addr);
35362306a36Sopenharmony_ci	t3_mac_set_rx_mode(mac, dev);
35462306a36Sopenharmony_ci	t3_link_start(&pi->phy, mac, &pi->link_config);
35562306a36Sopenharmony_ci	t3_mac_enable(mac, MAC_DIRECTION_RX | MAC_DIRECTION_TX);
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic inline void cxgb_disable_msi(struct adapter *adapter)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	if (adapter->flags & USING_MSIX) {
36162306a36Sopenharmony_ci		pci_disable_msix(adapter->pdev);
36262306a36Sopenharmony_ci		adapter->flags &= ~USING_MSIX;
36362306a36Sopenharmony_ci	} else if (adapter->flags & USING_MSI) {
36462306a36Sopenharmony_ci		pci_disable_msi(adapter->pdev);
36562306a36Sopenharmony_ci		adapter->flags &= ~USING_MSI;
36662306a36Sopenharmony_ci	}
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci/*
37062306a36Sopenharmony_ci * Interrupt handler for asynchronous events used with MSI-X.
37162306a36Sopenharmony_ci */
37262306a36Sopenharmony_cistatic irqreturn_t t3_async_intr_handler(int irq, void *cookie)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	t3_slow_intr_handler(cookie);
37562306a36Sopenharmony_ci	return IRQ_HANDLED;
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci/*
37962306a36Sopenharmony_ci * Name the MSI-X interrupts.
38062306a36Sopenharmony_ci */
38162306a36Sopenharmony_cistatic void name_msix_vecs(struct adapter *adap)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	int i, j, msi_idx = 1, n = sizeof(adap->msix_info[0].desc) - 1;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	snprintf(adap->msix_info[0].desc, n, "%s", adap->name);
38662306a36Sopenharmony_ci	adap->msix_info[0].desc[n] = 0;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	for_each_port(adap, j) {
38962306a36Sopenharmony_ci		struct net_device *d = adap->port[j];
39062306a36Sopenharmony_ci		const struct port_info *pi = netdev_priv(d);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci		for (i = 0; i < pi->nqsets; i++, msi_idx++) {
39362306a36Sopenharmony_ci			snprintf(adap->msix_info[msi_idx].desc, n,
39462306a36Sopenharmony_ci				 "%s-%d", d->name, pi->first_qset + i);
39562306a36Sopenharmony_ci			adap->msix_info[msi_idx].desc[n] = 0;
39662306a36Sopenharmony_ci		}
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic int request_msix_data_irqs(struct adapter *adap)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	int i, j, err, qidx = 0;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	for_each_port(adap, i) {
40562306a36Sopenharmony_ci		int nqsets = adap2pinfo(adap, i)->nqsets;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci		for (j = 0; j < nqsets; ++j) {
40862306a36Sopenharmony_ci			err = request_irq(adap->msix_info[qidx + 1].vec,
40962306a36Sopenharmony_ci					  t3_intr_handler(adap,
41062306a36Sopenharmony_ci							  adap->sge.qs[qidx].
41162306a36Sopenharmony_ci							  rspq.polling), 0,
41262306a36Sopenharmony_ci					  adap->msix_info[qidx + 1].desc,
41362306a36Sopenharmony_ci					  &adap->sge.qs[qidx]);
41462306a36Sopenharmony_ci			if (err) {
41562306a36Sopenharmony_ci				while (--qidx >= 0)
41662306a36Sopenharmony_ci					free_irq(adap->msix_info[qidx + 1].vec,
41762306a36Sopenharmony_ci						 &adap->sge.qs[qidx]);
41862306a36Sopenharmony_ci				return err;
41962306a36Sopenharmony_ci			}
42062306a36Sopenharmony_ci			qidx++;
42162306a36Sopenharmony_ci		}
42262306a36Sopenharmony_ci	}
42362306a36Sopenharmony_ci	return 0;
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic void free_irq_resources(struct adapter *adapter)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	if (adapter->flags & USING_MSIX) {
42962306a36Sopenharmony_ci		int i, n = 0;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci		free_irq(adapter->msix_info[0].vec, adapter);
43262306a36Sopenharmony_ci		for_each_port(adapter, i)
43362306a36Sopenharmony_ci			n += adap2pinfo(adapter, i)->nqsets;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci		for (i = 0; i < n; ++i)
43662306a36Sopenharmony_ci			free_irq(adapter->msix_info[i + 1].vec,
43762306a36Sopenharmony_ci				 &adapter->sge.qs[i]);
43862306a36Sopenharmony_ci	} else
43962306a36Sopenharmony_ci		free_irq(adapter->pdev->irq, adapter);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic int await_mgmt_replies(struct adapter *adap, unsigned long init_cnt,
44362306a36Sopenharmony_ci			      unsigned long n)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	int attempts = 10;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	while (adap->sge.qs[0].rspq.offload_pkts < init_cnt + n) {
44862306a36Sopenharmony_ci		if (!--attempts)
44962306a36Sopenharmony_ci			return -ETIMEDOUT;
45062306a36Sopenharmony_ci		msleep(10);
45162306a36Sopenharmony_ci	}
45262306a36Sopenharmony_ci	return 0;
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic int init_tp_parity(struct adapter *adap)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	int i;
45862306a36Sopenharmony_ci	struct sk_buff *skb;
45962306a36Sopenharmony_ci	struct cpl_set_tcb_field *greq;
46062306a36Sopenharmony_ci	unsigned long cnt = adap->sge.qs[0].rspq.offload_pkts;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	t3_tp_set_offload_mode(adap, 1);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	for (i = 0; i < 16; i++) {
46562306a36Sopenharmony_ci		struct cpl_smt_write_req *req;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci		skb = alloc_skb(sizeof(*req), GFP_KERNEL);
46862306a36Sopenharmony_ci		if (!skb)
46962306a36Sopenharmony_ci			skb = adap->nofail_skb;
47062306a36Sopenharmony_ci		if (!skb)
47162306a36Sopenharmony_ci			goto alloc_skb_fail;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci		req = __skb_put_zero(skb, sizeof(*req));
47462306a36Sopenharmony_ci		req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
47562306a36Sopenharmony_ci		OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SMT_WRITE_REQ, i));
47662306a36Sopenharmony_ci		req->mtu_idx = NMTUS - 1;
47762306a36Sopenharmony_ci		req->iff = i;
47862306a36Sopenharmony_ci		t3_mgmt_tx(adap, skb);
47962306a36Sopenharmony_ci		if (skb == adap->nofail_skb) {
48062306a36Sopenharmony_ci			await_mgmt_replies(adap, cnt, i + 1);
48162306a36Sopenharmony_ci			adap->nofail_skb = alloc_skb(sizeof(*greq), GFP_KERNEL);
48262306a36Sopenharmony_ci			if (!adap->nofail_skb)
48362306a36Sopenharmony_ci				goto alloc_skb_fail;
48462306a36Sopenharmony_ci		}
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	for (i = 0; i < 2048; i++) {
48862306a36Sopenharmony_ci		struct cpl_l2t_write_req *req;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci		skb = alloc_skb(sizeof(*req), GFP_KERNEL);
49162306a36Sopenharmony_ci		if (!skb)
49262306a36Sopenharmony_ci			skb = adap->nofail_skb;
49362306a36Sopenharmony_ci		if (!skb)
49462306a36Sopenharmony_ci			goto alloc_skb_fail;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci		req = __skb_put_zero(skb, sizeof(*req));
49762306a36Sopenharmony_ci		req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
49862306a36Sopenharmony_ci		OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_L2T_WRITE_REQ, i));
49962306a36Sopenharmony_ci		req->params = htonl(V_L2T_W_IDX(i));
50062306a36Sopenharmony_ci		t3_mgmt_tx(adap, skb);
50162306a36Sopenharmony_ci		if (skb == adap->nofail_skb) {
50262306a36Sopenharmony_ci			await_mgmt_replies(adap, cnt, 16 + i + 1);
50362306a36Sopenharmony_ci			adap->nofail_skb = alloc_skb(sizeof(*greq), GFP_KERNEL);
50462306a36Sopenharmony_ci			if (!adap->nofail_skb)
50562306a36Sopenharmony_ci				goto alloc_skb_fail;
50662306a36Sopenharmony_ci		}
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	for (i = 0; i < 2048; i++) {
51062306a36Sopenharmony_ci		struct cpl_rte_write_req *req;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci		skb = alloc_skb(sizeof(*req), GFP_KERNEL);
51362306a36Sopenharmony_ci		if (!skb)
51462306a36Sopenharmony_ci			skb = adap->nofail_skb;
51562306a36Sopenharmony_ci		if (!skb)
51662306a36Sopenharmony_ci			goto alloc_skb_fail;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci		req = __skb_put_zero(skb, sizeof(*req));
51962306a36Sopenharmony_ci		req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
52062306a36Sopenharmony_ci		OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_RTE_WRITE_REQ, i));
52162306a36Sopenharmony_ci		req->l2t_idx = htonl(V_L2T_W_IDX(i));
52262306a36Sopenharmony_ci		t3_mgmt_tx(adap, skb);
52362306a36Sopenharmony_ci		if (skb == adap->nofail_skb) {
52462306a36Sopenharmony_ci			await_mgmt_replies(adap, cnt, 16 + 2048 + i + 1);
52562306a36Sopenharmony_ci			adap->nofail_skb = alloc_skb(sizeof(*greq), GFP_KERNEL);
52662306a36Sopenharmony_ci			if (!adap->nofail_skb)
52762306a36Sopenharmony_ci				goto alloc_skb_fail;
52862306a36Sopenharmony_ci		}
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	skb = alloc_skb(sizeof(*greq), GFP_KERNEL);
53262306a36Sopenharmony_ci	if (!skb)
53362306a36Sopenharmony_ci		skb = adap->nofail_skb;
53462306a36Sopenharmony_ci	if (!skb)
53562306a36Sopenharmony_ci		goto alloc_skb_fail;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	greq = __skb_put_zero(skb, sizeof(*greq));
53862306a36Sopenharmony_ci	greq->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
53962306a36Sopenharmony_ci	OPCODE_TID(greq) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, 0));
54062306a36Sopenharmony_ci	greq->mask = cpu_to_be64(1);
54162306a36Sopenharmony_ci	t3_mgmt_tx(adap, skb);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	i = await_mgmt_replies(adap, cnt, 16 + 2048 + 2048 + 1);
54462306a36Sopenharmony_ci	if (skb == adap->nofail_skb) {
54562306a36Sopenharmony_ci		i = await_mgmt_replies(adap, cnt, 16 + 2048 + 2048 + 1);
54662306a36Sopenharmony_ci		adap->nofail_skb = alloc_skb(sizeof(*greq), GFP_KERNEL);
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	t3_tp_set_offload_mode(adap, 0);
55062306a36Sopenharmony_ci	return i;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cialloc_skb_fail:
55362306a36Sopenharmony_ci	t3_tp_set_offload_mode(adap, 0);
55462306a36Sopenharmony_ci	return -ENOMEM;
55562306a36Sopenharmony_ci}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci/**
55862306a36Sopenharmony_ci *	setup_rss - configure RSS
55962306a36Sopenharmony_ci *	@adap: the adapter
56062306a36Sopenharmony_ci *
56162306a36Sopenharmony_ci *	Sets up RSS to distribute packets to multiple receive queues.  We
56262306a36Sopenharmony_ci *	configure the RSS CPU lookup table to distribute to the number of HW
56362306a36Sopenharmony_ci *	receive queues, and the response queue lookup table to narrow that
56462306a36Sopenharmony_ci *	down to the response queues actually configured for each port.
56562306a36Sopenharmony_ci *	We always configure the RSS mapping for two ports since the mapping
56662306a36Sopenharmony_ci *	table has plenty of entries.
56762306a36Sopenharmony_ci */
56862306a36Sopenharmony_cistatic void setup_rss(struct adapter *adap)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	int i;
57162306a36Sopenharmony_ci	unsigned int nq0 = adap2pinfo(adap, 0)->nqsets;
57262306a36Sopenharmony_ci	unsigned int nq1 = adap->port[1] ? adap2pinfo(adap, 1)->nqsets : 1;
57362306a36Sopenharmony_ci	u8 cpus[SGE_QSETS + 1];
57462306a36Sopenharmony_ci	u16 rspq_map[RSS_TABLE_SIZE + 1];
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	for (i = 0; i < SGE_QSETS; ++i)
57762306a36Sopenharmony_ci		cpus[i] = i;
57862306a36Sopenharmony_ci	cpus[SGE_QSETS] = 0xff;	/* terminator */
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	for (i = 0; i < RSS_TABLE_SIZE / 2; ++i) {
58162306a36Sopenharmony_ci		rspq_map[i] = i % nq0;
58262306a36Sopenharmony_ci		rspq_map[i + RSS_TABLE_SIZE / 2] = (i % nq1) + nq0;
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci	rspq_map[RSS_TABLE_SIZE] = 0xffff; /* terminator */
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	t3_config_rss(adap, F_RQFEEDBACKENABLE | F_TNLLKPEN | F_TNLMAPEN |
58762306a36Sopenharmony_ci		      F_TNLPRTEN | F_TNL2TUPEN | F_TNL4TUPEN |
58862306a36Sopenharmony_ci		      V_RRCPLCPUSIZE(6) | F_HASHTOEPLITZ, cpus, rspq_map);
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_cistatic void ring_dbs(struct adapter *adap)
59262306a36Sopenharmony_ci{
59362306a36Sopenharmony_ci	int i, j;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	for (i = 0; i < SGE_QSETS; i++) {
59662306a36Sopenharmony_ci		struct sge_qset *qs = &adap->sge.qs[i];
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci		if (qs->adap)
59962306a36Sopenharmony_ci			for (j = 0; j < SGE_TXQ_PER_SET; j++)
60062306a36Sopenharmony_ci				t3_write_reg(adap, A_SG_KDOORBELL, F_SELEGRCNTX | V_EGRCNTX(qs->txq[j].cntxt_id));
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cistatic void init_napi(struct adapter *adap)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	int i;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	for (i = 0; i < SGE_QSETS; i++) {
60962306a36Sopenharmony_ci		struct sge_qset *qs = &adap->sge.qs[i];
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci		if (qs->adap)
61262306a36Sopenharmony_ci			netif_napi_add(qs->netdev, &qs->napi, qs->napi.poll);
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	/*
61662306a36Sopenharmony_ci	 * netif_napi_add() can be called only once per napi_struct because it
61762306a36Sopenharmony_ci	 * adds each new napi_struct to a list.  Be careful not to call it a
61862306a36Sopenharmony_ci	 * second time, e.g., during EEH recovery, by making a note of it.
61962306a36Sopenharmony_ci	 */
62062306a36Sopenharmony_ci	adap->flags |= NAPI_INIT;
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci/*
62462306a36Sopenharmony_ci * Wait until all NAPI handlers are descheduled.  This includes the handlers of
62562306a36Sopenharmony_ci * both netdevices representing interfaces and the dummy ones for the extra
62662306a36Sopenharmony_ci * queues.
62762306a36Sopenharmony_ci */
62862306a36Sopenharmony_cistatic void quiesce_rx(struct adapter *adap)
62962306a36Sopenharmony_ci{
63062306a36Sopenharmony_ci	int i;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	for (i = 0; i < SGE_QSETS; i++)
63362306a36Sopenharmony_ci		if (adap->sge.qs[i].adap)
63462306a36Sopenharmony_ci			napi_disable(&adap->sge.qs[i].napi);
63562306a36Sopenharmony_ci}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_cistatic void enable_all_napi(struct adapter *adap)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	int i;
64062306a36Sopenharmony_ci	for (i = 0; i < SGE_QSETS; i++)
64162306a36Sopenharmony_ci		if (adap->sge.qs[i].adap)
64262306a36Sopenharmony_ci			napi_enable(&adap->sge.qs[i].napi);
64362306a36Sopenharmony_ci}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci/**
64662306a36Sopenharmony_ci *	setup_sge_qsets - configure SGE Tx/Rx/response queues
64762306a36Sopenharmony_ci *	@adap: the adapter
64862306a36Sopenharmony_ci *
64962306a36Sopenharmony_ci *	Determines how many sets of SGE queues to use and initializes them.
65062306a36Sopenharmony_ci *	We support multiple queue sets per port if we have MSI-X, otherwise
65162306a36Sopenharmony_ci *	just one queue set per port.
65262306a36Sopenharmony_ci */
65362306a36Sopenharmony_cistatic int setup_sge_qsets(struct adapter *adap)
65462306a36Sopenharmony_ci{
65562306a36Sopenharmony_ci	int i, j, err, irq_idx = 0, qset_idx = 0;
65662306a36Sopenharmony_ci	unsigned int ntxq = SGE_TXQ_PER_SET;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	if (adap->params.rev > 0 && !(adap->flags & USING_MSI))
65962306a36Sopenharmony_ci		irq_idx = -1;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	for_each_port(adap, i) {
66262306a36Sopenharmony_ci		struct net_device *dev = adap->port[i];
66362306a36Sopenharmony_ci		struct port_info *pi = netdev_priv(dev);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci		pi->qs = &adap->sge.qs[pi->first_qset];
66662306a36Sopenharmony_ci		for (j = 0; j < pi->nqsets; ++j, ++qset_idx) {
66762306a36Sopenharmony_ci			err = t3_sge_alloc_qset(adap, qset_idx, 1,
66862306a36Sopenharmony_ci				(adap->flags & USING_MSIX) ? qset_idx + 1 :
66962306a36Sopenharmony_ci							     irq_idx,
67062306a36Sopenharmony_ci				&adap->params.sge.qset[qset_idx], ntxq, dev,
67162306a36Sopenharmony_ci				netdev_get_tx_queue(dev, j));
67262306a36Sopenharmony_ci			if (err) {
67362306a36Sopenharmony_ci				t3_free_sge_resources(adap);
67462306a36Sopenharmony_ci				return err;
67562306a36Sopenharmony_ci			}
67662306a36Sopenharmony_ci		}
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	return 0;
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic ssize_t attr_show(struct device *d, char *buf,
68362306a36Sopenharmony_ci			 ssize_t(*format) (struct net_device *, char *))
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	ssize_t len;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	/* Synchronize with ioctls that may shut down the device */
68862306a36Sopenharmony_ci	rtnl_lock();
68962306a36Sopenharmony_ci	len = (*format) (to_net_dev(d), buf);
69062306a36Sopenharmony_ci	rtnl_unlock();
69162306a36Sopenharmony_ci	return len;
69262306a36Sopenharmony_ci}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_cistatic ssize_t attr_store(struct device *d,
69562306a36Sopenharmony_ci			  const char *buf, size_t len,
69662306a36Sopenharmony_ci			  ssize_t(*set) (struct net_device *, unsigned int),
69762306a36Sopenharmony_ci			  unsigned int min_val, unsigned int max_val)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	ssize_t ret;
70062306a36Sopenharmony_ci	unsigned int val;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	if (!capable(CAP_NET_ADMIN))
70362306a36Sopenharmony_ci		return -EPERM;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	ret = kstrtouint(buf, 0, &val);
70662306a36Sopenharmony_ci	if (ret)
70762306a36Sopenharmony_ci		return ret;
70862306a36Sopenharmony_ci	if (val < min_val || val > max_val)
70962306a36Sopenharmony_ci		return -EINVAL;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	rtnl_lock();
71262306a36Sopenharmony_ci	ret = (*set) (to_net_dev(d), val);
71362306a36Sopenharmony_ci	if (!ret)
71462306a36Sopenharmony_ci		ret = len;
71562306a36Sopenharmony_ci	rtnl_unlock();
71662306a36Sopenharmony_ci	return ret;
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci#define CXGB3_SHOW(name, val_expr) \
72062306a36Sopenharmony_cistatic ssize_t format_##name(struct net_device *dev, char *buf) \
72162306a36Sopenharmony_ci{ \
72262306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev); \
72362306a36Sopenharmony_ci	struct adapter *adap = pi->adapter; \
72462306a36Sopenharmony_ci	return sprintf(buf, "%u\n", val_expr); \
72562306a36Sopenharmony_ci} \
72662306a36Sopenharmony_cistatic ssize_t show_##name(struct device *d, struct device_attribute *attr, \
72762306a36Sopenharmony_ci			   char *buf) \
72862306a36Sopenharmony_ci{ \
72962306a36Sopenharmony_ci	return attr_show(d, buf, format_##name); \
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_cistatic ssize_t set_nfilters(struct net_device *dev, unsigned int val)
73362306a36Sopenharmony_ci{
73462306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
73562306a36Sopenharmony_ci	struct adapter *adap = pi->adapter;
73662306a36Sopenharmony_ci	int min_tids = is_offload(adap) ? MC5_MIN_TIDS : 0;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	if (adap->flags & FULL_INIT_DONE)
73962306a36Sopenharmony_ci		return -EBUSY;
74062306a36Sopenharmony_ci	if (val && adap->params.rev == 0)
74162306a36Sopenharmony_ci		return -EINVAL;
74262306a36Sopenharmony_ci	if (val > t3_mc5_size(&adap->mc5) - adap->params.mc5.nservers -
74362306a36Sopenharmony_ci	    min_tids)
74462306a36Sopenharmony_ci		return -EINVAL;
74562306a36Sopenharmony_ci	adap->params.mc5.nfilters = val;
74662306a36Sopenharmony_ci	return 0;
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_cistatic ssize_t store_nfilters(struct device *d, struct device_attribute *attr,
75062306a36Sopenharmony_ci			      const char *buf, size_t len)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	return attr_store(d, buf, len, set_nfilters, 0, ~0);
75362306a36Sopenharmony_ci}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_cistatic ssize_t set_nservers(struct net_device *dev, unsigned int val)
75662306a36Sopenharmony_ci{
75762306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
75862306a36Sopenharmony_ci	struct adapter *adap = pi->adapter;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	if (adap->flags & FULL_INIT_DONE)
76162306a36Sopenharmony_ci		return -EBUSY;
76262306a36Sopenharmony_ci	if (val > t3_mc5_size(&adap->mc5) - adap->params.mc5.nfilters -
76362306a36Sopenharmony_ci	    MC5_MIN_TIDS)
76462306a36Sopenharmony_ci		return -EINVAL;
76562306a36Sopenharmony_ci	adap->params.mc5.nservers = val;
76662306a36Sopenharmony_ci	return 0;
76762306a36Sopenharmony_ci}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_cistatic ssize_t store_nservers(struct device *d, struct device_attribute *attr,
77062306a36Sopenharmony_ci			      const char *buf, size_t len)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	return attr_store(d, buf, len, set_nservers, 0, ~0);
77362306a36Sopenharmony_ci}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci#define CXGB3_ATTR_R(name, val_expr) \
77662306a36Sopenharmony_ciCXGB3_SHOW(name, val_expr) \
77762306a36Sopenharmony_cistatic DEVICE_ATTR(name, 0444, show_##name, NULL)
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci#define CXGB3_ATTR_RW(name, val_expr, store_method) \
78062306a36Sopenharmony_ciCXGB3_SHOW(name, val_expr) \
78162306a36Sopenharmony_cistatic DEVICE_ATTR(name, 0644, show_##name, store_method)
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ciCXGB3_ATTR_R(cam_size, t3_mc5_size(&adap->mc5));
78462306a36Sopenharmony_ciCXGB3_ATTR_RW(nfilters, adap->params.mc5.nfilters, store_nfilters);
78562306a36Sopenharmony_ciCXGB3_ATTR_RW(nservers, adap->params.mc5.nservers, store_nservers);
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_cistatic struct attribute *cxgb3_attrs[] = {
78862306a36Sopenharmony_ci	&dev_attr_cam_size.attr,
78962306a36Sopenharmony_ci	&dev_attr_nfilters.attr,
79062306a36Sopenharmony_ci	&dev_attr_nservers.attr,
79162306a36Sopenharmony_ci	NULL
79262306a36Sopenharmony_ci};
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_cistatic const struct attribute_group cxgb3_attr_group = {
79562306a36Sopenharmony_ci	.attrs = cxgb3_attrs,
79662306a36Sopenharmony_ci};
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_cistatic ssize_t tm_attr_show(struct device *d,
79962306a36Sopenharmony_ci			    char *buf, int sched)
80062306a36Sopenharmony_ci{
80162306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(to_net_dev(d));
80262306a36Sopenharmony_ci	struct adapter *adap = pi->adapter;
80362306a36Sopenharmony_ci	unsigned int v, addr, bpt, cpt;
80462306a36Sopenharmony_ci	ssize_t len;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	addr = A_TP_TX_MOD_Q1_Q0_RATE_LIMIT - sched / 2;
80762306a36Sopenharmony_ci	rtnl_lock();
80862306a36Sopenharmony_ci	t3_write_reg(adap, A_TP_TM_PIO_ADDR, addr);
80962306a36Sopenharmony_ci	v = t3_read_reg(adap, A_TP_TM_PIO_DATA);
81062306a36Sopenharmony_ci	if (sched & 1)
81162306a36Sopenharmony_ci		v >>= 16;
81262306a36Sopenharmony_ci	bpt = (v >> 8) & 0xff;
81362306a36Sopenharmony_ci	cpt = v & 0xff;
81462306a36Sopenharmony_ci	if (!cpt)
81562306a36Sopenharmony_ci		len = sprintf(buf, "disabled\n");
81662306a36Sopenharmony_ci	else {
81762306a36Sopenharmony_ci		v = (adap->params.vpd.cclk * 1000) / cpt;
81862306a36Sopenharmony_ci		len = sprintf(buf, "%u Kbps\n", (v * bpt) / 125);
81962306a36Sopenharmony_ci	}
82062306a36Sopenharmony_ci	rtnl_unlock();
82162306a36Sopenharmony_ci	return len;
82262306a36Sopenharmony_ci}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_cistatic ssize_t tm_attr_store(struct device *d,
82562306a36Sopenharmony_ci			     const char *buf, size_t len, int sched)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(to_net_dev(d));
82862306a36Sopenharmony_ci	struct adapter *adap = pi->adapter;
82962306a36Sopenharmony_ci	unsigned int val;
83062306a36Sopenharmony_ci	ssize_t ret;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	if (!capable(CAP_NET_ADMIN))
83362306a36Sopenharmony_ci		return -EPERM;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	ret = kstrtouint(buf, 0, &val);
83662306a36Sopenharmony_ci	if (ret)
83762306a36Sopenharmony_ci		return ret;
83862306a36Sopenharmony_ci	if (val > 10000000)
83962306a36Sopenharmony_ci		return -EINVAL;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	rtnl_lock();
84262306a36Sopenharmony_ci	ret = t3_config_sched(adap, val, sched);
84362306a36Sopenharmony_ci	if (!ret)
84462306a36Sopenharmony_ci		ret = len;
84562306a36Sopenharmony_ci	rtnl_unlock();
84662306a36Sopenharmony_ci	return ret;
84762306a36Sopenharmony_ci}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci#define TM_ATTR(name, sched) \
85062306a36Sopenharmony_cistatic ssize_t show_##name(struct device *d, struct device_attribute *attr, \
85162306a36Sopenharmony_ci			   char *buf) \
85262306a36Sopenharmony_ci{ \
85362306a36Sopenharmony_ci	return tm_attr_show(d, buf, sched); \
85462306a36Sopenharmony_ci} \
85562306a36Sopenharmony_cistatic ssize_t store_##name(struct device *d, struct device_attribute *attr, \
85662306a36Sopenharmony_ci			    const char *buf, size_t len) \
85762306a36Sopenharmony_ci{ \
85862306a36Sopenharmony_ci	return tm_attr_store(d, buf, len, sched); \
85962306a36Sopenharmony_ci} \
86062306a36Sopenharmony_cistatic DEVICE_ATTR(name, 0644, show_##name, store_##name)
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ciTM_ATTR(sched0, 0);
86362306a36Sopenharmony_ciTM_ATTR(sched1, 1);
86462306a36Sopenharmony_ciTM_ATTR(sched2, 2);
86562306a36Sopenharmony_ciTM_ATTR(sched3, 3);
86662306a36Sopenharmony_ciTM_ATTR(sched4, 4);
86762306a36Sopenharmony_ciTM_ATTR(sched5, 5);
86862306a36Sopenharmony_ciTM_ATTR(sched6, 6);
86962306a36Sopenharmony_ciTM_ATTR(sched7, 7);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_cistatic struct attribute *offload_attrs[] = {
87262306a36Sopenharmony_ci	&dev_attr_sched0.attr,
87362306a36Sopenharmony_ci	&dev_attr_sched1.attr,
87462306a36Sopenharmony_ci	&dev_attr_sched2.attr,
87562306a36Sopenharmony_ci	&dev_attr_sched3.attr,
87662306a36Sopenharmony_ci	&dev_attr_sched4.attr,
87762306a36Sopenharmony_ci	&dev_attr_sched5.attr,
87862306a36Sopenharmony_ci	&dev_attr_sched6.attr,
87962306a36Sopenharmony_ci	&dev_attr_sched7.attr,
88062306a36Sopenharmony_ci	NULL
88162306a36Sopenharmony_ci};
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_cistatic const struct attribute_group offload_attr_group = {
88462306a36Sopenharmony_ci	.attrs = offload_attrs,
88562306a36Sopenharmony_ci};
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci/*
88862306a36Sopenharmony_ci * Sends an sk_buff to an offload queue driver
88962306a36Sopenharmony_ci * after dealing with any active network taps.
89062306a36Sopenharmony_ci */
89162306a36Sopenharmony_cistatic inline int offload_tx(struct t3cdev *tdev, struct sk_buff *skb)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	int ret;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	local_bh_disable();
89662306a36Sopenharmony_ci	ret = t3_offload_tx(tdev, skb);
89762306a36Sopenharmony_ci	local_bh_enable();
89862306a36Sopenharmony_ci	return ret;
89962306a36Sopenharmony_ci}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_cistatic int write_smt_entry(struct adapter *adapter, int idx)
90262306a36Sopenharmony_ci{
90362306a36Sopenharmony_ci	struct cpl_smt_write_req *req;
90462306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(adapter->port[idx]);
90562306a36Sopenharmony_ci	struct sk_buff *skb = alloc_skb(sizeof(*req), GFP_KERNEL);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	if (!skb)
90862306a36Sopenharmony_ci		return -ENOMEM;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	req = __skb_put(skb, sizeof(*req));
91162306a36Sopenharmony_ci	req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
91262306a36Sopenharmony_ci	OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SMT_WRITE_REQ, idx));
91362306a36Sopenharmony_ci	req->mtu_idx = NMTUS - 1;	/* should be 0 but there's a T3 bug */
91462306a36Sopenharmony_ci	req->iff = idx;
91562306a36Sopenharmony_ci	memcpy(req->src_mac0, adapter->port[idx]->dev_addr, ETH_ALEN);
91662306a36Sopenharmony_ci	memcpy(req->src_mac1, pi->iscsic.mac_addr, ETH_ALEN);
91762306a36Sopenharmony_ci	skb->priority = 1;
91862306a36Sopenharmony_ci	offload_tx(&adapter->tdev, skb);
91962306a36Sopenharmony_ci	return 0;
92062306a36Sopenharmony_ci}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_cistatic int init_smt(struct adapter *adapter)
92362306a36Sopenharmony_ci{
92462306a36Sopenharmony_ci	int i;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	for_each_port(adapter, i)
92762306a36Sopenharmony_ci	    write_smt_entry(adapter, i);
92862306a36Sopenharmony_ci	return 0;
92962306a36Sopenharmony_ci}
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_cistatic void init_port_mtus(struct adapter *adapter)
93262306a36Sopenharmony_ci{
93362306a36Sopenharmony_ci	unsigned int mtus = adapter->port[0]->mtu;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	if (adapter->port[1])
93662306a36Sopenharmony_ci		mtus |= adapter->port[1]->mtu << 16;
93762306a36Sopenharmony_ci	t3_write_reg(adapter, A_TP_MTU_PORT_TABLE, mtus);
93862306a36Sopenharmony_ci}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_cistatic int send_pktsched_cmd(struct adapter *adap, int sched, int qidx, int lo,
94162306a36Sopenharmony_ci			      int hi, int port)
94262306a36Sopenharmony_ci{
94362306a36Sopenharmony_ci	struct sk_buff *skb;
94462306a36Sopenharmony_ci	struct mngt_pktsched_wr *req;
94562306a36Sopenharmony_ci	int ret;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	skb = alloc_skb(sizeof(*req), GFP_KERNEL);
94862306a36Sopenharmony_ci	if (!skb)
94962306a36Sopenharmony_ci		skb = adap->nofail_skb;
95062306a36Sopenharmony_ci	if (!skb)
95162306a36Sopenharmony_ci		return -ENOMEM;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	req = skb_put(skb, sizeof(*req));
95462306a36Sopenharmony_ci	req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_MNGT));
95562306a36Sopenharmony_ci	req->mngt_opcode = FW_MNGTOPCODE_PKTSCHED_SET;
95662306a36Sopenharmony_ci	req->sched = sched;
95762306a36Sopenharmony_ci	req->idx = qidx;
95862306a36Sopenharmony_ci	req->min = lo;
95962306a36Sopenharmony_ci	req->max = hi;
96062306a36Sopenharmony_ci	req->binding = port;
96162306a36Sopenharmony_ci	ret = t3_mgmt_tx(adap, skb);
96262306a36Sopenharmony_ci	if (skb == adap->nofail_skb) {
96362306a36Sopenharmony_ci		adap->nofail_skb = alloc_skb(sizeof(struct cpl_set_tcb_field),
96462306a36Sopenharmony_ci					     GFP_KERNEL);
96562306a36Sopenharmony_ci		if (!adap->nofail_skb)
96662306a36Sopenharmony_ci			ret = -ENOMEM;
96762306a36Sopenharmony_ci	}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	return ret;
97062306a36Sopenharmony_ci}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_cistatic int bind_qsets(struct adapter *adap)
97362306a36Sopenharmony_ci{
97462306a36Sopenharmony_ci	int i, j, err = 0;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	for_each_port(adap, i) {
97762306a36Sopenharmony_ci		const struct port_info *pi = adap2pinfo(adap, i);
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci		for (j = 0; j < pi->nqsets; ++j) {
98062306a36Sopenharmony_ci			int ret = send_pktsched_cmd(adap, 1,
98162306a36Sopenharmony_ci						    pi->first_qset + j, -1,
98262306a36Sopenharmony_ci						    -1, i);
98362306a36Sopenharmony_ci			if (ret)
98462306a36Sopenharmony_ci				err = ret;
98562306a36Sopenharmony_ci		}
98662306a36Sopenharmony_ci	}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	return err;
98962306a36Sopenharmony_ci}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci#define FW_VERSION __stringify(FW_VERSION_MAJOR) "."			\
99262306a36Sopenharmony_ci	__stringify(FW_VERSION_MINOR) "." __stringify(FW_VERSION_MICRO)
99362306a36Sopenharmony_ci#define FW_FNAME "cxgb3/t3fw-" FW_VERSION ".bin"
99462306a36Sopenharmony_ci#define TPSRAM_VERSION __stringify(TP_VERSION_MAJOR) "."		\
99562306a36Sopenharmony_ci	__stringify(TP_VERSION_MINOR) "." __stringify(TP_VERSION_MICRO)
99662306a36Sopenharmony_ci#define TPSRAM_NAME "cxgb3/t3%c_psram-" TPSRAM_VERSION ".bin"
99762306a36Sopenharmony_ci#define AEL2005_OPT_EDC_NAME "cxgb3/ael2005_opt_edc.bin"
99862306a36Sopenharmony_ci#define AEL2005_TWX_EDC_NAME "cxgb3/ael2005_twx_edc.bin"
99962306a36Sopenharmony_ci#define AEL2020_TWX_EDC_NAME "cxgb3/ael2020_twx_edc.bin"
100062306a36Sopenharmony_ciMODULE_FIRMWARE(FW_FNAME);
100162306a36Sopenharmony_ciMODULE_FIRMWARE("cxgb3/t3b_psram-" TPSRAM_VERSION ".bin");
100262306a36Sopenharmony_ciMODULE_FIRMWARE("cxgb3/t3c_psram-" TPSRAM_VERSION ".bin");
100362306a36Sopenharmony_ciMODULE_FIRMWARE(AEL2005_OPT_EDC_NAME);
100462306a36Sopenharmony_ciMODULE_FIRMWARE(AEL2005_TWX_EDC_NAME);
100562306a36Sopenharmony_ciMODULE_FIRMWARE(AEL2020_TWX_EDC_NAME);
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_cistatic inline const char *get_edc_fw_name(int edc_idx)
100862306a36Sopenharmony_ci{
100962306a36Sopenharmony_ci	const char *fw_name = NULL;
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	switch (edc_idx) {
101262306a36Sopenharmony_ci	case EDC_OPT_AEL2005:
101362306a36Sopenharmony_ci		fw_name = AEL2005_OPT_EDC_NAME;
101462306a36Sopenharmony_ci		break;
101562306a36Sopenharmony_ci	case EDC_TWX_AEL2005:
101662306a36Sopenharmony_ci		fw_name = AEL2005_TWX_EDC_NAME;
101762306a36Sopenharmony_ci		break;
101862306a36Sopenharmony_ci	case EDC_TWX_AEL2020:
101962306a36Sopenharmony_ci		fw_name = AEL2020_TWX_EDC_NAME;
102062306a36Sopenharmony_ci		break;
102162306a36Sopenharmony_ci	}
102262306a36Sopenharmony_ci	return fw_name;
102362306a36Sopenharmony_ci}
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ciint t3_get_edc_fw(struct cphy *phy, int edc_idx, int size)
102662306a36Sopenharmony_ci{
102762306a36Sopenharmony_ci	struct adapter *adapter = phy->adapter;
102862306a36Sopenharmony_ci	const struct firmware *fw;
102962306a36Sopenharmony_ci	const char *fw_name;
103062306a36Sopenharmony_ci	u32 csum;
103162306a36Sopenharmony_ci	const __be32 *p;
103262306a36Sopenharmony_ci	u16 *cache = phy->phy_cache;
103362306a36Sopenharmony_ci	int i, ret = -EINVAL;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	fw_name = get_edc_fw_name(edc_idx);
103662306a36Sopenharmony_ci	if (fw_name)
103762306a36Sopenharmony_ci		ret = request_firmware(&fw, fw_name, &adapter->pdev->dev);
103862306a36Sopenharmony_ci	if (ret < 0) {
103962306a36Sopenharmony_ci		dev_err(&adapter->pdev->dev,
104062306a36Sopenharmony_ci			"could not upgrade firmware: unable to load %s\n",
104162306a36Sopenharmony_ci			fw_name);
104262306a36Sopenharmony_ci		return ret;
104362306a36Sopenharmony_ci	}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	/* check size, take checksum in account */
104662306a36Sopenharmony_ci	if (fw->size > size + 4) {
104762306a36Sopenharmony_ci		CH_ERR(adapter, "firmware image too large %u, expected %d\n",
104862306a36Sopenharmony_ci		       (unsigned int)fw->size, size + 4);
104962306a36Sopenharmony_ci		ret = -EINVAL;
105062306a36Sopenharmony_ci	}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	/* compute checksum */
105362306a36Sopenharmony_ci	p = (const __be32 *)fw->data;
105462306a36Sopenharmony_ci	for (csum = 0, i = 0; i < fw->size / sizeof(csum); i++)
105562306a36Sopenharmony_ci		csum += ntohl(p[i]);
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	if (csum != 0xffffffff) {
105862306a36Sopenharmony_ci		CH_ERR(adapter, "corrupted firmware image, checksum %u\n",
105962306a36Sopenharmony_ci		       csum);
106062306a36Sopenharmony_ci		ret = -EINVAL;
106162306a36Sopenharmony_ci	}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	for (i = 0; i < size / 4 ; i++) {
106462306a36Sopenharmony_ci		*cache++ = (be32_to_cpu(p[i]) & 0xffff0000) >> 16;
106562306a36Sopenharmony_ci		*cache++ = be32_to_cpu(p[i]) & 0xffff;
106662306a36Sopenharmony_ci	}
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	release_firmware(fw);
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	return ret;
107162306a36Sopenharmony_ci}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_cistatic int upgrade_fw(struct adapter *adap)
107462306a36Sopenharmony_ci{
107562306a36Sopenharmony_ci	int ret;
107662306a36Sopenharmony_ci	const struct firmware *fw;
107762306a36Sopenharmony_ci	struct device *dev = &adap->pdev->dev;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	ret = request_firmware(&fw, FW_FNAME, dev);
108062306a36Sopenharmony_ci	if (ret < 0) {
108162306a36Sopenharmony_ci		dev_err(dev, "could not upgrade firmware: unable to load %s\n",
108262306a36Sopenharmony_ci			FW_FNAME);
108362306a36Sopenharmony_ci		return ret;
108462306a36Sopenharmony_ci	}
108562306a36Sopenharmony_ci	ret = t3_load_fw(adap, fw->data, fw->size);
108662306a36Sopenharmony_ci	release_firmware(fw);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	if (ret == 0)
108962306a36Sopenharmony_ci		dev_info(dev, "successful upgrade to firmware %d.%d.%d\n",
109062306a36Sopenharmony_ci			 FW_VERSION_MAJOR, FW_VERSION_MINOR, FW_VERSION_MICRO);
109162306a36Sopenharmony_ci	else
109262306a36Sopenharmony_ci		dev_err(dev, "failed to upgrade to firmware %d.%d.%d\n",
109362306a36Sopenharmony_ci			FW_VERSION_MAJOR, FW_VERSION_MINOR, FW_VERSION_MICRO);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	return ret;
109662306a36Sopenharmony_ci}
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_cistatic inline char t3rev2char(struct adapter *adapter)
109962306a36Sopenharmony_ci{
110062306a36Sopenharmony_ci	char rev = 0;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	switch(adapter->params.rev) {
110362306a36Sopenharmony_ci	case T3_REV_B:
110462306a36Sopenharmony_ci	case T3_REV_B2:
110562306a36Sopenharmony_ci		rev = 'b';
110662306a36Sopenharmony_ci		break;
110762306a36Sopenharmony_ci	case T3_REV_C:
110862306a36Sopenharmony_ci		rev = 'c';
110962306a36Sopenharmony_ci		break;
111062306a36Sopenharmony_ci	}
111162306a36Sopenharmony_ci	return rev;
111262306a36Sopenharmony_ci}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_cistatic int update_tpsram(struct adapter *adap)
111562306a36Sopenharmony_ci{
111662306a36Sopenharmony_ci	const struct firmware *tpsram;
111762306a36Sopenharmony_ci	char buf[64];
111862306a36Sopenharmony_ci	struct device *dev = &adap->pdev->dev;
111962306a36Sopenharmony_ci	int ret;
112062306a36Sopenharmony_ci	char rev;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	rev = t3rev2char(adap);
112362306a36Sopenharmony_ci	if (!rev)
112462306a36Sopenharmony_ci		return 0;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	snprintf(buf, sizeof(buf), TPSRAM_NAME, rev);
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	ret = request_firmware(&tpsram, buf, dev);
112962306a36Sopenharmony_ci	if (ret < 0) {
113062306a36Sopenharmony_ci		dev_err(dev, "could not load TP SRAM: unable to load %s\n",
113162306a36Sopenharmony_ci			buf);
113262306a36Sopenharmony_ci		return ret;
113362306a36Sopenharmony_ci	}
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	ret = t3_check_tpsram(adap, tpsram->data, tpsram->size);
113662306a36Sopenharmony_ci	if (ret)
113762306a36Sopenharmony_ci		goto release_tpsram;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	ret = t3_set_proto_sram(adap, tpsram->data);
114062306a36Sopenharmony_ci	if (ret == 0)
114162306a36Sopenharmony_ci		dev_info(dev,
114262306a36Sopenharmony_ci			 "successful update of protocol engine "
114362306a36Sopenharmony_ci			 "to %d.%d.%d\n",
114462306a36Sopenharmony_ci			 TP_VERSION_MAJOR, TP_VERSION_MINOR, TP_VERSION_MICRO);
114562306a36Sopenharmony_ci	else
114662306a36Sopenharmony_ci		dev_err(dev, "failed to update of protocol engine %d.%d.%d\n",
114762306a36Sopenharmony_ci			TP_VERSION_MAJOR, TP_VERSION_MINOR, TP_VERSION_MICRO);
114862306a36Sopenharmony_ci	if (ret)
114962306a36Sopenharmony_ci		dev_err(dev, "loading protocol SRAM failed\n");
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_cirelease_tpsram:
115262306a36Sopenharmony_ci	release_firmware(tpsram);
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	return ret;
115562306a36Sopenharmony_ci}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci/**
115862306a36Sopenharmony_ci * t3_synchronize_rx - wait for current Rx processing on a port to complete
115962306a36Sopenharmony_ci * @adap: the adapter
116062306a36Sopenharmony_ci * @p: the port
116162306a36Sopenharmony_ci *
116262306a36Sopenharmony_ci * Ensures that current Rx processing on any of the queues associated with
116362306a36Sopenharmony_ci * the given port completes before returning.  We do this by acquiring and
116462306a36Sopenharmony_ci * releasing the locks of the response queues associated with the port.
116562306a36Sopenharmony_ci */
116662306a36Sopenharmony_cistatic void t3_synchronize_rx(struct adapter *adap, const struct port_info *p)
116762306a36Sopenharmony_ci{
116862306a36Sopenharmony_ci	int i;
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	for (i = p->first_qset; i < p->first_qset + p->nqsets; i++) {
117162306a36Sopenharmony_ci		struct sge_rspq *q = &adap->sge.qs[i].rspq;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci		spin_lock_irq(&q->lock);
117462306a36Sopenharmony_ci		spin_unlock_irq(&q->lock);
117562306a36Sopenharmony_ci	}
117662306a36Sopenharmony_ci}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_cistatic void cxgb_vlan_mode(struct net_device *dev, netdev_features_t features)
117962306a36Sopenharmony_ci{
118062306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
118162306a36Sopenharmony_ci	struct adapter *adapter = pi->adapter;
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	if (adapter->params.rev > 0) {
118462306a36Sopenharmony_ci		t3_set_vlan_accel(adapter, 1 << pi->port_id,
118562306a36Sopenharmony_ci				  features & NETIF_F_HW_VLAN_CTAG_RX);
118662306a36Sopenharmony_ci	} else {
118762306a36Sopenharmony_ci		/* single control for all ports */
118862306a36Sopenharmony_ci		unsigned int i, have_vlans = features & NETIF_F_HW_VLAN_CTAG_RX;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci		for_each_port(adapter, i)
119162306a36Sopenharmony_ci			have_vlans |=
119262306a36Sopenharmony_ci				adapter->port[i]->features &
119362306a36Sopenharmony_ci				NETIF_F_HW_VLAN_CTAG_RX;
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci		t3_set_vlan_accel(adapter, 1, have_vlans);
119662306a36Sopenharmony_ci	}
119762306a36Sopenharmony_ci	t3_synchronize_rx(adapter, pi);
119862306a36Sopenharmony_ci}
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci/**
120162306a36Sopenharmony_ci *	cxgb_up - enable the adapter
120262306a36Sopenharmony_ci *	@adap: adapter being enabled
120362306a36Sopenharmony_ci *
120462306a36Sopenharmony_ci *	Called when the first port is enabled, this function performs the
120562306a36Sopenharmony_ci *	actions necessary to make an adapter operational, such as completing
120662306a36Sopenharmony_ci *	the initialization of HW modules, and enabling interrupts.
120762306a36Sopenharmony_ci *
120862306a36Sopenharmony_ci *	Must be called with the rtnl lock held.
120962306a36Sopenharmony_ci */
121062306a36Sopenharmony_cistatic int cxgb_up(struct adapter *adap)
121162306a36Sopenharmony_ci{
121262306a36Sopenharmony_ci	int i, err;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	if (!(adap->flags & FULL_INIT_DONE)) {
121562306a36Sopenharmony_ci		err = t3_check_fw_version(adap);
121662306a36Sopenharmony_ci		if (err == -EINVAL) {
121762306a36Sopenharmony_ci			err = upgrade_fw(adap);
121862306a36Sopenharmony_ci			CH_WARN(adap, "FW upgrade to %d.%d.%d %s\n",
121962306a36Sopenharmony_ci				FW_VERSION_MAJOR, FW_VERSION_MINOR,
122062306a36Sopenharmony_ci				FW_VERSION_MICRO, err ? "failed" : "succeeded");
122162306a36Sopenharmony_ci		}
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci		err = t3_check_tpsram_version(adap);
122462306a36Sopenharmony_ci		if (err == -EINVAL) {
122562306a36Sopenharmony_ci			err = update_tpsram(adap);
122662306a36Sopenharmony_ci			CH_WARN(adap, "TP upgrade to %d.%d.%d %s\n",
122762306a36Sopenharmony_ci				TP_VERSION_MAJOR, TP_VERSION_MINOR,
122862306a36Sopenharmony_ci				TP_VERSION_MICRO, err ? "failed" : "succeeded");
122962306a36Sopenharmony_ci		}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci		/*
123262306a36Sopenharmony_ci		 * Clear interrupts now to catch errors if t3_init_hw fails.
123362306a36Sopenharmony_ci		 * We clear them again later as initialization may trigger
123462306a36Sopenharmony_ci		 * conditions that can interrupt.
123562306a36Sopenharmony_ci		 */
123662306a36Sopenharmony_ci		t3_intr_clear(adap);
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci		err = t3_init_hw(adap, 0);
123962306a36Sopenharmony_ci		if (err)
124062306a36Sopenharmony_ci			goto out;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci		t3_set_reg_field(adap, A_TP_PARA_REG5, 0, F_RXDDPOFFINIT);
124362306a36Sopenharmony_ci		t3_write_reg(adap, A_ULPRX_TDDP_PSZ, V_HPZ0(PAGE_SHIFT - 12));
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci		err = setup_sge_qsets(adap);
124662306a36Sopenharmony_ci		if (err)
124762306a36Sopenharmony_ci			goto out;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci		for_each_port(adap, i)
125062306a36Sopenharmony_ci			cxgb_vlan_mode(adap->port[i], adap->port[i]->features);
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci		setup_rss(adap);
125362306a36Sopenharmony_ci		if (!(adap->flags & NAPI_INIT))
125462306a36Sopenharmony_ci			init_napi(adap);
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci		t3_start_sge_timers(adap);
125762306a36Sopenharmony_ci		adap->flags |= FULL_INIT_DONE;
125862306a36Sopenharmony_ci	}
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	t3_intr_clear(adap);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	if (adap->flags & USING_MSIX) {
126362306a36Sopenharmony_ci		name_msix_vecs(adap);
126462306a36Sopenharmony_ci		err = request_irq(adap->msix_info[0].vec,
126562306a36Sopenharmony_ci				  t3_async_intr_handler, 0,
126662306a36Sopenharmony_ci				  adap->msix_info[0].desc, adap);
126762306a36Sopenharmony_ci		if (err)
126862306a36Sopenharmony_ci			goto irq_err;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci		err = request_msix_data_irqs(adap);
127162306a36Sopenharmony_ci		if (err) {
127262306a36Sopenharmony_ci			free_irq(adap->msix_info[0].vec, adap);
127362306a36Sopenharmony_ci			goto irq_err;
127462306a36Sopenharmony_ci		}
127562306a36Sopenharmony_ci	} else {
127662306a36Sopenharmony_ci		err = request_irq(adap->pdev->irq,
127762306a36Sopenharmony_ci				  t3_intr_handler(adap, adap->sge.qs[0].rspq.polling),
127862306a36Sopenharmony_ci				  (adap->flags & USING_MSI) ? 0 : IRQF_SHARED,
127962306a36Sopenharmony_ci				  adap->name, adap);
128062306a36Sopenharmony_ci		if (err)
128162306a36Sopenharmony_ci			goto irq_err;
128262306a36Sopenharmony_ci	}
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	enable_all_napi(adap);
128562306a36Sopenharmony_ci	t3_sge_start(adap);
128662306a36Sopenharmony_ci	t3_intr_enable(adap);
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	if (adap->params.rev >= T3_REV_C && !(adap->flags & TP_PARITY_INIT) &&
128962306a36Sopenharmony_ci	    is_offload(adap) && init_tp_parity(adap) == 0)
129062306a36Sopenharmony_ci		adap->flags |= TP_PARITY_INIT;
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	if (adap->flags & TP_PARITY_INIT) {
129362306a36Sopenharmony_ci		t3_write_reg(adap, A_TP_INT_CAUSE,
129462306a36Sopenharmony_ci			     F_CMCACHEPERR | F_ARPLUTPERR);
129562306a36Sopenharmony_ci		t3_write_reg(adap, A_TP_INT_ENABLE, 0x7fbfffff);
129662306a36Sopenharmony_ci	}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	if (!(adap->flags & QUEUES_BOUND)) {
129962306a36Sopenharmony_ci		int ret = bind_qsets(adap);
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci		if (ret < 0) {
130262306a36Sopenharmony_ci			CH_ERR(adap, "failed to bind qsets, err %d\n", ret);
130362306a36Sopenharmony_ci			t3_intr_disable(adap);
130462306a36Sopenharmony_ci			quiesce_rx(adap);
130562306a36Sopenharmony_ci			free_irq_resources(adap);
130662306a36Sopenharmony_ci			err = ret;
130762306a36Sopenharmony_ci			goto out;
130862306a36Sopenharmony_ci		}
130962306a36Sopenharmony_ci		adap->flags |= QUEUES_BOUND;
131062306a36Sopenharmony_ci	}
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ciout:
131362306a36Sopenharmony_ci	return err;
131462306a36Sopenharmony_ciirq_err:
131562306a36Sopenharmony_ci	CH_ERR(adap, "request_irq failed, err %d\n", err);
131662306a36Sopenharmony_ci	goto out;
131762306a36Sopenharmony_ci}
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci/*
132062306a36Sopenharmony_ci * Release resources when all the ports and offloading have been stopped.
132162306a36Sopenharmony_ci */
132262306a36Sopenharmony_cistatic void cxgb_down(struct adapter *adapter, int on_wq)
132362306a36Sopenharmony_ci{
132462306a36Sopenharmony_ci	t3_sge_stop(adapter);
132562306a36Sopenharmony_ci	spin_lock_irq(&adapter->work_lock);	/* sync with PHY intr task */
132662306a36Sopenharmony_ci	t3_intr_disable(adapter);
132762306a36Sopenharmony_ci	spin_unlock_irq(&adapter->work_lock);
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	free_irq_resources(adapter);
133062306a36Sopenharmony_ci	quiesce_rx(adapter);
133162306a36Sopenharmony_ci	t3_sge_stop(adapter);
133262306a36Sopenharmony_ci	if (!on_wq)
133362306a36Sopenharmony_ci		flush_workqueue(cxgb3_wq);/* wait for external IRQ handler */
133462306a36Sopenharmony_ci}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_cistatic void schedule_chk_task(struct adapter *adap)
133762306a36Sopenharmony_ci{
133862306a36Sopenharmony_ci	unsigned int timeo;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	timeo = adap->params.linkpoll_period ?
134162306a36Sopenharmony_ci	    (HZ * adap->params.linkpoll_period) / 10 :
134262306a36Sopenharmony_ci	    adap->params.stats_update_period * HZ;
134362306a36Sopenharmony_ci	if (timeo)
134462306a36Sopenharmony_ci		queue_delayed_work(cxgb3_wq, &adap->adap_check_task, timeo);
134562306a36Sopenharmony_ci}
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_cistatic int offload_open(struct net_device *dev)
134862306a36Sopenharmony_ci{
134962306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
135062306a36Sopenharmony_ci	struct adapter *adapter = pi->adapter;
135162306a36Sopenharmony_ci	struct t3cdev *tdev = dev2t3cdev(dev);
135262306a36Sopenharmony_ci	int adap_up = adapter->open_device_map & PORT_MASK;
135362306a36Sopenharmony_ci	int err;
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	if (test_and_set_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map))
135662306a36Sopenharmony_ci		return 0;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	if (!adap_up && (err = cxgb_up(adapter)) < 0)
135962306a36Sopenharmony_ci		goto out;
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	t3_tp_set_offload_mode(adapter, 1);
136262306a36Sopenharmony_ci	tdev->lldev = adapter->port[0];
136362306a36Sopenharmony_ci	err = cxgb3_offload_activate(adapter);
136462306a36Sopenharmony_ci	if (err)
136562306a36Sopenharmony_ci		goto out;
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	init_port_mtus(adapter);
136862306a36Sopenharmony_ci	t3_load_mtus(adapter, adapter->params.mtus, adapter->params.a_wnd,
136962306a36Sopenharmony_ci		     adapter->params.b_wnd,
137062306a36Sopenharmony_ci		     adapter->params.rev == 0 ?
137162306a36Sopenharmony_ci		     adapter->port[0]->mtu : 0xffff);
137262306a36Sopenharmony_ci	init_smt(adapter);
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	if (sysfs_create_group(&tdev->lldev->dev.kobj, &offload_attr_group))
137562306a36Sopenharmony_ci		dev_dbg(&dev->dev, "cannot create sysfs group\n");
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	/* Call back all registered clients */
137862306a36Sopenharmony_ci	cxgb3_add_clients(tdev);
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ciout:
138162306a36Sopenharmony_ci	/* restore them in case the offload module has changed them */
138262306a36Sopenharmony_ci	if (err) {
138362306a36Sopenharmony_ci		t3_tp_set_offload_mode(adapter, 0);
138462306a36Sopenharmony_ci		clear_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map);
138562306a36Sopenharmony_ci		cxgb3_set_dummy_ops(tdev);
138662306a36Sopenharmony_ci	}
138762306a36Sopenharmony_ci	return err;
138862306a36Sopenharmony_ci}
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_cistatic int offload_close(struct t3cdev *tdev)
139162306a36Sopenharmony_ci{
139262306a36Sopenharmony_ci	struct adapter *adapter = tdev2adap(tdev);
139362306a36Sopenharmony_ci	struct t3c_data *td = T3C_DATA(tdev);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	if (!test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map))
139662306a36Sopenharmony_ci		return 0;
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	/* Call back all registered clients */
139962306a36Sopenharmony_ci	cxgb3_remove_clients(tdev);
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	sysfs_remove_group(&tdev->lldev->dev.kobj, &offload_attr_group);
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	/* Flush work scheduled while releasing TIDs */
140462306a36Sopenharmony_ci	flush_work(&td->tid_release_task);
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	tdev->lldev = NULL;
140762306a36Sopenharmony_ci	cxgb3_set_dummy_ops(tdev);
140862306a36Sopenharmony_ci	t3_tp_set_offload_mode(adapter, 0);
140962306a36Sopenharmony_ci	clear_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map);
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	if (!adapter->open_device_map)
141262306a36Sopenharmony_ci		cxgb_down(adapter, 0);
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	cxgb3_offload_deactivate(adapter);
141562306a36Sopenharmony_ci	return 0;
141662306a36Sopenharmony_ci}
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_cistatic int cxgb_open(struct net_device *dev)
141962306a36Sopenharmony_ci{
142062306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
142162306a36Sopenharmony_ci	struct adapter *adapter = pi->adapter;
142262306a36Sopenharmony_ci	int other_ports = adapter->open_device_map & PORT_MASK;
142362306a36Sopenharmony_ci	int err;
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	if (!adapter->open_device_map && (err = cxgb_up(adapter)) < 0)
142662306a36Sopenharmony_ci		return err;
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	set_bit(pi->port_id, &adapter->open_device_map);
142962306a36Sopenharmony_ci	if (is_offload(adapter) && !ofld_disable) {
143062306a36Sopenharmony_ci		err = offload_open(dev);
143162306a36Sopenharmony_ci		if (err)
143262306a36Sopenharmony_ci			pr_warn("Could not initialize offload capabilities\n");
143362306a36Sopenharmony_ci	}
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	netif_set_real_num_tx_queues(dev, pi->nqsets);
143662306a36Sopenharmony_ci	err = netif_set_real_num_rx_queues(dev, pi->nqsets);
143762306a36Sopenharmony_ci	if (err)
143862306a36Sopenharmony_ci		return err;
143962306a36Sopenharmony_ci	link_start(dev);
144062306a36Sopenharmony_ci	t3_port_intr_enable(adapter, pi->port_id);
144162306a36Sopenharmony_ci	netif_tx_start_all_queues(dev);
144262306a36Sopenharmony_ci	if (!other_ports)
144362306a36Sopenharmony_ci		schedule_chk_task(adapter);
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	cxgb3_event_notify(&adapter->tdev, OFFLOAD_PORT_UP, pi->port_id);
144662306a36Sopenharmony_ci	return 0;
144762306a36Sopenharmony_ci}
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_cistatic int __cxgb_close(struct net_device *dev, int on_wq)
145062306a36Sopenharmony_ci{
145162306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
145262306a36Sopenharmony_ci	struct adapter *adapter = pi->adapter;
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	if (!adapter->open_device_map)
145662306a36Sopenharmony_ci		return 0;
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	/* Stop link fault interrupts */
145962306a36Sopenharmony_ci	t3_xgm_intr_disable(adapter, pi->port_id);
146062306a36Sopenharmony_ci	t3_read_reg(adapter, A_XGM_INT_STATUS + pi->mac.offset);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	t3_port_intr_disable(adapter, pi->port_id);
146362306a36Sopenharmony_ci	netif_tx_stop_all_queues(dev);
146462306a36Sopenharmony_ci	pi->phy.ops->power_down(&pi->phy, 1);
146562306a36Sopenharmony_ci	netif_carrier_off(dev);
146662306a36Sopenharmony_ci	t3_mac_disable(&pi->mac, MAC_DIRECTION_TX | MAC_DIRECTION_RX);
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	spin_lock_irq(&adapter->work_lock);	/* sync with update task */
146962306a36Sopenharmony_ci	clear_bit(pi->port_id, &adapter->open_device_map);
147062306a36Sopenharmony_ci	spin_unlock_irq(&adapter->work_lock);
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	if (!(adapter->open_device_map & PORT_MASK))
147362306a36Sopenharmony_ci		cancel_delayed_work_sync(&adapter->adap_check_task);
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	if (!adapter->open_device_map)
147662306a36Sopenharmony_ci		cxgb_down(adapter, on_wq);
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	cxgb3_event_notify(&adapter->tdev, OFFLOAD_PORT_DOWN, pi->port_id);
147962306a36Sopenharmony_ci	return 0;
148062306a36Sopenharmony_ci}
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_cistatic int cxgb_close(struct net_device *dev)
148362306a36Sopenharmony_ci{
148462306a36Sopenharmony_ci	return __cxgb_close(dev, 0);
148562306a36Sopenharmony_ci}
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_cistatic struct net_device_stats *cxgb_get_stats(struct net_device *dev)
148862306a36Sopenharmony_ci{
148962306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
149062306a36Sopenharmony_ci	struct adapter *adapter = pi->adapter;
149162306a36Sopenharmony_ci	struct net_device_stats *ns = &dev->stats;
149262306a36Sopenharmony_ci	const struct mac_stats *pstats;
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	spin_lock(&adapter->stats_lock);
149562306a36Sopenharmony_ci	pstats = t3_mac_update_stats(&pi->mac);
149662306a36Sopenharmony_ci	spin_unlock(&adapter->stats_lock);
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	ns->tx_bytes = pstats->tx_octets;
149962306a36Sopenharmony_ci	ns->tx_packets = pstats->tx_frames;
150062306a36Sopenharmony_ci	ns->rx_bytes = pstats->rx_octets;
150162306a36Sopenharmony_ci	ns->rx_packets = pstats->rx_frames;
150262306a36Sopenharmony_ci	ns->multicast = pstats->rx_mcast_frames;
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	ns->tx_errors = pstats->tx_underrun;
150562306a36Sopenharmony_ci	ns->rx_errors = pstats->rx_symbol_errs + pstats->rx_fcs_errs +
150662306a36Sopenharmony_ci	    pstats->rx_too_long + pstats->rx_jabber + pstats->rx_short +
150762306a36Sopenharmony_ci	    pstats->rx_fifo_ovfl;
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	/* detailed rx_errors */
151062306a36Sopenharmony_ci	ns->rx_length_errors = pstats->rx_jabber + pstats->rx_too_long;
151162306a36Sopenharmony_ci	ns->rx_over_errors = 0;
151262306a36Sopenharmony_ci	ns->rx_crc_errors = pstats->rx_fcs_errs;
151362306a36Sopenharmony_ci	ns->rx_frame_errors = pstats->rx_symbol_errs;
151462306a36Sopenharmony_ci	ns->rx_fifo_errors = pstats->rx_fifo_ovfl;
151562306a36Sopenharmony_ci	ns->rx_missed_errors = pstats->rx_cong_drops;
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	/* detailed tx_errors */
151862306a36Sopenharmony_ci	ns->tx_aborted_errors = 0;
151962306a36Sopenharmony_ci	ns->tx_carrier_errors = 0;
152062306a36Sopenharmony_ci	ns->tx_fifo_errors = pstats->tx_underrun;
152162306a36Sopenharmony_ci	ns->tx_heartbeat_errors = 0;
152262306a36Sopenharmony_ci	ns->tx_window_errors = 0;
152362306a36Sopenharmony_ci	return ns;
152462306a36Sopenharmony_ci}
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_cistatic u32 get_msglevel(struct net_device *dev)
152762306a36Sopenharmony_ci{
152862306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
152962306a36Sopenharmony_ci	struct adapter *adapter = pi->adapter;
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	return adapter->msg_enable;
153262306a36Sopenharmony_ci}
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_cistatic void set_msglevel(struct net_device *dev, u32 val)
153562306a36Sopenharmony_ci{
153662306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
153762306a36Sopenharmony_ci	struct adapter *adapter = pi->adapter;
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	adapter->msg_enable = val;
154062306a36Sopenharmony_ci}
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_cistatic const char stats_strings[][ETH_GSTRING_LEN] = {
154362306a36Sopenharmony_ci	"TxOctetsOK         ",
154462306a36Sopenharmony_ci	"TxFramesOK         ",
154562306a36Sopenharmony_ci	"TxMulticastFramesOK",
154662306a36Sopenharmony_ci	"TxBroadcastFramesOK",
154762306a36Sopenharmony_ci	"TxPauseFrames      ",
154862306a36Sopenharmony_ci	"TxUnderrun         ",
154962306a36Sopenharmony_ci	"TxExtUnderrun      ",
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	"TxFrames64         ",
155262306a36Sopenharmony_ci	"TxFrames65To127    ",
155362306a36Sopenharmony_ci	"TxFrames128To255   ",
155462306a36Sopenharmony_ci	"TxFrames256To511   ",
155562306a36Sopenharmony_ci	"TxFrames512To1023  ",
155662306a36Sopenharmony_ci	"TxFrames1024To1518 ",
155762306a36Sopenharmony_ci	"TxFrames1519ToMax  ",
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	"RxOctetsOK         ",
156062306a36Sopenharmony_ci	"RxFramesOK         ",
156162306a36Sopenharmony_ci	"RxMulticastFramesOK",
156262306a36Sopenharmony_ci	"RxBroadcastFramesOK",
156362306a36Sopenharmony_ci	"RxPauseFrames      ",
156462306a36Sopenharmony_ci	"RxFCSErrors        ",
156562306a36Sopenharmony_ci	"RxSymbolErrors     ",
156662306a36Sopenharmony_ci	"RxShortErrors      ",
156762306a36Sopenharmony_ci	"RxJabberErrors     ",
156862306a36Sopenharmony_ci	"RxLengthErrors     ",
156962306a36Sopenharmony_ci	"RxFIFOoverflow     ",
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	"RxFrames64         ",
157262306a36Sopenharmony_ci	"RxFrames65To127    ",
157362306a36Sopenharmony_ci	"RxFrames128To255   ",
157462306a36Sopenharmony_ci	"RxFrames256To511   ",
157562306a36Sopenharmony_ci	"RxFrames512To1023  ",
157662306a36Sopenharmony_ci	"RxFrames1024To1518 ",
157762306a36Sopenharmony_ci	"RxFrames1519ToMax  ",
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	"PhyFIFOErrors      ",
158062306a36Sopenharmony_ci	"TSO                ",
158162306a36Sopenharmony_ci	"VLANextractions    ",
158262306a36Sopenharmony_ci	"VLANinsertions     ",
158362306a36Sopenharmony_ci	"TxCsumOffload      ",
158462306a36Sopenharmony_ci	"RxCsumGood         ",
158562306a36Sopenharmony_ci	"LroAggregated      ",
158662306a36Sopenharmony_ci	"LroFlushed         ",
158762306a36Sopenharmony_ci	"LroNoDesc          ",
158862306a36Sopenharmony_ci	"RxDrops            ",
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	"CheckTXEnToggled   ",
159162306a36Sopenharmony_ci	"CheckResets        ",
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	"LinkFaults         ",
159462306a36Sopenharmony_ci};
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_cistatic int get_sset_count(struct net_device *dev, int sset)
159762306a36Sopenharmony_ci{
159862306a36Sopenharmony_ci	switch (sset) {
159962306a36Sopenharmony_ci	case ETH_SS_STATS:
160062306a36Sopenharmony_ci		return ARRAY_SIZE(stats_strings);
160162306a36Sopenharmony_ci	default:
160262306a36Sopenharmony_ci		return -EOPNOTSUPP;
160362306a36Sopenharmony_ci	}
160462306a36Sopenharmony_ci}
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci#define T3_REGMAP_SIZE (3 * 1024)
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_cistatic int get_regs_len(struct net_device *dev)
160962306a36Sopenharmony_ci{
161062306a36Sopenharmony_ci	return T3_REGMAP_SIZE;
161162306a36Sopenharmony_ci}
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_cistatic int get_eeprom_len(struct net_device *dev)
161462306a36Sopenharmony_ci{
161562306a36Sopenharmony_ci	return EEPROMSIZE;
161662306a36Sopenharmony_ci}
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_cistatic void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
161962306a36Sopenharmony_ci{
162062306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
162162306a36Sopenharmony_ci	struct adapter *adapter = pi->adapter;
162262306a36Sopenharmony_ci	u32 fw_vers = 0;
162362306a36Sopenharmony_ci	u32 tp_vers = 0;
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	spin_lock(&adapter->stats_lock);
162662306a36Sopenharmony_ci	t3_get_fw_version(adapter, &fw_vers);
162762306a36Sopenharmony_ci	t3_get_tp_version(adapter, &tp_vers);
162862306a36Sopenharmony_ci	spin_unlock(&adapter->stats_lock);
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	strscpy(info->driver, DRV_NAME, sizeof(info->driver));
163162306a36Sopenharmony_ci	strscpy(info->bus_info, pci_name(adapter->pdev),
163262306a36Sopenharmony_ci		sizeof(info->bus_info));
163362306a36Sopenharmony_ci	if (fw_vers)
163462306a36Sopenharmony_ci		snprintf(info->fw_version, sizeof(info->fw_version),
163562306a36Sopenharmony_ci			 "%s %u.%u.%u TP %u.%u.%u",
163662306a36Sopenharmony_ci			 G_FW_VERSION_TYPE(fw_vers) ? "T" : "N",
163762306a36Sopenharmony_ci			 G_FW_VERSION_MAJOR(fw_vers),
163862306a36Sopenharmony_ci			 G_FW_VERSION_MINOR(fw_vers),
163962306a36Sopenharmony_ci			 G_FW_VERSION_MICRO(fw_vers),
164062306a36Sopenharmony_ci			 G_TP_VERSION_MAJOR(tp_vers),
164162306a36Sopenharmony_ci			 G_TP_VERSION_MINOR(tp_vers),
164262306a36Sopenharmony_ci			 G_TP_VERSION_MICRO(tp_vers));
164362306a36Sopenharmony_ci}
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_cistatic void get_strings(struct net_device *dev, u32 stringset, u8 * data)
164662306a36Sopenharmony_ci{
164762306a36Sopenharmony_ci	if (stringset == ETH_SS_STATS)
164862306a36Sopenharmony_ci		memcpy(data, stats_strings, sizeof(stats_strings));
164962306a36Sopenharmony_ci}
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_cistatic unsigned long collect_sge_port_stats(struct adapter *adapter,
165262306a36Sopenharmony_ci					    struct port_info *p, int idx)
165362306a36Sopenharmony_ci{
165462306a36Sopenharmony_ci	int i;
165562306a36Sopenharmony_ci	unsigned long tot = 0;
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci	for (i = p->first_qset; i < p->first_qset + p->nqsets; ++i)
165862306a36Sopenharmony_ci		tot += adapter->sge.qs[i].port_stats[idx];
165962306a36Sopenharmony_ci	return tot;
166062306a36Sopenharmony_ci}
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_cistatic void get_stats(struct net_device *dev, struct ethtool_stats *stats,
166362306a36Sopenharmony_ci		      u64 *data)
166462306a36Sopenharmony_ci{
166562306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
166662306a36Sopenharmony_ci	struct adapter *adapter = pi->adapter;
166762306a36Sopenharmony_ci	const struct mac_stats *s;
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci	spin_lock(&adapter->stats_lock);
167062306a36Sopenharmony_ci	s = t3_mac_update_stats(&pi->mac);
167162306a36Sopenharmony_ci	spin_unlock(&adapter->stats_lock);
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	*data++ = s->tx_octets;
167462306a36Sopenharmony_ci	*data++ = s->tx_frames;
167562306a36Sopenharmony_ci	*data++ = s->tx_mcast_frames;
167662306a36Sopenharmony_ci	*data++ = s->tx_bcast_frames;
167762306a36Sopenharmony_ci	*data++ = s->tx_pause;
167862306a36Sopenharmony_ci	*data++ = s->tx_underrun;
167962306a36Sopenharmony_ci	*data++ = s->tx_fifo_urun;
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci	*data++ = s->tx_frames_64;
168262306a36Sopenharmony_ci	*data++ = s->tx_frames_65_127;
168362306a36Sopenharmony_ci	*data++ = s->tx_frames_128_255;
168462306a36Sopenharmony_ci	*data++ = s->tx_frames_256_511;
168562306a36Sopenharmony_ci	*data++ = s->tx_frames_512_1023;
168662306a36Sopenharmony_ci	*data++ = s->tx_frames_1024_1518;
168762306a36Sopenharmony_ci	*data++ = s->tx_frames_1519_max;
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci	*data++ = s->rx_octets;
169062306a36Sopenharmony_ci	*data++ = s->rx_frames;
169162306a36Sopenharmony_ci	*data++ = s->rx_mcast_frames;
169262306a36Sopenharmony_ci	*data++ = s->rx_bcast_frames;
169362306a36Sopenharmony_ci	*data++ = s->rx_pause;
169462306a36Sopenharmony_ci	*data++ = s->rx_fcs_errs;
169562306a36Sopenharmony_ci	*data++ = s->rx_symbol_errs;
169662306a36Sopenharmony_ci	*data++ = s->rx_short;
169762306a36Sopenharmony_ci	*data++ = s->rx_jabber;
169862306a36Sopenharmony_ci	*data++ = s->rx_too_long;
169962306a36Sopenharmony_ci	*data++ = s->rx_fifo_ovfl;
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	*data++ = s->rx_frames_64;
170262306a36Sopenharmony_ci	*data++ = s->rx_frames_65_127;
170362306a36Sopenharmony_ci	*data++ = s->rx_frames_128_255;
170462306a36Sopenharmony_ci	*data++ = s->rx_frames_256_511;
170562306a36Sopenharmony_ci	*data++ = s->rx_frames_512_1023;
170662306a36Sopenharmony_ci	*data++ = s->rx_frames_1024_1518;
170762306a36Sopenharmony_ci	*data++ = s->rx_frames_1519_max;
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	*data++ = pi->phy.fifo_errors;
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci	*data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_TSO);
171262306a36Sopenharmony_ci	*data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_VLANEX);
171362306a36Sopenharmony_ci	*data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_VLANINS);
171462306a36Sopenharmony_ci	*data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_TX_CSUM);
171562306a36Sopenharmony_ci	*data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_RX_CSUM_GOOD);
171662306a36Sopenharmony_ci	*data++ = 0;
171762306a36Sopenharmony_ci	*data++ = 0;
171862306a36Sopenharmony_ci	*data++ = 0;
171962306a36Sopenharmony_ci	*data++ = s->rx_cong_drops;
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	*data++ = s->num_toggled;
172262306a36Sopenharmony_ci	*data++ = s->num_resets;
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	*data++ = s->link_faults;
172562306a36Sopenharmony_ci}
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_cistatic inline void reg_block_dump(struct adapter *ap, void *buf,
172862306a36Sopenharmony_ci				  unsigned int start, unsigned int end)
172962306a36Sopenharmony_ci{
173062306a36Sopenharmony_ci	u32 *p = buf + start;
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci	for (; start <= end; start += sizeof(u32))
173362306a36Sopenharmony_ci		*p++ = t3_read_reg(ap, start);
173462306a36Sopenharmony_ci}
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_cistatic void get_regs(struct net_device *dev, struct ethtool_regs *regs,
173762306a36Sopenharmony_ci		     void *buf)
173862306a36Sopenharmony_ci{
173962306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
174062306a36Sopenharmony_ci	struct adapter *ap = pi->adapter;
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci	/*
174362306a36Sopenharmony_ci	 * Version scheme:
174462306a36Sopenharmony_ci	 * bits 0..9: chip version
174562306a36Sopenharmony_ci	 * bits 10..15: chip revision
174662306a36Sopenharmony_ci	 * bit 31: set for PCIe cards
174762306a36Sopenharmony_ci	 */
174862306a36Sopenharmony_ci	regs->version = 3 | (ap->params.rev << 10) | (is_pcie(ap) << 31);
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	/*
175162306a36Sopenharmony_ci	 * We skip the MAC statistics registers because they are clear-on-read.
175262306a36Sopenharmony_ci	 * Also reading multi-register stats would need to synchronize with the
175362306a36Sopenharmony_ci	 * periodic mac stats accumulation.  Hard to justify the complexity.
175462306a36Sopenharmony_ci	 */
175562306a36Sopenharmony_ci	memset(buf, 0, T3_REGMAP_SIZE);
175662306a36Sopenharmony_ci	reg_block_dump(ap, buf, 0, A_SG_RSPQ_CREDIT_RETURN);
175762306a36Sopenharmony_ci	reg_block_dump(ap, buf, A_SG_HI_DRB_HI_THRSH, A_ULPRX_PBL_ULIMIT);
175862306a36Sopenharmony_ci	reg_block_dump(ap, buf, A_ULPTX_CONFIG, A_MPS_INT_CAUSE);
175962306a36Sopenharmony_ci	reg_block_dump(ap, buf, A_CPL_SWITCH_CNTRL, A_CPL_MAP_TBL_DATA);
176062306a36Sopenharmony_ci	reg_block_dump(ap, buf, A_SMB_GLOBAL_TIME_CFG, A_XGM_SERDES_STAT3);
176162306a36Sopenharmony_ci	reg_block_dump(ap, buf, A_XGM_SERDES_STATUS0,
176262306a36Sopenharmony_ci		       XGM_REG(A_XGM_SERDES_STAT3, 1));
176362306a36Sopenharmony_ci	reg_block_dump(ap, buf, XGM_REG(A_XGM_SERDES_STATUS0, 1),
176462306a36Sopenharmony_ci		       XGM_REG(A_XGM_RX_SPI4_SOP_EOP_CNT, 1));
176562306a36Sopenharmony_ci}
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_cistatic int restart_autoneg(struct net_device *dev)
176862306a36Sopenharmony_ci{
176962306a36Sopenharmony_ci	struct port_info *p = netdev_priv(dev);
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	if (!netif_running(dev))
177262306a36Sopenharmony_ci		return -EAGAIN;
177362306a36Sopenharmony_ci	if (p->link_config.autoneg != AUTONEG_ENABLE)
177462306a36Sopenharmony_ci		return -EINVAL;
177562306a36Sopenharmony_ci	p->phy.ops->autoneg_restart(&p->phy);
177662306a36Sopenharmony_ci	return 0;
177762306a36Sopenharmony_ci}
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_cistatic int set_phys_id(struct net_device *dev,
178062306a36Sopenharmony_ci		       enum ethtool_phys_id_state state)
178162306a36Sopenharmony_ci{
178262306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
178362306a36Sopenharmony_ci	struct adapter *adapter = pi->adapter;
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	switch (state) {
178662306a36Sopenharmony_ci	case ETHTOOL_ID_ACTIVE:
178762306a36Sopenharmony_ci		return 1;	/* cycle on/off once per second */
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci	case ETHTOOL_ID_OFF:
179062306a36Sopenharmony_ci		t3_set_reg_field(adapter, A_T3DBG_GPIO_EN, F_GPIO0_OUT_VAL, 0);
179162306a36Sopenharmony_ci		break;
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci	case ETHTOOL_ID_ON:
179462306a36Sopenharmony_ci	case ETHTOOL_ID_INACTIVE:
179562306a36Sopenharmony_ci		t3_set_reg_field(adapter, A_T3DBG_GPIO_EN, F_GPIO0_OUT_VAL,
179662306a36Sopenharmony_ci			 F_GPIO0_OUT_VAL);
179762306a36Sopenharmony_ci	}
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci	return 0;
180062306a36Sopenharmony_ci}
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_cistatic int get_link_ksettings(struct net_device *dev,
180362306a36Sopenharmony_ci			      struct ethtool_link_ksettings *cmd)
180462306a36Sopenharmony_ci{
180562306a36Sopenharmony_ci	struct port_info *p = netdev_priv(dev);
180662306a36Sopenharmony_ci	u32 supported;
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
180962306a36Sopenharmony_ci						p->link_config.supported);
181062306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
181162306a36Sopenharmony_ci						p->link_config.advertising);
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci	if (netif_carrier_ok(dev)) {
181462306a36Sopenharmony_ci		cmd->base.speed = p->link_config.speed;
181562306a36Sopenharmony_ci		cmd->base.duplex = p->link_config.duplex;
181662306a36Sopenharmony_ci	} else {
181762306a36Sopenharmony_ci		cmd->base.speed = SPEED_UNKNOWN;
181862306a36Sopenharmony_ci		cmd->base.duplex = DUPLEX_UNKNOWN;
181962306a36Sopenharmony_ci	}
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci	ethtool_convert_link_mode_to_legacy_u32(&supported,
182262306a36Sopenharmony_ci						cmd->link_modes.supported);
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	cmd->base.port = (supported & SUPPORTED_TP) ? PORT_TP : PORT_FIBRE;
182562306a36Sopenharmony_ci	cmd->base.phy_address = p->phy.mdio.prtad;
182662306a36Sopenharmony_ci	cmd->base.autoneg = p->link_config.autoneg;
182762306a36Sopenharmony_ci	return 0;
182862306a36Sopenharmony_ci}
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_cistatic int speed_duplex_to_caps(int speed, int duplex)
183162306a36Sopenharmony_ci{
183262306a36Sopenharmony_ci	int cap = 0;
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	switch (speed) {
183562306a36Sopenharmony_ci	case SPEED_10:
183662306a36Sopenharmony_ci		if (duplex == DUPLEX_FULL)
183762306a36Sopenharmony_ci			cap = SUPPORTED_10baseT_Full;
183862306a36Sopenharmony_ci		else
183962306a36Sopenharmony_ci			cap = SUPPORTED_10baseT_Half;
184062306a36Sopenharmony_ci		break;
184162306a36Sopenharmony_ci	case SPEED_100:
184262306a36Sopenharmony_ci		if (duplex == DUPLEX_FULL)
184362306a36Sopenharmony_ci			cap = SUPPORTED_100baseT_Full;
184462306a36Sopenharmony_ci		else
184562306a36Sopenharmony_ci			cap = SUPPORTED_100baseT_Half;
184662306a36Sopenharmony_ci		break;
184762306a36Sopenharmony_ci	case SPEED_1000:
184862306a36Sopenharmony_ci		if (duplex == DUPLEX_FULL)
184962306a36Sopenharmony_ci			cap = SUPPORTED_1000baseT_Full;
185062306a36Sopenharmony_ci		else
185162306a36Sopenharmony_ci			cap = SUPPORTED_1000baseT_Half;
185262306a36Sopenharmony_ci		break;
185362306a36Sopenharmony_ci	case SPEED_10000:
185462306a36Sopenharmony_ci		if (duplex == DUPLEX_FULL)
185562306a36Sopenharmony_ci			cap = SUPPORTED_10000baseT_Full;
185662306a36Sopenharmony_ci	}
185762306a36Sopenharmony_ci	return cap;
185862306a36Sopenharmony_ci}
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci#define ADVERTISED_MASK (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | \
186162306a36Sopenharmony_ci		      ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | \
186262306a36Sopenharmony_ci		      ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full | \
186362306a36Sopenharmony_ci		      ADVERTISED_10000baseT_Full)
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_cistatic int set_link_ksettings(struct net_device *dev,
186662306a36Sopenharmony_ci			      const struct ethtool_link_ksettings *cmd)
186762306a36Sopenharmony_ci{
186862306a36Sopenharmony_ci	struct port_info *p = netdev_priv(dev);
186962306a36Sopenharmony_ci	struct link_config *lc = &p->link_config;
187062306a36Sopenharmony_ci	u32 advertising;
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	ethtool_convert_link_mode_to_legacy_u32(&advertising,
187362306a36Sopenharmony_ci						cmd->link_modes.advertising);
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci	if (!(lc->supported & SUPPORTED_Autoneg)) {
187662306a36Sopenharmony_ci		/*
187762306a36Sopenharmony_ci		 * PHY offers a single speed/duplex.  See if that's what's
187862306a36Sopenharmony_ci		 * being requested.
187962306a36Sopenharmony_ci		 */
188062306a36Sopenharmony_ci		if (cmd->base.autoneg == AUTONEG_DISABLE) {
188162306a36Sopenharmony_ci			u32 speed = cmd->base.speed;
188262306a36Sopenharmony_ci			int cap = speed_duplex_to_caps(speed, cmd->base.duplex);
188362306a36Sopenharmony_ci			if (lc->supported & cap)
188462306a36Sopenharmony_ci				return 0;
188562306a36Sopenharmony_ci		}
188662306a36Sopenharmony_ci		return -EINVAL;
188762306a36Sopenharmony_ci	}
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci	if (cmd->base.autoneg == AUTONEG_DISABLE) {
189062306a36Sopenharmony_ci		u32 speed = cmd->base.speed;
189162306a36Sopenharmony_ci		int cap = speed_duplex_to_caps(speed, cmd->base.duplex);
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci		if (!(lc->supported & cap) || (speed == SPEED_1000))
189462306a36Sopenharmony_ci			return -EINVAL;
189562306a36Sopenharmony_ci		lc->requested_speed = speed;
189662306a36Sopenharmony_ci		lc->requested_duplex = cmd->base.duplex;
189762306a36Sopenharmony_ci		lc->advertising = 0;
189862306a36Sopenharmony_ci	} else {
189962306a36Sopenharmony_ci		advertising &= ADVERTISED_MASK;
190062306a36Sopenharmony_ci		advertising &= lc->supported;
190162306a36Sopenharmony_ci		if (!advertising)
190262306a36Sopenharmony_ci			return -EINVAL;
190362306a36Sopenharmony_ci		lc->requested_speed = SPEED_INVALID;
190462306a36Sopenharmony_ci		lc->requested_duplex = DUPLEX_INVALID;
190562306a36Sopenharmony_ci		lc->advertising = advertising | ADVERTISED_Autoneg;
190662306a36Sopenharmony_ci	}
190762306a36Sopenharmony_ci	lc->autoneg = cmd->base.autoneg;
190862306a36Sopenharmony_ci	if (netif_running(dev))
190962306a36Sopenharmony_ci		t3_link_start(&p->phy, &p->mac, lc);
191062306a36Sopenharmony_ci	return 0;
191162306a36Sopenharmony_ci}
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_cistatic void get_pauseparam(struct net_device *dev,
191462306a36Sopenharmony_ci			   struct ethtool_pauseparam *epause)
191562306a36Sopenharmony_ci{
191662306a36Sopenharmony_ci	struct port_info *p = netdev_priv(dev);
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci	epause->autoneg = (p->link_config.requested_fc & PAUSE_AUTONEG) != 0;
191962306a36Sopenharmony_ci	epause->rx_pause = (p->link_config.fc & PAUSE_RX) != 0;
192062306a36Sopenharmony_ci	epause->tx_pause = (p->link_config.fc & PAUSE_TX) != 0;
192162306a36Sopenharmony_ci}
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_cistatic int set_pauseparam(struct net_device *dev,
192462306a36Sopenharmony_ci			  struct ethtool_pauseparam *epause)
192562306a36Sopenharmony_ci{
192662306a36Sopenharmony_ci	struct port_info *p = netdev_priv(dev);
192762306a36Sopenharmony_ci	struct link_config *lc = &p->link_config;
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_ci	if (epause->autoneg == AUTONEG_DISABLE)
193062306a36Sopenharmony_ci		lc->requested_fc = 0;
193162306a36Sopenharmony_ci	else if (lc->supported & SUPPORTED_Autoneg)
193262306a36Sopenharmony_ci		lc->requested_fc = PAUSE_AUTONEG;
193362306a36Sopenharmony_ci	else
193462306a36Sopenharmony_ci		return -EINVAL;
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	if (epause->rx_pause)
193762306a36Sopenharmony_ci		lc->requested_fc |= PAUSE_RX;
193862306a36Sopenharmony_ci	if (epause->tx_pause)
193962306a36Sopenharmony_ci		lc->requested_fc |= PAUSE_TX;
194062306a36Sopenharmony_ci	if (lc->autoneg == AUTONEG_ENABLE) {
194162306a36Sopenharmony_ci		if (netif_running(dev))
194262306a36Sopenharmony_ci			t3_link_start(&p->phy, &p->mac, lc);
194362306a36Sopenharmony_ci	} else {
194462306a36Sopenharmony_ci		lc->fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX);
194562306a36Sopenharmony_ci		if (netif_running(dev))
194662306a36Sopenharmony_ci			t3_mac_set_speed_duplex_fc(&p->mac, -1, -1, lc->fc);
194762306a36Sopenharmony_ci	}
194862306a36Sopenharmony_ci	return 0;
194962306a36Sopenharmony_ci}
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_cistatic void get_sge_param(struct net_device *dev, struct ethtool_ringparam *e,
195262306a36Sopenharmony_ci			  struct kernel_ethtool_ringparam *kernel_e,
195362306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
195462306a36Sopenharmony_ci{
195562306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
195662306a36Sopenharmony_ci	struct adapter *adapter = pi->adapter;
195762306a36Sopenharmony_ci	const struct qset_params *q = &adapter->params.sge.qset[pi->first_qset];
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci	e->rx_max_pending = MAX_RX_BUFFERS;
196062306a36Sopenharmony_ci	e->rx_jumbo_max_pending = MAX_RX_JUMBO_BUFFERS;
196162306a36Sopenharmony_ci	e->tx_max_pending = MAX_TXQ_ENTRIES;
196262306a36Sopenharmony_ci
196362306a36Sopenharmony_ci	e->rx_pending = q->fl_size;
196462306a36Sopenharmony_ci	e->rx_mini_pending = q->rspq_size;
196562306a36Sopenharmony_ci	e->rx_jumbo_pending = q->jumbo_size;
196662306a36Sopenharmony_ci	e->tx_pending = q->txq_size[0];
196762306a36Sopenharmony_ci}
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_cistatic int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e,
197062306a36Sopenharmony_ci			 struct kernel_ethtool_ringparam *kernel_e,
197162306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
197262306a36Sopenharmony_ci{
197362306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
197462306a36Sopenharmony_ci	struct adapter *adapter = pi->adapter;
197562306a36Sopenharmony_ci	struct qset_params *q;
197662306a36Sopenharmony_ci	int i;
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci	if (e->rx_pending > MAX_RX_BUFFERS ||
197962306a36Sopenharmony_ci	    e->rx_jumbo_pending > MAX_RX_JUMBO_BUFFERS ||
198062306a36Sopenharmony_ci	    e->tx_pending > MAX_TXQ_ENTRIES ||
198162306a36Sopenharmony_ci	    e->rx_mini_pending > MAX_RSPQ_ENTRIES ||
198262306a36Sopenharmony_ci	    e->rx_mini_pending < MIN_RSPQ_ENTRIES ||
198362306a36Sopenharmony_ci	    e->rx_pending < MIN_FL_ENTRIES ||
198462306a36Sopenharmony_ci	    e->rx_jumbo_pending < MIN_FL_ENTRIES ||
198562306a36Sopenharmony_ci	    e->tx_pending < adapter->params.nports * MIN_TXQ_ENTRIES)
198662306a36Sopenharmony_ci		return -EINVAL;
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci	if (adapter->flags & FULL_INIT_DONE)
198962306a36Sopenharmony_ci		return -EBUSY;
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci	q = &adapter->params.sge.qset[pi->first_qset];
199262306a36Sopenharmony_ci	for (i = 0; i < pi->nqsets; ++i, ++q) {
199362306a36Sopenharmony_ci		q->rspq_size = e->rx_mini_pending;
199462306a36Sopenharmony_ci		q->fl_size = e->rx_pending;
199562306a36Sopenharmony_ci		q->jumbo_size = e->rx_jumbo_pending;
199662306a36Sopenharmony_ci		q->txq_size[0] = e->tx_pending;
199762306a36Sopenharmony_ci		q->txq_size[1] = e->tx_pending;
199862306a36Sopenharmony_ci		q->txq_size[2] = e->tx_pending;
199962306a36Sopenharmony_ci	}
200062306a36Sopenharmony_ci	return 0;
200162306a36Sopenharmony_ci}
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_cistatic int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c,
200462306a36Sopenharmony_ci			struct kernel_ethtool_coalesce *kernel_coal,
200562306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
200662306a36Sopenharmony_ci{
200762306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
200862306a36Sopenharmony_ci	struct adapter *adapter = pi->adapter;
200962306a36Sopenharmony_ci	struct qset_params *qsp;
201062306a36Sopenharmony_ci	struct sge_qset *qs;
201162306a36Sopenharmony_ci	int i;
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci	if (c->rx_coalesce_usecs * 10 > M_NEWTIMER)
201462306a36Sopenharmony_ci		return -EINVAL;
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci	for (i = 0; i < pi->nqsets; i++) {
201762306a36Sopenharmony_ci		qsp = &adapter->params.sge.qset[i];
201862306a36Sopenharmony_ci		qs = &adapter->sge.qs[i];
201962306a36Sopenharmony_ci		qsp->coalesce_usecs = c->rx_coalesce_usecs;
202062306a36Sopenharmony_ci		t3_update_qset_coalesce(qs, qsp);
202162306a36Sopenharmony_ci	}
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	return 0;
202462306a36Sopenharmony_ci}
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_cistatic int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c,
202762306a36Sopenharmony_ci			struct kernel_ethtool_coalesce *kernel_coal,
202862306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
202962306a36Sopenharmony_ci{
203062306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
203162306a36Sopenharmony_ci	struct adapter *adapter = pi->adapter;
203262306a36Sopenharmony_ci	struct qset_params *q = adapter->params.sge.qset;
203362306a36Sopenharmony_ci
203462306a36Sopenharmony_ci	c->rx_coalesce_usecs = q->coalesce_usecs;
203562306a36Sopenharmony_ci	return 0;
203662306a36Sopenharmony_ci}
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_cistatic int get_eeprom(struct net_device *dev, struct ethtool_eeprom *e,
203962306a36Sopenharmony_ci		      u8 * data)
204062306a36Sopenharmony_ci{
204162306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
204262306a36Sopenharmony_ci	struct adapter *adapter = pi->adapter;
204362306a36Sopenharmony_ci	int cnt;
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_ci	e->magic = EEPROM_MAGIC;
204662306a36Sopenharmony_ci	cnt = pci_read_vpd(adapter->pdev, e->offset, e->len, data);
204762306a36Sopenharmony_ci	if (cnt < 0)
204862306a36Sopenharmony_ci		return cnt;
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ci	e->len = cnt;
205162306a36Sopenharmony_ci
205262306a36Sopenharmony_ci	return 0;
205362306a36Sopenharmony_ci}
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_cistatic int set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
205662306a36Sopenharmony_ci		      u8 * data)
205762306a36Sopenharmony_ci{
205862306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
205962306a36Sopenharmony_ci	struct adapter *adapter = pi->adapter;
206062306a36Sopenharmony_ci	u32 aligned_offset, aligned_len;
206162306a36Sopenharmony_ci	u8 *buf;
206262306a36Sopenharmony_ci	int err;
206362306a36Sopenharmony_ci
206462306a36Sopenharmony_ci	if (eeprom->magic != EEPROM_MAGIC)
206562306a36Sopenharmony_ci		return -EINVAL;
206662306a36Sopenharmony_ci
206762306a36Sopenharmony_ci	aligned_offset = eeprom->offset & ~3;
206862306a36Sopenharmony_ci	aligned_len = (eeprom->len + (eeprom->offset & 3) + 3) & ~3;
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci	if (aligned_offset != eeprom->offset || aligned_len != eeprom->len) {
207162306a36Sopenharmony_ci		buf = kmalloc(aligned_len, GFP_KERNEL);
207262306a36Sopenharmony_ci		if (!buf)
207362306a36Sopenharmony_ci			return -ENOMEM;
207462306a36Sopenharmony_ci		err = pci_read_vpd(adapter->pdev, aligned_offset, aligned_len,
207562306a36Sopenharmony_ci				   buf);
207662306a36Sopenharmony_ci		if (err < 0)
207762306a36Sopenharmony_ci			goto out;
207862306a36Sopenharmony_ci		memcpy(buf + (eeprom->offset & 3), data, eeprom->len);
207962306a36Sopenharmony_ci	} else
208062306a36Sopenharmony_ci		buf = data;
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci	err = t3_seeprom_wp(adapter, 0);
208362306a36Sopenharmony_ci	if (err)
208462306a36Sopenharmony_ci		goto out;
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	err = pci_write_vpd(adapter->pdev, aligned_offset, aligned_len, buf);
208762306a36Sopenharmony_ci	if (err >= 0)
208862306a36Sopenharmony_ci		err = t3_seeprom_wp(adapter, 1);
208962306a36Sopenharmony_ciout:
209062306a36Sopenharmony_ci	if (buf != data)
209162306a36Sopenharmony_ci		kfree(buf);
209262306a36Sopenharmony_ci	return err < 0 ? err : 0;
209362306a36Sopenharmony_ci}
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_cistatic void get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
209662306a36Sopenharmony_ci{
209762306a36Sopenharmony_ci	wol->supported = 0;
209862306a36Sopenharmony_ci	wol->wolopts = 0;
209962306a36Sopenharmony_ci	memset(&wol->sopass, 0, sizeof(wol->sopass));
210062306a36Sopenharmony_ci}
210162306a36Sopenharmony_ci
210262306a36Sopenharmony_cistatic const struct ethtool_ops cxgb_ethtool_ops = {
210362306a36Sopenharmony_ci	.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS,
210462306a36Sopenharmony_ci	.get_drvinfo = get_drvinfo,
210562306a36Sopenharmony_ci	.get_msglevel = get_msglevel,
210662306a36Sopenharmony_ci	.set_msglevel = set_msglevel,
210762306a36Sopenharmony_ci	.get_ringparam = get_sge_param,
210862306a36Sopenharmony_ci	.set_ringparam = set_sge_param,
210962306a36Sopenharmony_ci	.get_coalesce = get_coalesce,
211062306a36Sopenharmony_ci	.set_coalesce = set_coalesce,
211162306a36Sopenharmony_ci	.get_eeprom_len = get_eeprom_len,
211262306a36Sopenharmony_ci	.get_eeprom = get_eeprom,
211362306a36Sopenharmony_ci	.set_eeprom = set_eeprom,
211462306a36Sopenharmony_ci	.get_pauseparam = get_pauseparam,
211562306a36Sopenharmony_ci	.set_pauseparam = set_pauseparam,
211662306a36Sopenharmony_ci	.get_link = ethtool_op_get_link,
211762306a36Sopenharmony_ci	.get_strings = get_strings,
211862306a36Sopenharmony_ci	.set_phys_id = set_phys_id,
211962306a36Sopenharmony_ci	.nway_reset = restart_autoneg,
212062306a36Sopenharmony_ci	.get_sset_count = get_sset_count,
212162306a36Sopenharmony_ci	.get_ethtool_stats = get_stats,
212262306a36Sopenharmony_ci	.get_regs_len = get_regs_len,
212362306a36Sopenharmony_ci	.get_regs = get_regs,
212462306a36Sopenharmony_ci	.get_wol = get_wol,
212562306a36Sopenharmony_ci	.get_link_ksettings = get_link_ksettings,
212662306a36Sopenharmony_ci	.set_link_ksettings = set_link_ksettings,
212762306a36Sopenharmony_ci};
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_cistatic int cxgb_in_range(int val, int lo, int hi)
213062306a36Sopenharmony_ci{
213162306a36Sopenharmony_ci	return val < 0 || (val <= hi && val >= lo);
213262306a36Sopenharmony_ci}
213362306a36Sopenharmony_ci
213462306a36Sopenharmony_cistatic int cxgb_siocdevprivate(struct net_device *dev,
213562306a36Sopenharmony_ci			       struct ifreq *ifreq,
213662306a36Sopenharmony_ci			       void __user *useraddr,
213762306a36Sopenharmony_ci			       int cmd)
213862306a36Sopenharmony_ci{
213962306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
214062306a36Sopenharmony_ci	struct adapter *adapter = pi->adapter;
214162306a36Sopenharmony_ci	int ret;
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	if (cmd != SIOCCHIOCTL)
214462306a36Sopenharmony_ci		return -EOPNOTSUPP;
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci	if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
214762306a36Sopenharmony_ci		return -EFAULT;
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci	switch (cmd) {
215062306a36Sopenharmony_ci	case CHELSIO_SET_QSET_PARAMS:{
215162306a36Sopenharmony_ci		int i;
215262306a36Sopenharmony_ci		struct qset_params *q;
215362306a36Sopenharmony_ci		struct ch_qset_params t;
215462306a36Sopenharmony_ci		int q1 = pi->first_qset;
215562306a36Sopenharmony_ci		int nqsets = pi->nqsets;
215662306a36Sopenharmony_ci
215762306a36Sopenharmony_ci		if (!capable(CAP_NET_ADMIN))
215862306a36Sopenharmony_ci			return -EPERM;
215962306a36Sopenharmony_ci		if (copy_from_user(&t, useraddr, sizeof(t)))
216062306a36Sopenharmony_ci			return -EFAULT;
216162306a36Sopenharmony_ci		if (t.cmd != CHELSIO_SET_QSET_PARAMS)
216262306a36Sopenharmony_ci			return -EINVAL;
216362306a36Sopenharmony_ci		if (t.qset_idx >= SGE_QSETS)
216462306a36Sopenharmony_ci			return -EINVAL;
216562306a36Sopenharmony_ci		if (!cxgb_in_range(t.intr_lat, 0, M_NEWTIMER) ||
216662306a36Sopenharmony_ci		    !cxgb_in_range(t.cong_thres, 0, 255) ||
216762306a36Sopenharmony_ci		    !cxgb_in_range(t.txq_size[0], MIN_TXQ_ENTRIES,
216862306a36Sopenharmony_ci			      MAX_TXQ_ENTRIES) ||
216962306a36Sopenharmony_ci		    !cxgb_in_range(t.txq_size[1], MIN_TXQ_ENTRIES,
217062306a36Sopenharmony_ci			      MAX_TXQ_ENTRIES) ||
217162306a36Sopenharmony_ci		    !cxgb_in_range(t.txq_size[2], MIN_CTRL_TXQ_ENTRIES,
217262306a36Sopenharmony_ci			      MAX_CTRL_TXQ_ENTRIES) ||
217362306a36Sopenharmony_ci		    !cxgb_in_range(t.fl_size[0], MIN_FL_ENTRIES,
217462306a36Sopenharmony_ci			      MAX_RX_BUFFERS) ||
217562306a36Sopenharmony_ci		    !cxgb_in_range(t.fl_size[1], MIN_FL_ENTRIES,
217662306a36Sopenharmony_ci			      MAX_RX_JUMBO_BUFFERS) ||
217762306a36Sopenharmony_ci		    !cxgb_in_range(t.rspq_size, MIN_RSPQ_ENTRIES,
217862306a36Sopenharmony_ci			      MAX_RSPQ_ENTRIES))
217962306a36Sopenharmony_ci			return -EINVAL;
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_ci		if ((adapter->flags & FULL_INIT_DONE) &&
218262306a36Sopenharmony_ci			(t.rspq_size >= 0 || t.fl_size[0] >= 0 ||
218362306a36Sopenharmony_ci			t.fl_size[1] >= 0 || t.txq_size[0] >= 0 ||
218462306a36Sopenharmony_ci			t.txq_size[1] >= 0 || t.txq_size[2] >= 0 ||
218562306a36Sopenharmony_ci			t.polling >= 0 || t.cong_thres >= 0))
218662306a36Sopenharmony_ci			return -EBUSY;
218762306a36Sopenharmony_ci
218862306a36Sopenharmony_ci		/* Allow setting of any available qset when offload enabled */
218962306a36Sopenharmony_ci		if (test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map)) {
219062306a36Sopenharmony_ci			q1 = 0;
219162306a36Sopenharmony_ci			for_each_port(adapter, i) {
219262306a36Sopenharmony_ci				pi = adap2pinfo(adapter, i);
219362306a36Sopenharmony_ci				nqsets += pi->first_qset + pi->nqsets;
219462306a36Sopenharmony_ci			}
219562306a36Sopenharmony_ci		}
219662306a36Sopenharmony_ci
219762306a36Sopenharmony_ci		if (t.qset_idx < q1)
219862306a36Sopenharmony_ci			return -EINVAL;
219962306a36Sopenharmony_ci		if (t.qset_idx > q1 + nqsets - 1)
220062306a36Sopenharmony_ci			return -EINVAL;
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci		q = &adapter->params.sge.qset[t.qset_idx];
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci		if (t.rspq_size >= 0)
220562306a36Sopenharmony_ci			q->rspq_size = t.rspq_size;
220662306a36Sopenharmony_ci		if (t.fl_size[0] >= 0)
220762306a36Sopenharmony_ci			q->fl_size = t.fl_size[0];
220862306a36Sopenharmony_ci		if (t.fl_size[1] >= 0)
220962306a36Sopenharmony_ci			q->jumbo_size = t.fl_size[1];
221062306a36Sopenharmony_ci		if (t.txq_size[0] >= 0)
221162306a36Sopenharmony_ci			q->txq_size[0] = t.txq_size[0];
221262306a36Sopenharmony_ci		if (t.txq_size[1] >= 0)
221362306a36Sopenharmony_ci			q->txq_size[1] = t.txq_size[1];
221462306a36Sopenharmony_ci		if (t.txq_size[2] >= 0)
221562306a36Sopenharmony_ci			q->txq_size[2] = t.txq_size[2];
221662306a36Sopenharmony_ci		if (t.cong_thres >= 0)
221762306a36Sopenharmony_ci			q->cong_thres = t.cong_thres;
221862306a36Sopenharmony_ci		if (t.intr_lat >= 0) {
221962306a36Sopenharmony_ci			struct sge_qset *qs =
222062306a36Sopenharmony_ci				&adapter->sge.qs[t.qset_idx];
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci			q->coalesce_usecs = t.intr_lat;
222362306a36Sopenharmony_ci			t3_update_qset_coalesce(qs, q);
222462306a36Sopenharmony_ci		}
222562306a36Sopenharmony_ci		if (t.polling >= 0) {
222662306a36Sopenharmony_ci			if (adapter->flags & USING_MSIX)
222762306a36Sopenharmony_ci				q->polling = t.polling;
222862306a36Sopenharmony_ci			else {
222962306a36Sopenharmony_ci				/* No polling with INTx for T3A */
223062306a36Sopenharmony_ci				if (adapter->params.rev == 0 &&
223162306a36Sopenharmony_ci					!(adapter->flags & USING_MSI))
223262306a36Sopenharmony_ci					t.polling = 0;
223362306a36Sopenharmony_ci
223462306a36Sopenharmony_ci				for (i = 0; i < SGE_QSETS; i++) {
223562306a36Sopenharmony_ci					q = &adapter->params.sge.
223662306a36Sopenharmony_ci						qset[i];
223762306a36Sopenharmony_ci					q->polling = t.polling;
223862306a36Sopenharmony_ci				}
223962306a36Sopenharmony_ci			}
224062306a36Sopenharmony_ci		}
224162306a36Sopenharmony_ci
224262306a36Sopenharmony_ci		if (t.lro >= 0) {
224362306a36Sopenharmony_ci			if (t.lro)
224462306a36Sopenharmony_ci				dev->wanted_features |= NETIF_F_GRO;
224562306a36Sopenharmony_ci			else
224662306a36Sopenharmony_ci				dev->wanted_features &= ~NETIF_F_GRO;
224762306a36Sopenharmony_ci			netdev_update_features(dev);
224862306a36Sopenharmony_ci		}
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_ci		break;
225162306a36Sopenharmony_ci	}
225262306a36Sopenharmony_ci	case CHELSIO_GET_QSET_PARAMS:{
225362306a36Sopenharmony_ci		struct qset_params *q;
225462306a36Sopenharmony_ci		struct ch_qset_params t;
225562306a36Sopenharmony_ci		int q1 = pi->first_qset;
225662306a36Sopenharmony_ci		int nqsets = pi->nqsets;
225762306a36Sopenharmony_ci		int i;
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci		if (copy_from_user(&t, useraddr, sizeof(t)))
226062306a36Sopenharmony_ci			return -EFAULT;
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci		if (t.cmd != CHELSIO_GET_QSET_PARAMS)
226362306a36Sopenharmony_ci			return -EINVAL;
226462306a36Sopenharmony_ci
226562306a36Sopenharmony_ci		/* Display qsets for all ports when offload enabled */
226662306a36Sopenharmony_ci		if (test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map)) {
226762306a36Sopenharmony_ci			q1 = 0;
226862306a36Sopenharmony_ci			for_each_port(adapter, i) {
226962306a36Sopenharmony_ci				pi = adap2pinfo(adapter, i);
227062306a36Sopenharmony_ci				nqsets = pi->first_qset + pi->nqsets;
227162306a36Sopenharmony_ci			}
227262306a36Sopenharmony_ci		}
227362306a36Sopenharmony_ci
227462306a36Sopenharmony_ci		if (t.qset_idx >= nqsets)
227562306a36Sopenharmony_ci			return -EINVAL;
227662306a36Sopenharmony_ci		t.qset_idx = array_index_nospec(t.qset_idx, nqsets);
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_ci		q = &adapter->params.sge.qset[q1 + t.qset_idx];
227962306a36Sopenharmony_ci		t.rspq_size = q->rspq_size;
228062306a36Sopenharmony_ci		t.txq_size[0] = q->txq_size[0];
228162306a36Sopenharmony_ci		t.txq_size[1] = q->txq_size[1];
228262306a36Sopenharmony_ci		t.txq_size[2] = q->txq_size[2];
228362306a36Sopenharmony_ci		t.fl_size[0] = q->fl_size;
228462306a36Sopenharmony_ci		t.fl_size[1] = q->jumbo_size;
228562306a36Sopenharmony_ci		t.polling = q->polling;
228662306a36Sopenharmony_ci		t.lro = !!(dev->features & NETIF_F_GRO);
228762306a36Sopenharmony_ci		t.intr_lat = q->coalesce_usecs;
228862306a36Sopenharmony_ci		t.cong_thres = q->cong_thres;
228962306a36Sopenharmony_ci		t.qnum = q1;
229062306a36Sopenharmony_ci
229162306a36Sopenharmony_ci		if (adapter->flags & USING_MSIX)
229262306a36Sopenharmony_ci			t.vector = adapter->msix_info[q1 + t.qset_idx + 1].vec;
229362306a36Sopenharmony_ci		else
229462306a36Sopenharmony_ci			t.vector = adapter->pdev->irq;
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci		if (copy_to_user(useraddr, &t, sizeof(t)))
229762306a36Sopenharmony_ci			return -EFAULT;
229862306a36Sopenharmony_ci		break;
229962306a36Sopenharmony_ci	}
230062306a36Sopenharmony_ci	case CHELSIO_SET_QSET_NUM:{
230162306a36Sopenharmony_ci		struct ch_reg edata;
230262306a36Sopenharmony_ci		unsigned int i, first_qset = 0, other_qsets = 0;
230362306a36Sopenharmony_ci
230462306a36Sopenharmony_ci		if (!capable(CAP_NET_ADMIN))
230562306a36Sopenharmony_ci			return -EPERM;
230662306a36Sopenharmony_ci		if (adapter->flags & FULL_INIT_DONE)
230762306a36Sopenharmony_ci			return -EBUSY;
230862306a36Sopenharmony_ci		if (copy_from_user(&edata, useraddr, sizeof(edata)))
230962306a36Sopenharmony_ci			return -EFAULT;
231062306a36Sopenharmony_ci		if (edata.cmd != CHELSIO_SET_QSET_NUM)
231162306a36Sopenharmony_ci			return -EINVAL;
231262306a36Sopenharmony_ci		if (edata.val < 1 ||
231362306a36Sopenharmony_ci			(edata.val > 1 && !(adapter->flags & USING_MSIX)))
231462306a36Sopenharmony_ci			return -EINVAL;
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_ci		for_each_port(adapter, i)
231762306a36Sopenharmony_ci			if (adapter->port[i] && adapter->port[i] != dev)
231862306a36Sopenharmony_ci				other_qsets += adap2pinfo(adapter, i)->nqsets;
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_ci		if (edata.val + other_qsets > SGE_QSETS)
232162306a36Sopenharmony_ci			return -EINVAL;
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci		pi->nqsets = edata.val;
232462306a36Sopenharmony_ci
232562306a36Sopenharmony_ci		for_each_port(adapter, i)
232662306a36Sopenharmony_ci			if (adapter->port[i]) {
232762306a36Sopenharmony_ci				pi = adap2pinfo(adapter, i);
232862306a36Sopenharmony_ci				pi->first_qset = first_qset;
232962306a36Sopenharmony_ci				first_qset += pi->nqsets;
233062306a36Sopenharmony_ci			}
233162306a36Sopenharmony_ci		break;
233262306a36Sopenharmony_ci	}
233362306a36Sopenharmony_ci	case CHELSIO_GET_QSET_NUM:{
233462306a36Sopenharmony_ci		struct ch_reg edata;
233562306a36Sopenharmony_ci
233662306a36Sopenharmony_ci		memset(&edata, 0, sizeof(struct ch_reg));
233762306a36Sopenharmony_ci
233862306a36Sopenharmony_ci		edata.cmd = CHELSIO_GET_QSET_NUM;
233962306a36Sopenharmony_ci		edata.val = pi->nqsets;
234062306a36Sopenharmony_ci		if (copy_to_user(useraddr, &edata, sizeof(edata)))
234162306a36Sopenharmony_ci			return -EFAULT;
234262306a36Sopenharmony_ci		break;
234362306a36Sopenharmony_ci	}
234462306a36Sopenharmony_ci	case CHELSIO_LOAD_FW:{
234562306a36Sopenharmony_ci		u8 *fw_data;
234662306a36Sopenharmony_ci		struct ch_mem_range t;
234762306a36Sopenharmony_ci
234862306a36Sopenharmony_ci		if (!capable(CAP_SYS_RAWIO))
234962306a36Sopenharmony_ci			return -EPERM;
235062306a36Sopenharmony_ci		if (copy_from_user(&t, useraddr, sizeof(t)))
235162306a36Sopenharmony_ci			return -EFAULT;
235262306a36Sopenharmony_ci		if (t.cmd != CHELSIO_LOAD_FW)
235362306a36Sopenharmony_ci			return -EINVAL;
235462306a36Sopenharmony_ci		/* Check t.len sanity ? */
235562306a36Sopenharmony_ci		fw_data = memdup_user(useraddr + sizeof(t), t.len);
235662306a36Sopenharmony_ci		if (IS_ERR(fw_data))
235762306a36Sopenharmony_ci			return PTR_ERR(fw_data);
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci		ret = t3_load_fw(adapter, fw_data, t.len);
236062306a36Sopenharmony_ci		kfree(fw_data);
236162306a36Sopenharmony_ci		if (ret)
236262306a36Sopenharmony_ci			return ret;
236362306a36Sopenharmony_ci		break;
236462306a36Sopenharmony_ci	}
236562306a36Sopenharmony_ci	case CHELSIO_SETMTUTAB:{
236662306a36Sopenharmony_ci		struct ch_mtus m;
236762306a36Sopenharmony_ci		int i;
236862306a36Sopenharmony_ci
236962306a36Sopenharmony_ci		if (!is_offload(adapter))
237062306a36Sopenharmony_ci			return -EOPNOTSUPP;
237162306a36Sopenharmony_ci		if (!capable(CAP_NET_ADMIN))
237262306a36Sopenharmony_ci			return -EPERM;
237362306a36Sopenharmony_ci		if (offload_running(adapter))
237462306a36Sopenharmony_ci			return -EBUSY;
237562306a36Sopenharmony_ci		if (copy_from_user(&m, useraddr, sizeof(m)))
237662306a36Sopenharmony_ci			return -EFAULT;
237762306a36Sopenharmony_ci		if (m.cmd != CHELSIO_SETMTUTAB)
237862306a36Sopenharmony_ci			return -EINVAL;
237962306a36Sopenharmony_ci		if (m.nmtus != NMTUS)
238062306a36Sopenharmony_ci			return -EINVAL;
238162306a36Sopenharmony_ci		if (m.mtus[0] < 81)	/* accommodate SACK */
238262306a36Sopenharmony_ci			return -EINVAL;
238362306a36Sopenharmony_ci
238462306a36Sopenharmony_ci		/* MTUs must be in ascending order */
238562306a36Sopenharmony_ci		for (i = 1; i < NMTUS; ++i)
238662306a36Sopenharmony_ci			if (m.mtus[i] < m.mtus[i - 1])
238762306a36Sopenharmony_ci				return -EINVAL;
238862306a36Sopenharmony_ci
238962306a36Sopenharmony_ci		memcpy(adapter->params.mtus, m.mtus,
239062306a36Sopenharmony_ci			sizeof(adapter->params.mtus));
239162306a36Sopenharmony_ci		break;
239262306a36Sopenharmony_ci	}
239362306a36Sopenharmony_ci	case CHELSIO_GET_PM:{
239462306a36Sopenharmony_ci		struct tp_params *p = &adapter->params.tp;
239562306a36Sopenharmony_ci		struct ch_pm m = {.cmd = CHELSIO_GET_PM };
239662306a36Sopenharmony_ci
239762306a36Sopenharmony_ci		if (!is_offload(adapter))
239862306a36Sopenharmony_ci			return -EOPNOTSUPP;
239962306a36Sopenharmony_ci		m.tx_pg_sz = p->tx_pg_size;
240062306a36Sopenharmony_ci		m.tx_num_pg = p->tx_num_pgs;
240162306a36Sopenharmony_ci		m.rx_pg_sz = p->rx_pg_size;
240262306a36Sopenharmony_ci		m.rx_num_pg = p->rx_num_pgs;
240362306a36Sopenharmony_ci		m.pm_total = p->pmtx_size + p->chan_rx_size * p->nchan;
240462306a36Sopenharmony_ci		if (copy_to_user(useraddr, &m, sizeof(m)))
240562306a36Sopenharmony_ci			return -EFAULT;
240662306a36Sopenharmony_ci		break;
240762306a36Sopenharmony_ci	}
240862306a36Sopenharmony_ci	case CHELSIO_SET_PM:{
240962306a36Sopenharmony_ci		struct ch_pm m;
241062306a36Sopenharmony_ci		struct tp_params *p = &adapter->params.tp;
241162306a36Sopenharmony_ci
241262306a36Sopenharmony_ci		if (!is_offload(adapter))
241362306a36Sopenharmony_ci			return -EOPNOTSUPP;
241462306a36Sopenharmony_ci		if (!capable(CAP_NET_ADMIN))
241562306a36Sopenharmony_ci			return -EPERM;
241662306a36Sopenharmony_ci		if (adapter->flags & FULL_INIT_DONE)
241762306a36Sopenharmony_ci			return -EBUSY;
241862306a36Sopenharmony_ci		if (copy_from_user(&m, useraddr, sizeof(m)))
241962306a36Sopenharmony_ci			return -EFAULT;
242062306a36Sopenharmony_ci		if (m.cmd != CHELSIO_SET_PM)
242162306a36Sopenharmony_ci			return -EINVAL;
242262306a36Sopenharmony_ci		if (!is_power_of_2(m.rx_pg_sz) ||
242362306a36Sopenharmony_ci			!is_power_of_2(m.tx_pg_sz))
242462306a36Sopenharmony_ci			return -EINVAL;	/* not power of 2 */
242562306a36Sopenharmony_ci		if (!(m.rx_pg_sz & 0x14000))
242662306a36Sopenharmony_ci			return -EINVAL;	/* not 16KB or 64KB */
242762306a36Sopenharmony_ci		if (!(m.tx_pg_sz & 0x1554000))
242862306a36Sopenharmony_ci			return -EINVAL;
242962306a36Sopenharmony_ci		if (m.tx_num_pg == -1)
243062306a36Sopenharmony_ci			m.tx_num_pg = p->tx_num_pgs;
243162306a36Sopenharmony_ci		if (m.rx_num_pg == -1)
243262306a36Sopenharmony_ci			m.rx_num_pg = p->rx_num_pgs;
243362306a36Sopenharmony_ci		if (m.tx_num_pg % 24 || m.rx_num_pg % 24)
243462306a36Sopenharmony_ci			return -EINVAL;
243562306a36Sopenharmony_ci		if (m.rx_num_pg * m.rx_pg_sz > p->chan_rx_size ||
243662306a36Sopenharmony_ci			m.tx_num_pg * m.tx_pg_sz > p->chan_tx_size)
243762306a36Sopenharmony_ci			return -EINVAL;
243862306a36Sopenharmony_ci		p->rx_pg_size = m.rx_pg_sz;
243962306a36Sopenharmony_ci		p->tx_pg_size = m.tx_pg_sz;
244062306a36Sopenharmony_ci		p->rx_num_pgs = m.rx_num_pg;
244162306a36Sopenharmony_ci		p->tx_num_pgs = m.tx_num_pg;
244262306a36Sopenharmony_ci		break;
244362306a36Sopenharmony_ci	}
244462306a36Sopenharmony_ci	case CHELSIO_GET_MEM:{
244562306a36Sopenharmony_ci		struct ch_mem_range t;
244662306a36Sopenharmony_ci		struct mc7 *mem;
244762306a36Sopenharmony_ci		u64 buf[32];
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_ci		if (!is_offload(adapter))
245062306a36Sopenharmony_ci			return -EOPNOTSUPP;
245162306a36Sopenharmony_ci		if (!capable(CAP_NET_ADMIN))
245262306a36Sopenharmony_ci			return -EPERM;
245362306a36Sopenharmony_ci		if (!(adapter->flags & FULL_INIT_DONE))
245462306a36Sopenharmony_ci			return -EIO;	/* need the memory controllers */
245562306a36Sopenharmony_ci		if (copy_from_user(&t, useraddr, sizeof(t)))
245662306a36Sopenharmony_ci			return -EFAULT;
245762306a36Sopenharmony_ci		if (t.cmd != CHELSIO_GET_MEM)
245862306a36Sopenharmony_ci			return -EINVAL;
245962306a36Sopenharmony_ci		if ((t.addr & 7) || (t.len & 7))
246062306a36Sopenharmony_ci			return -EINVAL;
246162306a36Sopenharmony_ci		if (t.mem_id == MEM_CM)
246262306a36Sopenharmony_ci			mem = &adapter->cm;
246362306a36Sopenharmony_ci		else if (t.mem_id == MEM_PMRX)
246462306a36Sopenharmony_ci			mem = &adapter->pmrx;
246562306a36Sopenharmony_ci		else if (t.mem_id == MEM_PMTX)
246662306a36Sopenharmony_ci			mem = &adapter->pmtx;
246762306a36Sopenharmony_ci		else
246862306a36Sopenharmony_ci			return -EINVAL;
246962306a36Sopenharmony_ci
247062306a36Sopenharmony_ci		/*
247162306a36Sopenharmony_ci		 * Version scheme:
247262306a36Sopenharmony_ci		 * bits 0..9: chip version
247362306a36Sopenharmony_ci		 * bits 10..15: chip revision
247462306a36Sopenharmony_ci		 */
247562306a36Sopenharmony_ci		t.version = 3 | (adapter->params.rev << 10);
247662306a36Sopenharmony_ci		if (copy_to_user(useraddr, &t, sizeof(t)))
247762306a36Sopenharmony_ci			return -EFAULT;
247862306a36Sopenharmony_ci
247962306a36Sopenharmony_ci		/*
248062306a36Sopenharmony_ci		 * Read 256 bytes at a time as len can be large and we don't
248162306a36Sopenharmony_ci		 * want to use huge intermediate buffers.
248262306a36Sopenharmony_ci		 */
248362306a36Sopenharmony_ci		useraddr += sizeof(t);	/* advance to start of buffer */
248462306a36Sopenharmony_ci		while (t.len) {
248562306a36Sopenharmony_ci			unsigned int chunk =
248662306a36Sopenharmony_ci				min_t(unsigned int, t.len, sizeof(buf));
248762306a36Sopenharmony_ci
248862306a36Sopenharmony_ci			ret =
248962306a36Sopenharmony_ci				t3_mc7_bd_read(mem, t.addr / 8, chunk / 8,
249062306a36Sopenharmony_ci						buf);
249162306a36Sopenharmony_ci			if (ret)
249262306a36Sopenharmony_ci				return ret;
249362306a36Sopenharmony_ci			if (copy_to_user(useraddr, buf, chunk))
249462306a36Sopenharmony_ci				return -EFAULT;
249562306a36Sopenharmony_ci			useraddr += chunk;
249662306a36Sopenharmony_ci			t.addr += chunk;
249762306a36Sopenharmony_ci			t.len -= chunk;
249862306a36Sopenharmony_ci		}
249962306a36Sopenharmony_ci		break;
250062306a36Sopenharmony_ci	}
250162306a36Sopenharmony_ci	case CHELSIO_SET_TRACE_FILTER:{
250262306a36Sopenharmony_ci		struct ch_trace t;
250362306a36Sopenharmony_ci		const struct trace_params *tp;
250462306a36Sopenharmony_ci
250562306a36Sopenharmony_ci		if (!capable(CAP_NET_ADMIN))
250662306a36Sopenharmony_ci			return -EPERM;
250762306a36Sopenharmony_ci		if (!offload_running(adapter))
250862306a36Sopenharmony_ci			return -EAGAIN;
250962306a36Sopenharmony_ci		if (copy_from_user(&t, useraddr, sizeof(t)))
251062306a36Sopenharmony_ci			return -EFAULT;
251162306a36Sopenharmony_ci		if (t.cmd != CHELSIO_SET_TRACE_FILTER)
251262306a36Sopenharmony_ci			return -EINVAL;
251362306a36Sopenharmony_ci
251462306a36Sopenharmony_ci		tp = (const struct trace_params *)&t.sip;
251562306a36Sopenharmony_ci		if (t.config_tx)
251662306a36Sopenharmony_ci			t3_config_trace_filter(adapter, tp, 0,
251762306a36Sopenharmony_ci						t.invert_match,
251862306a36Sopenharmony_ci						t.trace_tx);
251962306a36Sopenharmony_ci		if (t.config_rx)
252062306a36Sopenharmony_ci			t3_config_trace_filter(adapter, tp, 1,
252162306a36Sopenharmony_ci						t.invert_match,
252262306a36Sopenharmony_ci						t.trace_rx);
252362306a36Sopenharmony_ci		break;
252462306a36Sopenharmony_ci	}
252562306a36Sopenharmony_ci	default:
252662306a36Sopenharmony_ci		return -EOPNOTSUPP;
252762306a36Sopenharmony_ci	}
252862306a36Sopenharmony_ci	return 0;
252962306a36Sopenharmony_ci}
253062306a36Sopenharmony_ci
253162306a36Sopenharmony_cistatic int cxgb_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
253262306a36Sopenharmony_ci{
253362306a36Sopenharmony_ci	struct mii_ioctl_data *data = if_mii(req);
253462306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
253562306a36Sopenharmony_ci	struct adapter *adapter = pi->adapter;
253662306a36Sopenharmony_ci
253762306a36Sopenharmony_ci	switch (cmd) {
253862306a36Sopenharmony_ci	case SIOCGMIIREG:
253962306a36Sopenharmony_ci	case SIOCSMIIREG:
254062306a36Sopenharmony_ci		/* Convert phy_id from older PRTAD/DEVAD format */
254162306a36Sopenharmony_ci		if (is_10G(adapter) &&
254262306a36Sopenharmony_ci		    !mdio_phy_id_is_c45(data->phy_id) &&
254362306a36Sopenharmony_ci		    (data->phy_id & 0x1f00) &&
254462306a36Sopenharmony_ci		    !(data->phy_id & 0xe0e0))
254562306a36Sopenharmony_ci			data->phy_id = mdio_phy_id_c45(data->phy_id >> 8,
254662306a36Sopenharmony_ci						       data->phy_id & 0x1f);
254762306a36Sopenharmony_ci		fallthrough;
254862306a36Sopenharmony_ci	case SIOCGMIIPHY:
254962306a36Sopenharmony_ci		return mdio_mii_ioctl(&pi->phy.mdio, data, cmd);
255062306a36Sopenharmony_ci	default:
255162306a36Sopenharmony_ci		return -EOPNOTSUPP;
255262306a36Sopenharmony_ci	}
255362306a36Sopenharmony_ci}
255462306a36Sopenharmony_ci
255562306a36Sopenharmony_cistatic int cxgb_change_mtu(struct net_device *dev, int new_mtu)
255662306a36Sopenharmony_ci{
255762306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
255862306a36Sopenharmony_ci	struct adapter *adapter = pi->adapter;
255962306a36Sopenharmony_ci	int ret;
256062306a36Sopenharmony_ci
256162306a36Sopenharmony_ci	if ((ret = t3_mac_set_mtu(&pi->mac, new_mtu)))
256262306a36Sopenharmony_ci		return ret;
256362306a36Sopenharmony_ci	dev->mtu = new_mtu;
256462306a36Sopenharmony_ci	init_port_mtus(adapter);
256562306a36Sopenharmony_ci	if (adapter->params.rev == 0 && offload_running(adapter))
256662306a36Sopenharmony_ci		t3_load_mtus(adapter, adapter->params.mtus,
256762306a36Sopenharmony_ci			     adapter->params.a_wnd, adapter->params.b_wnd,
256862306a36Sopenharmony_ci			     adapter->port[0]->mtu);
256962306a36Sopenharmony_ci	return 0;
257062306a36Sopenharmony_ci}
257162306a36Sopenharmony_ci
257262306a36Sopenharmony_cistatic int cxgb_set_mac_addr(struct net_device *dev, void *p)
257362306a36Sopenharmony_ci{
257462306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
257562306a36Sopenharmony_ci	struct adapter *adapter = pi->adapter;
257662306a36Sopenharmony_ci	struct sockaddr *addr = p;
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_ci	if (!is_valid_ether_addr(addr->sa_data))
257962306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
258062306a36Sopenharmony_ci
258162306a36Sopenharmony_ci	eth_hw_addr_set(dev, addr->sa_data);
258262306a36Sopenharmony_ci	t3_mac_set_address(&pi->mac, LAN_MAC_IDX, dev->dev_addr);
258362306a36Sopenharmony_ci	if (offload_running(adapter))
258462306a36Sopenharmony_ci		write_smt_entry(adapter, pi->port_id);
258562306a36Sopenharmony_ci	return 0;
258662306a36Sopenharmony_ci}
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_cistatic netdev_features_t cxgb_fix_features(struct net_device *dev,
258962306a36Sopenharmony_ci	netdev_features_t features)
259062306a36Sopenharmony_ci{
259162306a36Sopenharmony_ci	/*
259262306a36Sopenharmony_ci	 * Since there is no support for separate rx/tx vlan accel
259362306a36Sopenharmony_ci	 * enable/disable make sure tx flag is always in same state as rx.
259462306a36Sopenharmony_ci	 */
259562306a36Sopenharmony_ci	if (features & NETIF_F_HW_VLAN_CTAG_RX)
259662306a36Sopenharmony_ci		features |= NETIF_F_HW_VLAN_CTAG_TX;
259762306a36Sopenharmony_ci	else
259862306a36Sopenharmony_ci		features &= ~NETIF_F_HW_VLAN_CTAG_TX;
259962306a36Sopenharmony_ci
260062306a36Sopenharmony_ci	return features;
260162306a36Sopenharmony_ci}
260262306a36Sopenharmony_ci
260362306a36Sopenharmony_cistatic int cxgb_set_features(struct net_device *dev, netdev_features_t features)
260462306a36Sopenharmony_ci{
260562306a36Sopenharmony_ci	netdev_features_t changed = dev->features ^ features;
260662306a36Sopenharmony_ci
260762306a36Sopenharmony_ci	if (changed & NETIF_F_HW_VLAN_CTAG_RX)
260862306a36Sopenharmony_ci		cxgb_vlan_mode(dev, features);
260962306a36Sopenharmony_ci
261062306a36Sopenharmony_ci	return 0;
261162306a36Sopenharmony_ci}
261262306a36Sopenharmony_ci
261362306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
261462306a36Sopenharmony_cistatic void cxgb_netpoll(struct net_device *dev)
261562306a36Sopenharmony_ci{
261662306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
261762306a36Sopenharmony_ci	struct adapter *adapter = pi->adapter;
261862306a36Sopenharmony_ci	int qidx;
261962306a36Sopenharmony_ci
262062306a36Sopenharmony_ci	for (qidx = pi->first_qset; qidx < pi->first_qset + pi->nqsets; qidx++) {
262162306a36Sopenharmony_ci		struct sge_qset *qs = &adapter->sge.qs[qidx];
262262306a36Sopenharmony_ci		void *source;
262362306a36Sopenharmony_ci
262462306a36Sopenharmony_ci		if (adapter->flags & USING_MSIX)
262562306a36Sopenharmony_ci			source = qs;
262662306a36Sopenharmony_ci		else
262762306a36Sopenharmony_ci			source = adapter;
262862306a36Sopenharmony_ci
262962306a36Sopenharmony_ci		t3_intr_handler(adapter, qs->rspq.polling) (0, source);
263062306a36Sopenharmony_ci	}
263162306a36Sopenharmony_ci}
263262306a36Sopenharmony_ci#endif
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci/*
263562306a36Sopenharmony_ci * Periodic accumulation of MAC statistics.
263662306a36Sopenharmony_ci */
263762306a36Sopenharmony_cistatic void mac_stats_update(struct adapter *adapter)
263862306a36Sopenharmony_ci{
263962306a36Sopenharmony_ci	int i;
264062306a36Sopenharmony_ci
264162306a36Sopenharmony_ci	for_each_port(adapter, i) {
264262306a36Sopenharmony_ci		struct net_device *dev = adapter->port[i];
264362306a36Sopenharmony_ci		struct port_info *p = netdev_priv(dev);
264462306a36Sopenharmony_ci
264562306a36Sopenharmony_ci		if (netif_running(dev)) {
264662306a36Sopenharmony_ci			spin_lock(&adapter->stats_lock);
264762306a36Sopenharmony_ci			t3_mac_update_stats(&p->mac);
264862306a36Sopenharmony_ci			spin_unlock(&adapter->stats_lock);
264962306a36Sopenharmony_ci		}
265062306a36Sopenharmony_ci	}
265162306a36Sopenharmony_ci}
265262306a36Sopenharmony_ci
265362306a36Sopenharmony_cistatic void check_link_status(struct adapter *adapter)
265462306a36Sopenharmony_ci{
265562306a36Sopenharmony_ci	int i;
265662306a36Sopenharmony_ci
265762306a36Sopenharmony_ci	for_each_port(adapter, i) {
265862306a36Sopenharmony_ci		struct net_device *dev = adapter->port[i];
265962306a36Sopenharmony_ci		struct port_info *p = netdev_priv(dev);
266062306a36Sopenharmony_ci		int link_fault;
266162306a36Sopenharmony_ci
266262306a36Sopenharmony_ci		spin_lock_irq(&adapter->work_lock);
266362306a36Sopenharmony_ci		link_fault = p->link_fault;
266462306a36Sopenharmony_ci		spin_unlock_irq(&adapter->work_lock);
266562306a36Sopenharmony_ci
266662306a36Sopenharmony_ci		if (link_fault) {
266762306a36Sopenharmony_ci			t3_link_fault(adapter, i);
266862306a36Sopenharmony_ci			continue;
266962306a36Sopenharmony_ci		}
267062306a36Sopenharmony_ci
267162306a36Sopenharmony_ci		if (!(p->phy.caps & SUPPORTED_IRQ) && netif_running(dev)) {
267262306a36Sopenharmony_ci			t3_xgm_intr_disable(adapter, i);
267362306a36Sopenharmony_ci			t3_read_reg(adapter, A_XGM_INT_STATUS + p->mac.offset);
267462306a36Sopenharmony_ci
267562306a36Sopenharmony_ci			t3_link_changed(adapter, i);
267662306a36Sopenharmony_ci			t3_xgm_intr_enable(adapter, i);
267762306a36Sopenharmony_ci		}
267862306a36Sopenharmony_ci	}
267962306a36Sopenharmony_ci}
268062306a36Sopenharmony_ci
268162306a36Sopenharmony_cistatic void check_t3b2_mac(struct adapter *adapter)
268262306a36Sopenharmony_ci{
268362306a36Sopenharmony_ci	int i;
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_ci	if (!rtnl_trylock())	/* synchronize with ifdown */
268662306a36Sopenharmony_ci		return;
268762306a36Sopenharmony_ci
268862306a36Sopenharmony_ci	for_each_port(adapter, i) {
268962306a36Sopenharmony_ci		struct net_device *dev = adapter->port[i];
269062306a36Sopenharmony_ci		struct port_info *p = netdev_priv(dev);
269162306a36Sopenharmony_ci		int status;
269262306a36Sopenharmony_ci
269362306a36Sopenharmony_ci		if (!netif_running(dev))
269462306a36Sopenharmony_ci			continue;
269562306a36Sopenharmony_ci
269662306a36Sopenharmony_ci		status = 0;
269762306a36Sopenharmony_ci		if (netif_running(dev) && netif_carrier_ok(dev))
269862306a36Sopenharmony_ci			status = t3b2_mac_watchdog_task(&p->mac);
269962306a36Sopenharmony_ci		if (status == 1)
270062306a36Sopenharmony_ci			p->mac.stats.num_toggled++;
270162306a36Sopenharmony_ci		else if (status == 2) {
270262306a36Sopenharmony_ci			struct cmac *mac = &p->mac;
270362306a36Sopenharmony_ci
270462306a36Sopenharmony_ci			t3_mac_set_mtu(mac, dev->mtu);
270562306a36Sopenharmony_ci			t3_mac_set_address(mac, LAN_MAC_IDX, dev->dev_addr);
270662306a36Sopenharmony_ci			cxgb_set_rxmode(dev);
270762306a36Sopenharmony_ci			t3_link_start(&p->phy, mac, &p->link_config);
270862306a36Sopenharmony_ci			t3_mac_enable(mac, MAC_DIRECTION_RX | MAC_DIRECTION_TX);
270962306a36Sopenharmony_ci			t3_port_intr_enable(adapter, p->port_id);
271062306a36Sopenharmony_ci			p->mac.stats.num_resets++;
271162306a36Sopenharmony_ci		}
271262306a36Sopenharmony_ci	}
271362306a36Sopenharmony_ci	rtnl_unlock();
271462306a36Sopenharmony_ci}
271562306a36Sopenharmony_ci
271662306a36Sopenharmony_ci
271762306a36Sopenharmony_cistatic void t3_adap_check_task(struct work_struct *work)
271862306a36Sopenharmony_ci{
271962306a36Sopenharmony_ci	struct adapter *adapter = container_of(work, struct adapter,
272062306a36Sopenharmony_ci					       adap_check_task.work);
272162306a36Sopenharmony_ci	const struct adapter_params *p = &adapter->params;
272262306a36Sopenharmony_ci	int port;
272362306a36Sopenharmony_ci	unsigned int v, status, reset;
272462306a36Sopenharmony_ci
272562306a36Sopenharmony_ci	adapter->check_task_cnt++;
272662306a36Sopenharmony_ci
272762306a36Sopenharmony_ci	check_link_status(adapter);
272862306a36Sopenharmony_ci
272962306a36Sopenharmony_ci	/* Accumulate MAC stats if needed */
273062306a36Sopenharmony_ci	if (!p->linkpoll_period ||
273162306a36Sopenharmony_ci	    (adapter->check_task_cnt * p->linkpoll_period) / 10 >=
273262306a36Sopenharmony_ci	    p->stats_update_period) {
273362306a36Sopenharmony_ci		mac_stats_update(adapter);
273462306a36Sopenharmony_ci		adapter->check_task_cnt = 0;
273562306a36Sopenharmony_ci	}
273662306a36Sopenharmony_ci
273762306a36Sopenharmony_ci	if (p->rev == T3_REV_B2)
273862306a36Sopenharmony_ci		check_t3b2_mac(adapter);
273962306a36Sopenharmony_ci
274062306a36Sopenharmony_ci	/*
274162306a36Sopenharmony_ci	 * Scan the XGMAC's to check for various conditions which we want to
274262306a36Sopenharmony_ci	 * monitor in a periodic polling manner rather than via an interrupt
274362306a36Sopenharmony_ci	 * condition.  This is used for conditions which would otherwise flood
274462306a36Sopenharmony_ci	 * the system with interrupts and we only really need to know that the
274562306a36Sopenharmony_ci	 * conditions are "happening" ...  For each condition we count the
274662306a36Sopenharmony_ci	 * detection of the condition and reset it for the next polling loop.
274762306a36Sopenharmony_ci	 */
274862306a36Sopenharmony_ci	for_each_port(adapter, port) {
274962306a36Sopenharmony_ci		struct cmac *mac =  &adap2pinfo(adapter, port)->mac;
275062306a36Sopenharmony_ci		u32 cause;
275162306a36Sopenharmony_ci
275262306a36Sopenharmony_ci		cause = t3_read_reg(adapter, A_XGM_INT_CAUSE + mac->offset);
275362306a36Sopenharmony_ci		reset = 0;
275462306a36Sopenharmony_ci		if (cause & F_RXFIFO_OVERFLOW) {
275562306a36Sopenharmony_ci			mac->stats.rx_fifo_ovfl++;
275662306a36Sopenharmony_ci			reset |= F_RXFIFO_OVERFLOW;
275762306a36Sopenharmony_ci		}
275862306a36Sopenharmony_ci
275962306a36Sopenharmony_ci		t3_write_reg(adapter, A_XGM_INT_CAUSE + mac->offset, reset);
276062306a36Sopenharmony_ci	}
276162306a36Sopenharmony_ci
276262306a36Sopenharmony_ci	/*
276362306a36Sopenharmony_ci	 * We do the same as above for FL_EMPTY interrupts.
276462306a36Sopenharmony_ci	 */
276562306a36Sopenharmony_ci	status = t3_read_reg(adapter, A_SG_INT_CAUSE);
276662306a36Sopenharmony_ci	reset = 0;
276762306a36Sopenharmony_ci
276862306a36Sopenharmony_ci	if (status & F_FLEMPTY) {
276962306a36Sopenharmony_ci		struct sge_qset *qs = &adapter->sge.qs[0];
277062306a36Sopenharmony_ci		int i = 0;
277162306a36Sopenharmony_ci
277262306a36Sopenharmony_ci		reset |= F_FLEMPTY;
277362306a36Sopenharmony_ci
277462306a36Sopenharmony_ci		v = (t3_read_reg(adapter, A_SG_RSPQ_FL_STATUS) >> S_FL0EMPTY) &
277562306a36Sopenharmony_ci		    0xffff;
277662306a36Sopenharmony_ci
277762306a36Sopenharmony_ci		while (v) {
277862306a36Sopenharmony_ci			qs->fl[i].empty += (v & 1);
277962306a36Sopenharmony_ci			if (i)
278062306a36Sopenharmony_ci				qs++;
278162306a36Sopenharmony_ci			i ^= 1;
278262306a36Sopenharmony_ci			v >>= 1;
278362306a36Sopenharmony_ci		}
278462306a36Sopenharmony_ci	}
278562306a36Sopenharmony_ci
278662306a36Sopenharmony_ci	t3_write_reg(adapter, A_SG_INT_CAUSE, reset);
278762306a36Sopenharmony_ci
278862306a36Sopenharmony_ci	/* Schedule the next check update if any port is active. */
278962306a36Sopenharmony_ci	spin_lock_irq(&adapter->work_lock);
279062306a36Sopenharmony_ci	if (adapter->open_device_map & PORT_MASK)
279162306a36Sopenharmony_ci		schedule_chk_task(adapter);
279262306a36Sopenharmony_ci	spin_unlock_irq(&adapter->work_lock);
279362306a36Sopenharmony_ci}
279462306a36Sopenharmony_ci
279562306a36Sopenharmony_cistatic void db_full_task(struct work_struct *work)
279662306a36Sopenharmony_ci{
279762306a36Sopenharmony_ci	struct adapter *adapter = container_of(work, struct adapter,
279862306a36Sopenharmony_ci					       db_full_task);
279962306a36Sopenharmony_ci
280062306a36Sopenharmony_ci	cxgb3_event_notify(&adapter->tdev, OFFLOAD_DB_FULL, 0);
280162306a36Sopenharmony_ci}
280262306a36Sopenharmony_ci
280362306a36Sopenharmony_cistatic void db_empty_task(struct work_struct *work)
280462306a36Sopenharmony_ci{
280562306a36Sopenharmony_ci	struct adapter *adapter = container_of(work, struct adapter,
280662306a36Sopenharmony_ci					       db_empty_task);
280762306a36Sopenharmony_ci
280862306a36Sopenharmony_ci	cxgb3_event_notify(&adapter->tdev, OFFLOAD_DB_EMPTY, 0);
280962306a36Sopenharmony_ci}
281062306a36Sopenharmony_ci
281162306a36Sopenharmony_cistatic void db_drop_task(struct work_struct *work)
281262306a36Sopenharmony_ci{
281362306a36Sopenharmony_ci	struct adapter *adapter = container_of(work, struct adapter,
281462306a36Sopenharmony_ci					       db_drop_task);
281562306a36Sopenharmony_ci	unsigned long delay = 1000;
281662306a36Sopenharmony_ci	unsigned short r;
281762306a36Sopenharmony_ci
281862306a36Sopenharmony_ci	cxgb3_event_notify(&adapter->tdev, OFFLOAD_DB_DROP, 0);
281962306a36Sopenharmony_ci
282062306a36Sopenharmony_ci	/*
282162306a36Sopenharmony_ci	 * Sleep a while before ringing the driver qset dbs.
282262306a36Sopenharmony_ci	 * The delay is between 1000-2023 usecs.
282362306a36Sopenharmony_ci	 */
282462306a36Sopenharmony_ci	get_random_bytes(&r, 2);
282562306a36Sopenharmony_ci	delay += r & 1023;
282662306a36Sopenharmony_ci	set_current_state(TASK_UNINTERRUPTIBLE);
282762306a36Sopenharmony_ci	schedule_timeout(usecs_to_jiffies(delay));
282862306a36Sopenharmony_ci	ring_dbs(adapter);
282962306a36Sopenharmony_ci}
283062306a36Sopenharmony_ci
283162306a36Sopenharmony_ci/*
283262306a36Sopenharmony_ci * Processes external (PHY) interrupts in process context.
283362306a36Sopenharmony_ci */
283462306a36Sopenharmony_cistatic void ext_intr_task(struct work_struct *work)
283562306a36Sopenharmony_ci{
283662306a36Sopenharmony_ci	struct adapter *adapter = container_of(work, struct adapter,
283762306a36Sopenharmony_ci					       ext_intr_handler_task);
283862306a36Sopenharmony_ci	int i;
283962306a36Sopenharmony_ci
284062306a36Sopenharmony_ci	/* Disable link fault interrupts */
284162306a36Sopenharmony_ci	for_each_port(adapter, i) {
284262306a36Sopenharmony_ci		struct net_device *dev = adapter->port[i];
284362306a36Sopenharmony_ci		struct port_info *p = netdev_priv(dev);
284462306a36Sopenharmony_ci
284562306a36Sopenharmony_ci		t3_xgm_intr_disable(adapter, i);
284662306a36Sopenharmony_ci		t3_read_reg(adapter, A_XGM_INT_STATUS + p->mac.offset);
284762306a36Sopenharmony_ci	}
284862306a36Sopenharmony_ci
284962306a36Sopenharmony_ci	/* Re-enable link fault interrupts */
285062306a36Sopenharmony_ci	t3_phy_intr_handler(adapter);
285162306a36Sopenharmony_ci
285262306a36Sopenharmony_ci	for_each_port(adapter, i)
285362306a36Sopenharmony_ci		t3_xgm_intr_enable(adapter, i);
285462306a36Sopenharmony_ci
285562306a36Sopenharmony_ci	/* Now reenable external interrupts */
285662306a36Sopenharmony_ci	spin_lock_irq(&adapter->work_lock);
285762306a36Sopenharmony_ci	if (adapter->slow_intr_mask) {
285862306a36Sopenharmony_ci		adapter->slow_intr_mask |= F_T3DBG;
285962306a36Sopenharmony_ci		t3_write_reg(adapter, A_PL_INT_CAUSE0, F_T3DBG);
286062306a36Sopenharmony_ci		t3_write_reg(adapter, A_PL_INT_ENABLE0,
286162306a36Sopenharmony_ci			     adapter->slow_intr_mask);
286262306a36Sopenharmony_ci	}
286362306a36Sopenharmony_ci	spin_unlock_irq(&adapter->work_lock);
286462306a36Sopenharmony_ci}
286562306a36Sopenharmony_ci
286662306a36Sopenharmony_ci/*
286762306a36Sopenharmony_ci * Interrupt-context handler for external (PHY) interrupts.
286862306a36Sopenharmony_ci */
286962306a36Sopenharmony_civoid t3_os_ext_intr_handler(struct adapter *adapter)
287062306a36Sopenharmony_ci{
287162306a36Sopenharmony_ci	/*
287262306a36Sopenharmony_ci	 * Schedule a task to handle external interrupts as they may be slow
287362306a36Sopenharmony_ci	 * and we use a mutex to protect MDIO registers.  We disable PHY
287462306a36Sopenharmony_ci	 * interrupts in the meantime and let the task reenable them when
287562306a36Sopenharmony_ci	 * it's done.
287662306a36Sopenharmony_ci	 */
287762306a36Sopenharmony_ci	spin_lock(&adapter->work_lock);
287862306a36Sopenharmony_ci	if (adapter->slow_intr_mask) {
287962306a36Sopenharmony_ci		adapter->slow_intr_mask &= ~F_T3DBG;
288062306a36Sopenharmony_ci		t3_write_reg(adapter, A_PL_INT_ENABLE0,
288162306a36Sopenharmony_ci			     adapter->slow_intr_mask);
288262306a36Sopenharmony_ci		queue_work(cxgb3_wq, &adapter->ext_intr_handler_task);
288362306a36Sopenharmony_ci	}
288462306a36Sopenharmony_ci	spin_unlock(&adapter->work_lock);
288562306a36Sopenharmony_ci}
288662306a36Sopenharmony_ci
288762306a36Sopenharmony_civoid t3_os_link_fault_handler(struct adapter *adapter, int port_id)
288862306a36Sopenharmony_ci{
288962306a36Sopenharmony_ci	struct net_device *netdev = adapter->port[port_id];
289062306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(netdev);
289162306a36Sopenharmony_ci
289262306a36Sopenharmony_ci	spin_lock(&adapter->work_lock);
289362306a36Sopenharmony_ci	pi->link_fault = 1;
289462306a36Sopenharmony_ci	spin_unlock(&adapter->work_lock);
289562306a36Sopenharmony_ci}
289662306a36Sopenharmony_ci
289762306a36Sopenharmony_cistatic int t3_adapter_error(struct adapter *adapter, int reset, int on_wq)
289862306a36Sopenharmony_ci{
289962306a36Sopenharmony_ci	int i, ret = 0;
290062306a36Sopenharmony_ci
290162306a36Sopenharmony_ci	if (is_offload(adapter) &&
290262306a36Sopenharmony_ci	    test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map)) {
290362306a36Sopenharmony_ci		cxgb3_event_notify(&adapter->tdev, OFFLOAD_STATUS_DOWN, 0);
290462306a36Sopenharmony_ci		offload_close(&adapter->tdev);
290562306a36Sopenharmony_ci	}
290662306a36Sopenharmony_ci
290762306a36Sopenharmony_ci	/* Stop all ports */
290862306a36Sopenharmony_ci	for_each_port(adapter, i) {
290962306a36Sopenharmony_ci		struct net_device *netdev = adapter->port[i];
291062306a36Sopenharmony_ci
291162306a36Sopenharmony_ci		if (netif_running(netdev))
291262306a36Sopenharmony_ci			__cxgb_close(netdev, on_wq);
291362306a36Sopenharmony_ci	}
291462306a36Sopenharmony_ci
291562306a36Sopenharmony_ci	/* Stop SGE timers */
291662306a36Sopenharmony_ci	t3_stop_sge_timers(adapter);
291762306a36Sopenharmony_ci
291862306a36Sopenharmony_ci	adapter->flags &= ~FULL_INIT_DONE;
291962306a36Sopenharmony_ci
292062306a36Sopenharmony_ci	if (reset)
292162306a36Sopenharmony_ci		ret = t3_reset_adapter(adapter);
292262306a36Sopenharmony_ci
292362306a36Sopenharmony_ci	pci_disable_device(adapter->pdev);
292462306a36Sopenharmony_ci
292562306a36Sopenharmony_ci	return ret;
292662306a36Sopenharmony_ci}
292762306a36Sopenharmony_ci
292862306a36Sopenharmony_cistatic int t3_reenable_adapter(struct adapter *adapter)
292962306a36Sopenharmony_ci{
293062306a36Sopenharmony_ci	if (pci_enable_device(adapter->pdev)) {
293162306a36Sopenharmony_ci		dev_err(&adapter->pdev->dev,
293262306a36Sopenharmony_ci			"Cannot re-enable PCI device after reset.\n");
293362306a36Sopenharmony_ci		goto err;
293462306a36Sopenharmony_ci	}
293562306a36Sopenharmony_ci	pci_set_master(adapter->pdev);
293662306a36Sopenharmony_ci	pci_restore_state(adapter->pdev);
293762306a36Sopenharmony_ci	pci_save_state(adapter->pdev);
293862306a36Sopenharmony_ci
293962306a36Sopenharmony_ci	/* Free sge resources */
294062306a36Sopenharmony_ci	t3_free_sge_resources(adapter);
294162306a36Sopenharmony_ci
294262306a36Sopenharmony_ci	if (t3_replay_prep_adapter(adapter))
294362306a36Sopenharmony_ci		goto err;
294462306a36Sopenharmony_ci
294562306a36Sopenharmony_ci	return 0;
294662306a36Sopenharmony_cierr:
294762306a36Sopenharmony_ci	return -1;
294862306a36Sopenharmony_ci}
294962306a36Sopenharmony_ci
295062306a36Sopenharmony_cistatic void t3_resume_ports(struct adapter *adapter)
295162306a36Sopenharmony_ci{
295262306a36Sopenharmony_ci	int i;
295362306a36Sopenharmony_ci
295462306a36Sopenharmony_ci	/* Restart the ports */
295562306a36Sopenharmony_ci	for_each_port(adapter, i) {
295662306a36Sopenharmony_ci		struct net_device *netdev = adapter->port[i];
295762306a36Sopenharmony_ci
295862306a36Sopenharmony_ci		if (netif_running(netdev)) {
295962306a36Sopenharmony_ci			if (cxgb_open(netdev)) {
296062306a36Sopenharmony_ci				dev_err(&adapter->pdev->dev,
296162306a36Sopenharmony_ci					"can't bring device back up"
296262306a36Sopenharmony_ci					" after reset\n");
296362306a36Sopenharmony_ci				continue;
296462306a36Sopenharmony_ci			}
296562306a36Sopenharmony_ci		}
296662306a36Sopenharmony_ci	}
296762306a36Sopenharmony_ci
296862306a36Sopenharmony_ci	if (is_offload(adapter) && !ofld_disable)
296962306a36Sopenharmony_ci		cxgb3_event_notify(&adapter->tdev, OFFLOAD_STATUS_UP, 0);
297062306a36Sopenharmony_ci}
297162306a36Sopenharmony_ci
297262306a36Sopenharmony_ci/*
297362306a36Sopenharmony_ci * processes a fatal error.
297462306a36Sopenharmony_ci * Bring the ports down, reset the chip, bring the ports back up.
297562306a36Sopenharmony_ci */
297662306a36Sopenharmony_cistatic void fatal_error_task(struct work_struct *work)
297762306a36Sopenharmony_ci{
297862306a36Sopenharmony_ci	struct adapter *adapter = container_of(work, struct adapter,
297962306a36Sopenharmony_ci					       fatal_error_handler_task);
298062306a36Sopenharmony_ci	int err = 0;
298162306a36Sopenharmony_ci
298262306a36Sopenharmony_ci	rtnl_lock();
298362306a36Sopenharmony_ci	err = t3_adapter_error(adapter, 1, 1);
298462306a36Sopenharmony_ci	if (!err)
298562306a36Sopenharmony_ci		err = t3_reenable_adapter(adapter);
298662306a36Sopenharmony_ci	if (!err)
298762306a36Sopenharmony_ci		t3_resume_ports(adapter);
298862306a36Sopenharmony_ci
298962306a36Sopenharmony_ci	CH_ALERT(adapter, "adapter reset %s\n", err ? "failed" : "succeeded");
299062306a36Sopenharmony_ci	rtnl_unlock();
299162306a36Sopenharmony_ci}
299262306a36Sopenharmony_ci
299362306a36Sopenharmony_civoid t3_fatal_err(struct adapter *adapter)
299462306a36Sopenharmony_ci{
299562306a36Sopenharmony_ci	unsigned int fw_status[4];
299662306a36Sopenharmony_ci
299762306a36Sopenharmony_ci	if (adapter->flags & FULL_INIT_DONE) {
299862306a36Sopenharmony_ci		t3_sge_stop_dma(adapter);
299962306a36Sopenharmony_ci		t3_write_reg(adapter, A_XGM_TX_CTRL, 0);
300062306a36Sopenharmony_ci		t3_write_reg(adapter, A_XGM_RX_CTRL, 0);
300162306a36Sopenharmony_ci		t3_write_reg(adapter, XGM_REG(A_XGM_TX_CTRL, 1), 0);
300262306a36Sopenharmony_ci		t3_write_reg(adapter, XGM_REG(A_XGM_RX_CTRL, 1), 0);
300362306a36Sopenharmony_ci
300462306a36Sopenharmony_ci		spin_lock(&adapter->work_lock);
300562306a36Sopenharmony_ci		t3_intr_disable(adapter);
300662306a36Sopenharmony_ci		queue_work(cxgb3_wq, &adapter->fatal_error_handler_task);
300762306a36Sopenharmony_ci		spin_unlock(&adapter->work_lock);
300862306a36Sopenharmony_ci	}
300962306a36Sopenharmony_ci	CH_ALERT(adapter, "encountered fatal error, operation suspended\n");
301062306a36Sopenharmony_ci	if (!t3_cim_ctl_blk_read(adapter, 0xa0, 4, fw_status))
301162306a36Sopenharmony_ci		CH_ALERT(adapter, "FW status: 0x%x, 0x%x, 0x%x, 0x%x\n",
301262306a36Sopenharmony_ci			 fw_status[0], fw_status[1],
301362306a36Sopenharmony_ci			 fw_status[2], fw_status[3]);
301462306a36Sopenharmony_ci}
301562306a36Sopenharmony_ci
301662306a36Sopenharmony_ci/**
301762306a36Sopenharmony_ci * t3_io_error_detected - called when PCI error is detected
301862306a36Sopenharmony_ci * @pdev: Pointer to PCI device
301962306a36Sopenharmony_ci * @state: The current pci connection state
302062306a36Sopenharmony_ci *
302162306a36Sopenharmony_ci * This function is called after a PCI bus error affecting
302262306a36Sopenharmony_ci * this device has been detected.
302362306a36Sopenharmony_ci */
302462306a36Sopenharmony_cistatic pci_ers_result_t t3_io_error_detected(struct pci_dev *pdev,
302562306a36Sopenharmony_ci					     pci_channel_state_t state)
302662306a36Sopenharmony_ci{
302762306a36Sopenharmony_ci	struct adapter *adapter = pci_get_drvdata(pdev);
302862306a36Sopenharmony_ci
302962306a36Sopenharmony_ci	if (state == pci_channel_io_perm_failure)
303062306a36Sopenharmony_ci		return PCI_ERS_RESULT_DISCONNECT;
303162306a36Sopenharmony_ci
303262306a36Sopenharmony_ci	t3_adapter_error(adapter, 0, 0);
303362306a36Sopenharmony_ci
303462306a36Sopenharmony_ci	/* Request a slot reset. */
303562306a36Sopenharmony_ci	return PCI_ERS_RESULT_NEED_RESET;
303662306a36Sopenharmony_ci}
303762306a36Sopenharmony_ci
303862306a36Sopenharmony_ci/**
303962306a36Sopenharmony_ci * t3_io_slot_reset - called after the pci bus has been reset.
304062306a36Sopenharmony_ci * @pdev: Pointer to PCI device
304162306a36Sopenharmony_ci *
304262306a36Sopenharmony_ci * Restart the card from scratch, as if from a cold-boot.
304362306a36Sopenharmony_ci */
304462306a36Sopenharmony_cistatic pci_ers_result_t t3_io_slot_reset(struct pci_dev *pdev)
304562306a36Sopenharmony_ci{
304662306a36Sopenharmony_ci	struct adapter *adapter = pci_get_drvdata(pdev);
304762306a36Sopenharmony_ci
304862306a36Sopenharmony_ci	if (!t3_reenable_adapter(adapter))
304962306a36Sopenharmony_ci		return PCI_ERS_RESULT_RECOVERED;
305062306a36Sopenharmony_ci
305162306a36Sopenharmony_ci	return PCI_ERS_RESULT_DISCONNECT;
305262306a36Sopenharmony_ci}
305362306a36Sopenharmony_ci
305462306a36Sopenharmony_ci/**
305562306a36Sopenharmony_ci * t3_io_resume - called when traffic can start flowing again.
305662306a36Sopenharmony_ci * @pdev: Pointer to PCI device
305762306a36Sopenharmony_ci *
305862306a36Sopenharmony_ci * This callback is called when the error recovery driver tells us that
305962306a36Sopenharmony_ci * its OK to resume normal operation.
306062306a36Sopenharmony_ci */
306162306a36Sopenharmony_cistatic void t3_io_resume(struct pci_dev *pdev)
306262306a36Sopenharmony_ci{
306362306a36Sopenharmony_ci	struct adapter *adapter = pci_get_drvdata(pdev);
306462306a36Sopenharmony_ci
306562306a36Sopenharmony_ci	CH_ALERT(adapter, "adapter recovering, PEX ERR 0x%x\n",
306662306a36Sopenharmony_ci		 t3_read_reg(adapter, A_PCIE_PEX_ERR));
306762306a36Sopenharmony_ci
306862306a36Sopenharmony_ci	rtnl_lock();
306962306a36Sopenharmony_ci	t3_resume_ports(adapter);
307062306a36Sopenharmony_ci	rtnl_unlock();
307162306a36Sopenharmony_ci}
307262306a36Sopenharmony_ci
307362306a36Sopenharmony_cistatic const struct pci_error_handlers t3_err_handler = {
307462306a36Sopenharmony_ci	.error_detected = t3_io_error_detected,
307562306a36Sopenharmony_ci	.slot_reset = t3_io_slot_reset,
307662306a36Sopenharmony_ci	.resume = t3_io_resume,
307762306a36Sopenharmony_ci};
307862306a36Sopenharmony_ci
307962306a36Sopenharmony_ci/*
308062306a36Sopenharmony_ci * Set the number of qsets based on the number of CPUs and the number of ports,
308162306a36Sopenharmony_ci * not to exceed the number of available qsets, assuming there are enough qsets
308262306a36Sopenharmony_ci * per port in HW.
308362306a36Sopenharmony_ci */
308462306a36Sopenharmony_cistatic void set_nqsets(struct adapter *adap)
308562306a36Sopenharmony_ci{
308662306a36Sopenharmony_ci	int i, j = 0;
308762306a36Sopenharmony_ci	int num_cpus = netif_get_num_default_rss_queues();
308862306a36Sopenharmony_ci	int hwports = adap->params.nports;
308962306a36Sopenharmony_ci	int nqsets = adap->msix_nvectors - 1;
309062306a36Sopenharmony_ci
309162306a36Sopenharmony_ci	if (adap->params.rev > 0 && adap->flags & USING_MSIX) {
309262306a36Sopenharmony_ci		if (hwports == 2 &&
309362306a36Sopenharmony_ci		    (hwports * nqsets > SGE_QSETS ||
309462306a36Sopenharmony_ci		     num_cpus >= nqsets / hwports))
309562306a36Sopenharmony_ci			nqsets /= hwports;
309662306a36Sopenharmony_ci		if (nqsets > num_cpus)
309762306a36Sopenharmony_ci			nqsets = num_cpus;
309862306a36Sopenharmony_ci		if (nqsets < 1 || hwports == 4)
309962306a36Sopenharmony_ci			nqsets = 1;
310062306a36Sopenharmony_ci	} else {
310162306a36Sopenharmony_ci		nqsets = 1;
310262306a36Sopenharmony_ci	}
310362306a36Sopenharmony_ci
310462306a36Sopenharmony_ci	for_each_port(adap, i) {
310562306a36Sopenharmony_ci		struct port_info *pi = adap2pinfo(adap, i);
310662306a36Sopenharmony_ci
310762306a36Sopenharmony_ci		pi->first_qset = j;
310862306a36Sopenharmony_ci		pi->nqsets = nqsets;
310962306a36Sopenharmony_ci		j = pi->first_qset + nqsets;
311062306a36Sopenharmony_ci
311162306a36Sopenharmony_ci		dev_info(&adap->pdev->dev,
311262306a36Sopenharmony_ci			 "Port %d using %d queue sets.\n", i, nqsets);
311362306a36Sopenharmony_ci	}
311462306a36Sopenharmony_ci}
311562306a36Sopenharmony_ci
311662306a36Sopenharmony_cistatic int cxgb_enable_msix(struct adapter *adap)
311762306a36Sopenharmony_ci{
311862306a36Sopenharmony_ci	struct msix_entry entries[SGE_QSETS + 1];
311962306a36Sopenharmony_ci	int vectors;
312062306a36Sopenharmony_ci	int i;
312162306a36Sopenharmony_ci
312262306a36Sopenharmony_ci	vectors = ARRAY_SIZE(entries);
312362306a36Sopenharmony_ci	for (i = 0; i < vectors; ++i)
312462306a36Sopenharmony_ci		entries[i].entry = i;
312562306a36Sopenharmony_ci
312662306a36Sopenharmony_ci	vectors = pci_enable_msix_range(adap->pdev, entries,
312762306a36Sopenharmony_ci					adap->params.nports + 1, vectors);
312862306a36Sopenharmony_ci	if (vectors < 0)
312962306a36Sopenharmony_ci		return vectors;
313062306a36Sopenharmony_ci
313162306a36Sopenharmony_ci	for (i = 0; i < vectors; ++i)
313262306a36Sopenharmony_ci		adap->msix_info[i].vec = entries[i].vector;
313362306a36Sopenharmony_ci	adap->msix_nvectors = vectors;
313462306a36Sopenharmony_ci
313562306a36Sopenharmony_ci	return 0;
313662306a36Sopenharmony_ci}
313762306a36Sopenharmony_ci
313862306a36Sopenharmony_cistatic void print_port_info(struct adapter *adap, const struct adapter_info *ai)
313962306a36Sopenharmony_ci{
314062306a36Sopenharmony_ci	static const char *pci_variant[] = {
314162306a36Sopenharmony_ci		"PCI", "PCI-X", "PCI-X ECC", "PCI-X 266", "PCI Express"
314262306a36Sopenharmony_ci	};
314362306a36Sopenharmony_ci
314462306a36Sopenharmony_ci	int i;
314562306a36Sopenharmony_ci	char buf[80];
314662306a36Sopenharmony_ci
314762306a36Sopenharmony_ci	if (is_pcie(adap))
314862306a36Sopenharmony_ci		snprintf(buf, sizeof(buf), "%s x%d",
314962306a36Sopenharmony_ci			 pci_variant[adap->params.pci.variant],
315062306a36Sopenharmony_ci			 adap->params.pci.width);
315162306a36Sopenharmony_ci	else
315262306a36Sopenharmony_ci		snprintf(buf, sizeof(buf), "%s %dMHz/%d-bit",
315362306a36Sopenharmony_ci			 pci_variant[adap->params.pci.variant],
315462306a36Sopenharmony_ci			 adap->params.pci.speed, adap->params.pci.width);
315562306a36Sopenharmony_ci
315662306a36Sopenharmony_ci	for_each_port(adap, i) {
315762306a36Sopenharmony_ci		struct net_device *dev = adap->port[i];
315862306a36Sopenharmony_ci		const struct port_info *pi = netdev_priv(dev);
315962306a36Sopenharmony_ci
316062306a36Sopenharmony_ci		if (!test_bit(i, &adap->registered_device_map))
316162306a36Sopenharmony_ci			continue;
316262306a36Sopenharmony_ci		netdev_info(dev, "%s %s %sNIC (rev %d) %s%s\n",
316362306a36Sopenharmony_ci			    ai->desc, pi->phy.desc,
316462306a36Sopenharmony_ci			    is_offload(adap) ? "R" : "", adap->params.rev, buf,
316562306a36Sopenharmony_ci			    (adap->flags & USING_MSIX) ? " MSI-X" :
316662306a36Sopenharmony_ci			    (adap->flags & USING_MSI) ? " MSI" : "");
316762306a36Sopenharmony_ci		if (adap->name == dev->name && adap->params.vpd.mclk)
316862306a36Sopenharmony_ci			pr_info("%s: %uMB CM, %uMB PMTX, %uMB PMRX, S/N: %s\n",
316962306a36Sopenharmony_ci			       adap->name, t3_mc7_size(&adap->cm) >> 20,
317062306a36Sopenharmony_ci			       t3_mc7_size(&adap->pmtx) >> 20,
317162306a36Sopenharmony_ci			       t3_mc7_size(&adap->pmrx) >> 20,
317262306a36Sopenharmony_ci			       adap->params.vpd.sn);
317362306a36Sopenharmony_ci	}
317462306a36Sopenharmony_ci}
317562306a36Sopenharmony_ci
317662306a36Sopenharmony_cistatic const struct net_device_ops cxgb_netdev_ops = {
317762306a36Sopenharmony_ci	.ndo_open		= cxgb_open,
317862306a36Sopenharmony_ci	.ndo_stop		= cxgb_close,
317962306a36Sopenharmony_ci	.ndo_start_xmit		= t3_eth_xmit,
318062306a36Sopenharmony_ci	.ndo_get_stats		= cxgb_get_stats,
318162306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
318262306a36Sopenharmony_ci	.ndo_set_rx_mode	= cxgb_set_rxmode,
318362306a36Sopenharmony_ci	.ndo_eth_ioctl		= cxgb_ioctl,
318462306a36Sopenharmony_ci	.ndo_siocdevprivate	= cxgb_siocdevprivate,
318562306a36Sopenharmony_ci	.ndo_change_mtu		= cxgb_change_mtu,
318662306a36Sopenharmony_ci	.ndo_set_mac_address	= cxgb_set_mac_addr,
318762306a36Sopenharmony_ci	.ndo_fix_features	= cxgb_fix_features,
318862306a36Sopenharmony_ci	.ndo_set_features	= cxgb_set_features,
318962306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
319062306a36Sopenharmony_ci	.ndo_poll_controller	= cxgb_netpoll,
319162306a36Sopenharmony_ci#endif
319262306a36Sopenharmony_ci};
319362306a36Sopenharmony_ci
319462306a36Sopenharmony_cistatic void cxgb3_init_iscsi_mac(struct net_device *dev)
319562306a36Sopenharmony_ci{
319662306a36Sopenharmony_ci	struct port_info *pi = netdev_priv(dev);
319762306a36Sopenharmony_ci
319862306a36Sopenharmony_ci	memcpy(pi->iscsic.mac_addr, dev->dev_addr, ETH_ALEN);
319962306a36Sopenharmony_ci	pi->iscsic.mac_addr[3] |= 0x80;
320062306a36Sopenharmony_ci}
320162306a36Sopenharmony_ci
320262306a36Sopenharmony_ci#define TSO_FLAGS (NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN)
320362306a36Sopenharmony_ci#define VLAN_FEAT (NETIF_F_SG | NETIF_F_IP_CSUM | TSO_FLAGS | \
320462306a36Sopenharmony_ci			NETIF_F_IPV6_CSUM | NETIF_F_HIGHDMA)
320562306a36Sopenharmony_cistatic int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
320662306a36Sopenharmony_ci{
320762306a36Sopenharmony_ci	int i, err;
320862306a36Sopenharmony_ci	resource_size_t mmio_start, mmio_len;
320962306a36Sopenharmony_ci	const struct adapter_info *ai;
321062306a36Sopenharmony_ci	struct adapter *adapter = NULL;
321162306a36Sopenharmony_ci	struct port_info *pi;
321262306a36Sopenharmony_ci
321362306a36Sopenharmony_ci	if (!cxgb3_wq) {
321462306a36Sopenharmony_ci		cxgb3_wq = create_singlethread_workqueue(DRV_NAME);
321562306a36Sopenharmony_ci		if (!cxgb3_wq) {
321662306a36Sopenharmony_ci			pr_err("cannot initialize work queue\n");
321762306a36Sopenharmony_ci			return -ENOMEM;
321862306a36Sopenharmony_ci		}
321962306a36Sopenharmony_ci	}
322062306a36Sopenharmony_ci
322162306a36Sopenharmony_ci	err = pci_enable_device(pdev);
322262306a36Sopenharmony_ci	if (err) {
322362306a36Sopenharmony_ci		dev_err(&pdev->dev, "cannot enable PCI device\n");
322462306a36Sopenharmony_ci		goto out;
322562306a36Sopenharmony_ci	}
322662306a36Sopenharmony_ci
322762306a36Sopenharmony_ci	err = pci_request_regions(pdev, DRV_NAME);
322862306a36Sopenharmony_ci	if (err) {
322962306a36Sopenharmony_ci		/* Just info, some other driver may have claimed the device. */
323062306a36Sopenharmony_ci		dev_info(&pdev->dev, "cannot obtain PCI resources\n");
323162306a36Sopenharmony_ci		goto out_disable_device;
323262306a36Sopenharmony_ci	}
323362306a36Sopenharmony_ci
323462306a36Sopenharmony_ci	err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
323562306a36Sopenharmony_ci	if (err) {
323662306a36Sopenharmony_ci		dev_err(&pdev->dev, "no usable DMA configuration\n");
323762306a36Sopenharmony_ci		goto out_release_regions;
323862306a36Sopenharmony_ci	}
323962306a36Sopenharmony_ci
324062306a36Sopenharmony_ci	pci_set_master(pdev);
324162306a36Sopenharmony_ci	pci_save_state(pdev);
324262306a36Sopenharmony_ci
324362306a36Sopenharmony_ci	mmio_start = pci_resource_start(pdev, 0);
324462306a36Sopenharmony_ci	mmio_len = pci_resource_len(pdev, 0);
324562306a36Sopenharmony_ci	ai = t3_get_adapter_info(ent->driver_data);
324662306a36Sopenharmony_ci
324762306a36Sopenharmony_ci	adapter = kzalloc(sizeof(*adapter), GFP_KERNEL);
324862306a36Sopenharmony_ci	if (!adapter) {
324962306a36Sopenharmony_ci		err = -ENOMEM;
325062306a36Sopenharmony_ci		goto out_release_regions;
325162306a36Sopenharmony_ci	}
325262306a36Sopenharmony_ci
325362306a36Sopenharmony_ci	adapter->nofail_skb =
325462306a36Sopenharmony_ci		alloc_skb(sizeof(struct cpl_set_tcb_field), GFP_KERNEL);
325562306a36Sopenharmony_ci	if (!adapter->nofail_skb) {
325662306a36Sopenharmony_ci		dev_err(&pdev->dev, "cannot allocate nofail buffer\n");
325762306a36Sopenharmony_ci		err = -ENOMEM;
325862306a36Sopenharmony_ci		goto out_free_adapter;
325962306a36Sopenharmony_ci	}
326062306a36Sopenharmony_ci
326162306a36Sopenharmony_ci	adapter->regs = ioremap(mmio_start, mmio_len);
326262306a36Sopenharmony_ci	if (!adapter->regs) {
326362306a36Sopenharmony_ci		dev_err(&pdev->dev, "cannot map device registers\n");
326462306a36Sopenharmony_ci		err = -ENOMEM;
326562306a36Sopenharmony_ci		goto out_free_adapter_nofail;
326662306a36Sopenharmony_ci	}
326762306a36Sopenharmony_ci
326862306a36Sopenharmony_ci	adapter->pdev = pdev;
326962306a36Sopenharmony_ci	adapter->name = pci_name(pdev);
327062306a36Sopenharmony_ci	adapter->msg_enable = dflt_msg_enable;
327162306a36Sopenharmony_ci	adapter->mmio_len = mmio_len;
327262306a36Sopenharmony_ci
327362306a36Sopenharmony_ci	mutex_init(&adapter->mdio_lock);
327462306a36Sopenharmony_ci	spin_lock_init(&adapter->work_lock);
327562306a36Sopenharmony_ci	spin_lock_init(&adapter->stats_lock);
327662306a36Sopenharmony_ci
327762306a36Sopenharmony_ci	INIT_LIST_HEAD(&adapter->adapter_list);
327862306a36Sopenharmony_ci	INIT_WORK(&adapter->ext_intr_handler_task, ext_intr_task);
327962306a36Sopenharmony_ci	INIT_WORK(&adapter->fatal_error_handler_task, fatal_error_task);
328062306a36Sopenharmony_ci
328162306a36Sopenharmony_ci	INIT_WORK(&adapter->db_full_task, db_full_task);
328262306a36Sopenharmony_ci	INIT_WORK(&adapter->db_empty_task, db_empty_task);
328362306a36Sopenharmony_ci	INIT_WORK(&adapter->db_drop_task, db_drop_task);
328462306a36Sopenharmony_ci
328562306a36Sopenharmony_ci	INIT_DELAYED_WORK(&adapter->adap_check_task, t3_adap_check_task);
328662306a36Sopenharmony_ci
328762306a36Sopenharmony_ci	for (i = 0; i < ai->nports0 + ai->nports1; ++i) {
328862306a36Sopenharmony_ci		struct net_device *netdev;
328962306a36Sopenharmony_ci
329062306a36Sopenharmony_ci		netdev = alloc_etherdev_mq(sizeof(struct port_info), SGE_QSETS);
329162306a36Sopenharmony_ci		if (!netdev) {
329262306a36Sopenharmony_ci			err = -ENOMEM;
329362306a36Sopenharmony_ci			goto out_free_dev;
329462306a36Sopenharmony_ci		}
329562306a36Sopenharmony_ci
329662306a36Sopenharmony_ci		SET_NETDEV_DEV(netdev, &pdev->dev);
329762306a36Sopenharmony_ci
329862306a36Sopenharmony_ci		adapter->port[i] = netdev;
329962306a36Sopenharmony_ci		pi = netdev_priv(netdev);
330062306a36Sopenharmony_ci		pi->adapter = adapter;
330162306a36Sopenharmony_ci		pi->port_id = i;
330262306a36Sopenharmony_ci		netif_carrier_off(netdev);
330362306a36Sopenharmony_ci		netdev->irq = pdev->irq;
330462306a36Sopenharmony_ci		netdev->mem_start = mmio_start;
330562306a36Sopenharmony_ci		netdev->mem_end = mmio_start + mmio_len - 1;
330662306a36Sopenharmony_ci		netdev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM |
330762306a36Sopenharmony_ci			NETIF_F_TSO | NETIF_F_RXCSUM | NETIF_F_HW_VLAN_CTAG_RX;
330862306a36Sopenharmony_ci		netdev->features |= netdev->hw_features |
330962306a36Sopenharmony_ci				    NETIF_F_HW_VLAN_CTAG_TX;
331062306a36Sopenharmony_ci		netdev->vlan_features |= netdev->features & VLAN_FEAT;
331162306a36Sopenharmony_ci
331262306a36Sopenharmony_ci		netdev->features |= NETIF_F_HIGHDMA;
331362306a36Sopenharmony_ci
331462306a36Sopenharmony_ci		netdev->netdev_ops = &cxgb_netdev_ops;
331562306a36Sopenharmony_ci		netdev->ethtool_ops = &cxgb_ethtool_ops;
331662306a36Sopenharmony_ci		netdev->min_mtu = 81;
331762306a36Sopenharmony_ci		netdev->max_mtu = ETH_MAX_MTU;
331862306a36Sopenharmony_ci		netdev->dev_port = pi->port_id;
331962306a36Sopenharmony_ci	}
332062306a36Sopenharmony_ci
332162306a36Sopenharmony_ci	pci_set_drvdata(pdev, adapter);
332262306a36Sopenharmony_ci	if (t3_prep_adapter(adapter, ai, 1) < 0) {
332362306a36Sopenharmony_ci		err = -ENODEV;
332462306a36Sopenharmony_ci		goto out_free_dev;
332562306a36Sopenharmony_ci	}
332662306a36Sopenharmony_ci
332762306a36Sopenharmony_ci	/*
332862306a36Sopenharmony_ci	 * The card is now ready to go.  If any errors occur during device
332962306a36Sopenharmony_ci	 * registration we do not fail the whole card but rather proceed only
333062306a36Sopenharmony_ci	 * with the ports we manage to register successfully.  However we must
333162306a36Sopenharmony_ci	 * register at least one net device.
333262306a36Sopenharmony_ci	 */
333362306a36Sopenharmony_ci	for_each_port(adapter, i) {
333462306a36Sopenharmony_ci		err = register_netdev(adapter->port[i]);
333562306a36Sopenharmony_ci		if (err)
333662306a36Sopenharmony_ci			dev_warn(&pdev->dev,
333762306a36Sopenharmony_ci				 "cannot register net device %s, skipping\n",
333862306a36Sopenharmony_ci				 adapter->port[i]->name);
333962306a36Sopenharmony_ci		else {
334062306a36Sopenharmony_ci			/*
334162306a36Sopenharmony_ci			 * Change the name we use for messages to the name of
334262306a36Sopenharmony_ci			 * the first successfully registered interface.
334362306a36Sopenharmony_ci			 */
334462306a36Sopenharmony_ci			if (!adapter->registered_device_map)
334562306a36Sopenharmony_ci				adapter->name = adapter->port[i]->name;
334662306a36Sopenharmony_ci
334762306a36Sopenharmony_ci			__set_bit(i, &adapter->registered_device_map);
334862306a36Sopenharmony_ci		}
334962306a36Sopenharmony_ci	}
335062306a36Sopenharmony_ci	if (!adapter->registered_device_map) {
335162306a36Sopenharmony_ci		dev_err(&pdev->dev, "could not register any net devices\n");
335262306a36Sopenharmony_ci		err = -ENODEV;
335362306a36Sopenharmony_ci		goto out_free_dev;
335462306a36Sopenharmony_ci	}
335562306a36Sopenharmony_ci
335662306a36Sopenharmony_ci	for_each_port(adapter, i)
335762306a36Sopenharmony_ci		cxgb3_init_iscsi_mac(adapter->port[i]);
335862306a36Sopenharmony_ci
335962306a36Sopenharmony_ci	/* Driver's ready. Reflect it on LEDs */
336062306a36Sopenharmony_ci	t3_led_ready(adapter);
336162306a36Sopenharmony_ci
336262306a36Sopenharmony_ci	if (is_offload(adapter)) {
336362306a36Sopenharmony_ci		__set_bit(OFFLOAD_DEVMAP_BIT, &adapter->registered_device_map);
336462306a36Sopenharmony_ci		cxgb3_adapter_ofld(adapter);
336562306a36Sopenharmony_ci	}
336662306a36Sopenharmony_ci
336762306a36Sopenharmony_ci	/* See what interrupts we'll be using */
336862306a36Sopenharmony_ci	if (msi > 1 && cxgb_enable_msix(adapter) == 0)
336962306a36Sopenharmony_ci		adapter->flags |= USING_MSIX;
337062306a36Sopenharmony_ci	else if (msi > 0 && pci_enable_msi(pdev) == 0)
337162306a36Sopenharmony_ci		adapter->flags |= USING_MSI;
337262306a36Sopenharmony_ci
337362306a36Sopenharmony_ci	set_nqsets(adapter);
337462306a36Sopenharmony_ci
337562306a36Sopenharmony_ci	err = sysfs_create_group(&adapter->port[0]->dev.kobj,
337662306a36Sopenharmony_ci				 &cxgb3_attr_group);
337762306a36Sopenharmony_ci	if (err) {
337862306a36Sopenharmony_ci		dev_err(&pdev->dev, "cannot create sysfs group\n");
337962306a36Sopenharmony_ci		goto out_close_led;
338062306a36Sopenharmony_ci	}
338162306a36Sopenharmony_ci
338262306a36Sopenharmony_ci	print_port_info(adapter, ai);
338362306a36Sopenharmony_ci	return 0;
338462306a36Sopenharmony_ci
338562306a36Sopenharmony_ciout_close_led:
338662306a36Sopenharmony_ci	t3_set_reg_field(adapter, A_T3DBG_GPIO_EN, F_GPIO0_OUT_VAL, 0);
338762306a36Sopenharmony_ci
338862306a36Sopenharmony_ciout_free_dev:
338962306a36Sopenharmony_ci	iounmap(adapter->regs);
339062306a36Sopenharmony_ci	for (i = ai->nports0 + ai->nports1 - 1; i >= 0; --i)
339162306a36Sopenharmony_ci		if (adapter->port[i])
339262306a36Sopenharmony_ci			free_netdev(adapter->port[i]);
339362306a36Sopenharmony_ci
339462306a36Sopenharmony_ciout_free_adapter_nofail:
339562306a36Sopenharmony_ci	kfree_skb(adapter->nofail_skb);
339662306a36Sopenharmony_ci
339762306a36Sopenharmony_ciout_free_adapter:
339862306a36Sopenharmony_ci	kfree(adapter);
339962306a36Sopenharmony_ci
340062306a36Sopenharmony_ciout_release_regions:
340162306a36Sopenharmony_ci	pci_release_regions(pdev);
340262306a36Sopenharmony_ciout_disable_device:
340362306a36Sopenharmony_ci	pci_disable_device(pdev);
340462306a36Sopenharmony_ciout:
340562306a36Sopenharmony_ci	return err;
340662306a36Sopenharmony_ci}
340762306a36Sopenharmony_ci
340862306a36Sopenharmony_cistatic void remove_one(struct pci_dev *pdev)
340962306a36Sopenharmony_ci{
341062306a36Sopenharmony_ci	struct adapter *adapter = pci_get_drvdata(pdev);
341162306a36Sopenharmony_ci
341262306a36Sopenharmony_ci	if (adapter) {
341362306a36Sopenharmony_ci		int i;
341462306a36Sopenharmony_ci
341562306a36Sopenharmony_ci		t3_sge_stop(adapter);
341662306a36Sopenharmony_ci		sysfs_remove_group(&adapter->port[0]->dev.kobj,
341762306a36Sopenharmony_ci				   &cxgb3_attr_group);
341862306a36Sopenharmony_ci
341962306a36Sopenharmony_ci		if (is_offload(adapter)) {
342062306a36Sopenharmony_ci			cxgb3_adapter_unofld(adapter);
342162306a36Sopenharmony_ci			if (test_bit(OFFLOAD_DEVMAP_BIT,
342262306a36Sopenharmony_ci				     &adapter->open_device_map))
342362306a36Sopenharmony_ci				offload_close(&adapter->tdev);
342462306a36Sopenharmony_ci		}
342562306a36Sopenharmony_ci
342662306a36Sopenharmony_ci		for_each_port(adapter, i)
342762306a36Sopenharmony_ci		    if (test_bit(i, &adapter->registered_device_map))
342862306a36Sopenharmony_ci			unregister_netdev(adapter->port[i]);
342962306a36Sopenharmony_ci
343062306a36Sopenharmony_ci		t3_stop_sge_timers(adapter);
343162306a36Sopenharmony_ci		t3_free_sge_resources(adapter);
343262306a36Sopenharmony_ci		cxgb_disable_msi(adapter);
343362306a36Sopenharmony_ci
343462306a36Sopenharmony_ci		for_each_port(adapter, i)
343562306a36Sopenharmony_ci			if (adapter->port[i])
343662306a36Sopenharmony_ci				free_netdev(adapter->port[i]);
343762306a36Sopenharmony_ci
343862306a36Sopenharmony_ci		iounmap(adapter->regs);
343962306a36Sopenharmony_ci		kfree_skb(adapter->nofail_skb);
344062306a36Sopenharmony_ci		kfree(adapter);
344162306a36Sopenharmony_ci		pci_release_regions(pdev);
344262306a36Sopenharmony_ci		pci_disable_device(pdev);
344362306a36Sopenharmony_ci	}
344462306a36Sopenharmony_ci}
344562306a36Sopenharmony_ci
344662306a36Sopenharmony_cistatic struct pci_driver driver = {
344762306a36Sopenharmony_ci	.name = DRV_NAME,
344862306a36Sopenharmony_ci	.id_table = cxgb3_pci_tbl,
344962306a36Sopenharmony_ci	.probe = init_one,
345062306a36Sopenharmony_ci	.remove = remove_one,
345162306a36Sopenharmony_ci	.err_handler = &t3_err_handler,
345262306a36Sopenharmony_ci};
345362306a36Sopenharmony_ci
345462306a36Sopenharmony_cistatic int __init cxgb3_init_module(void)
345562306a36Sopenharmony_ci{
345662306a36Sopenharmony_ci	int ret;
345762306a36Sopenharmony_ci
345862306a36Sopenharmony_ci	cxgb3_offload_init();
345962306a36Sopenharmony_ci
346062306a36Sopenharmony_ci	ret = pci_register_driver(&driver);
346162306a36Sopenharmony_ci	return ret;
346262306a36Sopenharmony_ci}
346362306a36Sopenharmony_ci
346462306a36Sopenharmony_cistatic void __exit cxgb3_cleanup_module(void)
346562306a36Sopenharmony_ci{
346662306a36Sopenharmony_ci	pci_unregister_driver(&driver);
346762306a36Sopenharmony_ci	if (cxgb3_wq)
346862306a36Sopenharmony_ci		destroy_workqueue(cxgb3_wq);
346962306a36Sopenharmony_ci}
347062306a36Sopenharmony_ci
347162306a36Sopenharmony_cimodule_init(cxgb3_init_module);
347262306a36Sopenharmony_cimodule_exit(cxgb3_cleanup_module);
3473