162306a36Sopenharmony_ci/*****************************************************************************
262306a36Sopenharmony_ci *                                                                           *
362306a36Sopenharmony_ci * File: cxgb2.c                                                             *
462306a36Sopenharmony_ci * $Revision: 1.25 $                                                         *
562306a36Sopenharmony_ci * $Date: 2005/06/22 00:43:25 $                                              *
662306a36Sopenharmony_ci * Description:                                                              *
762306a36Sopenharmony_ci *  Chelsio 10Gb Ethernet Driver.                                            *
862306a36Sopenharmony_ci *                                                                           *
962306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify      *
1062306a36Sopenharmony_ci * it under the terms of the GNU General Public License, version 2, as       *
1162306a36Sopenharmony_ci * published by the Free Software Foundation.                                *
1262306a36Sopenharmony_ci *                                                                           *
1362306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License along   *
1462306a36Sopenharmony_ci * with this program; if not, see <http://www.gnu.org/licenses/>.            *
1562306a36Sopenharmony_ci *                                                                           *
1662306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED    *
1762306a36Sopenharmony_ci * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF      *
1862306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.                     *
1962306a36Sopenharmony_ci *                                                                           *
2062306a36Sopenharmony_ci * http://www.chelsio.com                                                    *
2162306a36Sopenharmony_ci *                                                                           *
2262306a36Sopenharmony_ci * Copyright (c) 2003 - 2005 Chelsio Communications, Inc.                    *
2362306a36Sopenharmony_ci * All rights reserved.                                                      *
2462306a36Sopenharmony_ci *                                                                           *
2562306a36Sopenharmony_ci * Maintainers: maintainers@chelsio.com                                      *
2662306a36Sopenharmony_ci *                                                                           *
2762306a36Sopenharmony_ci * Authors: Dimitrios Michailidis   <dm@chelsio.com>                         *
2862306a36Sopenharmony_ci *          Tina Yang               <tainay@chelsio.com>                     *
2962306a36Sopenharmony_ci *          Felix Marti             <felix@chelsio.com>                      *
3062306a36Sopenharmony_ci *          Scott Bardone           <sbardone@chelsio.com>                   *
3162306a36Sopenharmony_ci *          Kurt Ottaway            <kottaway@chelsio.com>                   *
3262306a36Sopenharmony_ci *          Frank DiMambro          <frank@chelsio.com>                      *
3362306a36Sopenharmony_ci *                                                                           *
3462306a36Sopenharmony_ci * History:                                                                  *
3562306a36Sopenharmony_ci *                                                                           *
3662306a36Sopenharmony_ci ****************************************************************************/
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#include "common.h"
3962306a36Sopenharmony_ci#include <linux/module.h>
4062306a36Sopenharmony_ci#include <linux/pci.h>
4162306a36Sopenharmony_ci#include <linux/netdevice.h>
4262306a36Sopenharmony_ci#include <linux/etherdevice.h>
4362306a36Sopenharmony_ci#include <linux/if_vlan.h>
4462306a36Sopenharmony_ci#include <linux/mii.h>
4562306a36Sopenharmony_ci#include <linux/sockios.h>
4662306a36Sopenharmony_ci#include <linux/dma-mapping.h>
4762306a36Sopenharmony_ci#include <linux/uaccess.h>
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#include "cpl5_cmd.h"
5062306a36Sopenharmony_ci#include "regs.h"
5162306a36Sopenharmony_ci#include "gmac.h"
5262306a36Sopenharmony_ci#include "cphy.h"
5362306a36Sopenharmony_ci#include "sge.h"
5462306a36Sopenharmony_ci#include "tp.h"
5562306a36Sopenharmony_ci#include "espi.h"
5662306a36Sopenharmony_ci#include "elmer0.h"
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#include <linux/workqueue.h>
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic inline void schedule_mac_stats_update(struct adapter *ap, int secs)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	schedule_delayed_work(&ap->stats_update_task, secs * HZ);
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic inline void cancel_mac_stats_update(struct adapter *ap)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	cancel_delayed_work(&ap->stats_update_task);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define MAX_CMDQ_ENTRIES	16384
7162306a36Sopenharmony_ci#define MAX_CMDQ1_ENTRIES	1024
7262306a36Sopenharmony_ci#define MAX_RX_BUFFERS		16384
7362306a36Sopenharmony_ci#define MAX_RX_JUMBO_BUFFERS	16384
7462306a36Sopenharmony_ci#define MAX_TX_BUFFERS_HIGH	16384U
7562306a36Sopenharmony_ci#define MAX_TX_BUFFERS_LOW	1536U
7662306a36Sopenharmony_ci#define MAX_TX_BUFFERS		1460U
7762306a36Sopenharmony_ci#define MIN_FL_ENTRIES		32
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/*
8462306a36Sopenharmony_ci * The EEPROM is actually bigger but only the first few bytes are used so we
8562306a36Sopenharmony_ci * only report those.
8662306a36Sopenharmony_ci */
8762306a36Sopenharmony_ci#define EEPROM_SIZE 32
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESCRIPTION);
9062306a36Sopenharmony_ciMODULE_AUTHOR("Chelsio Communications");
9162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int dflt_msg_enable = DFLT_MSG_ENABLE;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cimodule_param(dflt_msg_enable, int, 0);
9662306a36Sopenharmony_ciMODULE_PARM_DESC(dflt_msg_enable, "Chelsio T1 default message enable bitmap");
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci#define HCLOCK 0x0
9962306a36Sopenharmony_ci#define LCLOCK 0x1
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci/* T1 cards powersave mode */
10262306a36Sopenharmony_cistatic int t1_clock(struct adapter *adapter, int mode);
10362306a36Sopenharmony_cistatic int t1powersave = 1;	/* HW default is powersave mode. */
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cimodule_param(t1powersave, int, 0);
10662306a36Sopenharmony_ciMODULE_PARM_DESC(t1powersave, "Enable/Disable T1 powersaving mode");
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic int disable_msi = 0;
10962306a36Sopenharmony_cimodule_param(disable_msi, int, 0);
11062306a36Sopenharmony_ciMODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)");
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/*
11362306a36Sopenharmony_ci * Setup MAC to receive the types of packets we want.
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_cistatic void t1_set_rxmode(struct net_device *dev)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
11862306a36Sopenharmony_ci	struct cmac *mac = adapter->port[dev->if_port].mac;
11962306a36Sopenharmony_ci	struct t1_rx_mode rm;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	rm.dev = dev;
12262306a36Sopenharmony_ci	mac->ops->set_rx_mode(mac, &rm);
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic void link_report(struct port_info *p)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	if (!netif_carrier_ok(p->dev))
12862306a36Sopenharmony_ci		netdev_info(p->dev, "link down\n");
12962306a36Sopenharmony_ci	else {
13062306a36Sopenharmony_ci		const char *s = "10Mbps";
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci		switch (p->link_config.speed) {
13362306a36Sopenharmony_ci			case SPEED_10000: s = "10Gbps"; break;
13462306a36Sopenharmony_ci			case SPEED_1000:  s = "1000Mbps"; break;
13562306a36Sopenharmony_ci			case SPEED_100:   s = "100Mbps"; break;
13662306a36Sopenharmony_ci		}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci		netdev_info(p->dev, "link up, %s, %s-duplex\n",
13962306a36Sopenharmony_ci			    s, p->link_config.duplex == DUPLEX_FULL
14062306a36Sopenharmony_ci			    ? "full" : "half");
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_civoid t1_link_negotiated(struct adapter *adapter, int port_id, int link_stat,
14562306a36Sopenharmony_ci			int speed, int duplex, int pause)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	struct port_info *p = &adapter->port[port_id];
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	if (link_stat != netif_carrier_ok(p->dev)) {
15062306a36Sopenharmony_ci		if (link_stat)
15162306a36Sopenharmony_ci			netif_carrier_on(p->dev);
15262306a36Sopenharmony_ci		else
15362306a36Sopenharmony_ci			netif_carrier_off(p->dev);
15462306a36Sopenharmony_ci		link_report(p);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci		/* multi-ports: inform toe */
15762306a36Sopenharmony_ci		if ((speed > 0) && (adapter->params.nports > 1)) {
15862306a36Sopenharmony_ci			unsigned int sched_speed = 10;
15962306a36Sopenharmony_ci			switch (speed) {
16062306a36Sopenharmony_ci			case SPEED_1000:
16162306a36Sopenharmony_ci				sched_speed = 1000;
16262306a36Sopenharmony_ci				break;
16362306a36Sopenharmony_ci			case SPEED_100:
16462306a36Sopenharmony_ci				sched_speed = 100;
16562306a36Sopenharmony_ci				break;
16662306a36Sopenharmony_ci			case SPEED_10:
16762306a36Sopenharmony_ci				sched_speed = 10;
16862306a36Sopenharmony_ci				break;
16962306a36Sopenharmony_ci			}
17062306a36Sopenharmony_ci			t1_sched_update_parms(adapter->sge, port_id, 0, sched_speed);
17162306a36Sopenharmony_ci		}
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic void link_start(struct port_info *p)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct cmac *mac = p->mac;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	mac->ops->reset(mac);
18062306a36Sopenharmony_ci	if (mac->ops->macaddress_set)
18162306a36Sopenharmony_ci		mac->ops->macaddress_set(mac, p->dev->dev_addr);
18262306a36Sopenharmony_ci	t1_set_rxmode(p->dev);
18362306a36Sopenharmony_ci	t1_link_start(p->phy, mac, &p->link_config);
18462306a36Sopenharmony_ci	mac->ops->enable(mac, MAC_DIRECTION_RX | MAC_DIRECTION_TX);
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic void enable_hw_csum(struct adapter *adapter)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	if (adapter->port[0].dev->hw_features & NETIF_F_TSO)
19062306a36Sopenharmony_ci		t1_tp_set_ip_checksum_offload(adapter->tp, 1);	/* for TSO only */
19162306a36Sopenharmony_ci	t1_tp_set_tcp_checksum_offload(adapter->tp, 1);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci/*
19562306a36Sopenharmony_ci * Things to do upon first use of a card.
19662306a36Sopenharmony_ci * This must run with the rtnl lock held.
19762306a36Sopenharmony_ci */
19862306a36Sopenharmony_cistatic int cxgb_up(struct adapter *adapter)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	int err = 0;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (!(adapter->flags & FULL_INIT_DONE)) {
20362306a36Sopenharmony_ci		err = t1_init_hw_modules(adapter);
20462306a36Sopenharmony_ci		if (err)
20562306a36Sopenharmony_ci			goto out_err;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		enable_hw_csum(adapter);
20862306a36Sopenharmony_ci		adapter->flags |= FULL_INIT_DONE;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	t1_interrupts_clear(adapter);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	adapter->params.has_msi = !disable_msi && !pci_enable_msi(adapter->pdev);
21462306a36Sopenharmony_ci	err = request_threaded_irq(adapter->pdev->irq, t1_interrupt,
21562306a36Sopenharmony_ci				   t1_interrupt_thread,
21662306a36Sopenharmony_ci				   adapter->params.has_msi ? 0 : IRQF_SHARED,
21762306a36Sopenharmony_ci				   adapter->name, adapter);
21862306a36Sopenharmony_ci	if (err) {
21962306a36Sopenharmony_ci		if (adapter->params.has_msi)
22062306a36Sopenharmony_ci			pci_disable_msi(adapter->pdev);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		goto out_err;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	t1_sge_start(adapter->sge);
22662306a36Sopenharmony_ci	t1_interrupts_enable(adapter);
22762306a36Sopenharmony_ciout_err:
22862306a36Sopenharmony_ci	return err;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci/*
23262306a36Sopenharmony_ci * Release resources when all the ports have been stopped.
23362306a36Sopenharmony_ci */
23462306a36Sopenharmony_cistatic void cxgb_down(struct adapter *adapter)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	t1_sge_stop(adapter->sge);
23762306a36Sopenharmony_ci	t1_interrupts_disable(adapter);
23862306a36Sopenharmony_ci	free_irq(adapter->pdev->irq, adapter);
23962306a36Sopenharmony_ci	if (adapter->params.has_msi)
24062306a36Sopenharmony_ci		pci_disable_msi(adapter->pdev);
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic int cxgb_open(struct net_device *dev)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	int err;
24662306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
24762306a36Sopenharmony_ci	int other_ports = adapter->open_device_map & PORT_MASK;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	napi_enable(&adapter->napi);
25062306a36Sopenharmony_ci	if (!adapter->open_device_map && (err = cxgb_up(adapter)) < 0) {
25162306a36Sopenharmony_ci		napi_disable(&adapter->napi);
25262306a36Sopenharmony_ci		return err;
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	__set_bit(dev->if_port, &adapter->open_device_map);
25662306a36Sopenharmony_ci	link_start(&adapter->port[dev->if_port]);
25762306a36Sopenharmony_ci	netif_start_queue(dev);
25862306a36Sopenharmony_ci	if (!other_ports && adapter->params.stats_update_period)
25962306a36Sopenharmony_ci		schedule_mac_stats_update(adapter,
26062306a36Sopenharmony_ci					  adapter->params.stats_update_period);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	t1_vlan_mode(adapter, dev->features);
26362306a36Sopenharmony_ci	return 0;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic int cxgb_close(struct net_device *dev)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
26962306a36Sopenharmony_ci	struct port_info *p = &adapter->port[dev->if_port];
27062306a36Sopenharmony_ci	struct cmac *mac = p->mac;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	netif_stop_queue(dev);
27362306a36Sopenharmony_ci	napi_disable(&adapter->napi);
27462306a36Sopenharmony_ci	mac->ops->disable(mac, MAC_DIRECTION_TX | MAC_DIRECTION_RX);
27562306a36Sopenharmony_ci	netif_carrier_off(dev);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	clear_bit(dev->if_port, &adapter->open_device_map);
27862306a36Sopenharmony_ci	if (adapter->params.stats_update_period &&
27962306a36Sopenharmony_ci	    !(adapter->open_device_map & PORT_MASK)) {
28062306a36Sopenharmony_ci		/* Stop statistics accumulation. */
28162306a36Sopenharmony_ci		smp_mb__after_atomic();
28262306a36Sopenharmony_ci		spin_lock(&adapter->work_lock);   /* sync with update task */
28362306a36Sopenharmony_ci		spin_unlock(&adapter->work_lock);
28462306a36Sopenharmony_ci		cancel_mac_stats_update(adapter);
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	if (!adapter->open_device_map)
28862306a36Sopenharmony_ci		cxgb_down(adapter);
28962306a36Sopenharmony_ci	return 0;
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic struct net_device_stats *t1_get_stats(struct net_device *dev)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
29562306a36Sopenharmony_ci	struct port_info *p = &adapter->port[dev->if_port];
29662306a36Sopenharmony_ci	struct net_device_stats *ns = &dev->stats;
29762306a36Sopenharmony_ci	const struct cmac_statistics *pstats;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	/* Do a full update of the MAC stats */
30062306a36Sopenharmony_ci	pstats = p->mac->ops->statistics_update(p->mac,
30162306a36Sopenharmony_ci						MAC_STATS_UPDATE_FULL);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	ns->tx_packets = pstats->TxUnicastFramesOK +
30462306a36Sopenharmony_ci		pstats->TxMulticastFramesOK + pstats->TxBroadcastFramesOK;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	ns->rx_packets = pstats->RxUnicastFramesOK +
30762306a36Sopenharmony_ci		pstats->RxMulticastFramesOK + pstats->RxBroadcastFramesOK;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	ns->tx_bytes = pstats->TxOctetsOK;
31062306a36Sopenharmony_ci	ns->rx_bytes = pstats->RxOctetsOK;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	ns->tx_errors = pstats->TxLateCollisions + pstats->TxLengthErrors +
31362306a36Sopenharmony_ci		pstats->TxUnderrun + pstats->TxFramesAbortedDueToXSCollisions;
31462306a36Sopenharmony_ci	ns->rx_errors = pstats->RxDataErrors + pstats->RxJabberErrors +
31562306a36Sopenharmony_ci		pstats->RxFCSErrors + pstats->RxAlignErrors +
31662306a36Sopenharmony_ci		pstats->RxSequenceErrors + pstats->RxFrameTooLongErrors +
31762306a36Sopenharmony_ci		pstats->RxSymbolErrors + pstats->RxRuntErrors;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	ns->multicast  = pstats->RxMulticastFramesOK;
32062306a36Sopenharmony_ci	ns->collisions = pstats->TxTotalCollisions;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	/* detailed rx_errors */
32362306a36Sopenharmony_ci	ns->rx_length_errors = pstats->RxFrameTooLongErrors +
32462306a36Sopenharmony_ci		pstats->RxJabberErrors;
32562306a36Sopenharmony_ci	ns->rx_over_errors   = 0;
32662306a36Sopenharmony_ci	ns->rx_crc_errors    = pstats->RxFCSErrors;
32762306a36Sopenharmony_ci	ns->rx_frame_errors  = pstats->RxAlignErrors;
32862306a36Sopenharmony_ci	ns->rx_fifo_errors   = 0;
32962306a36Sopenharmony_ci	ns->rx_missed_errors = 0;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	/* detailed tx_errors */
33262306a36Sopenharmony_ci	ns->tx_aborted_errors   = pstats->TxFramesAbortedDueToXSCollisions;
33362306a36Sopenharmony_ci	ns->tx_carrier_errors   = 0;
33462306a36Sopenharmony_ci	ns->tx_fifo_errors      = pstats->TxUnderrun;
33562306a36Sopenharmony_ci	ns->tx_heartbeat_errors = 0;
33662306a36Sopenharmony_ci	ns->tx_window_errors    = pstats->TxLateCollisions;
33762306a36Sopenharmony_ci	return ns;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic u32 get_msglevel(struct net_device *dev)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	return adapter->msg_enable;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic void set_msglevel(struct net_device *dev, u32 val)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	adapter->msg_enable = val;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic const char stats_strings[][ETH_GSTRING_LEN] = {
35562306a36Sopenharmony_ci	"TxOctetsOK",
35662306a36Sopenharmony_ci	"TxOctetsBad",
35762306a36Sopenharmony_ci	"TxUnicastFramesOK",
35862306a36Sopenharmony_ci	"TxMulticastFramesOK",
35962306a36Sopenharmony_ci	"TxBroadcastFramesOK",
36062306a36Sopenharmony_ci	"TxPauseFrames",
36162306a36Sopenharmony_ci	"TxFramesWithDeferredXmissions",
36262306a36Sopenharmony_ci	"TxLateCollisions",
36362306a36Sopenharmony_ci	"TxTotalCollisions",
36462306a36Sopenharmony_ci	"TxFramesAbortedDueToXSCollisions",
36562306a36Sopenharmony_ci	"TxUnderrun",
36662306a36Sopenharmony_ci	"TxLengthErrors",
36762306a36Sopenharmony_ci	"TxInternalMACXmitError",
36862306a36Sopenharmony_ci	"TxFramesWithExcessiveDeferral",
36962306a36Sopenharmony_ci	"TxFCSErrors",
37062306a36Sopenharmony_ci	"TxJumboFramesOk",
37162306a36Sopenharmony_ci	"TxJumboOctetsOk",
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	"RxOctetsOK",
37462306a36Sopenharmony_ci	"RxOctetsBad",
37562306a36Sopenharmony_ci	"RxUnicastFramesOK",
37662306a36Sopenharmony_ci	"RxMulticastFramesOK",
37762306a36Sopenharmony_ci	"RxBroadcastFramesOK",
37862306a36Sopenharmony_ci	"RxPauseFrames",
37962306a36Sopenharmony_ci	"RxFCSErrors",
38062306a36Sopenharmony_ci	"RxAlignErrors",
38162306a36Sopenharmony_ci	"RxSymbolErrors",
38262306a36Sopenharmony_ci	"RxDataErrors",
38362306a36Sopenharmony_ci	"RxSequenceErrors",
38462306a36Sopenharmony_ci	"RxRuntErrors",
38562306a36Sopenharmony_ci	"RxJabberErrors",
38662306a36Sopenharmony_ci	"RxInternalMACRcvError",
38762306a36Sopenharmony_ci	"RxInRangeLengthErrors",
38862306a36Sopenharmony_ci	"RxOutOfRangeLengthField",
38962306a36Sopenharmony_ci	"RxFrameTooLongErrors",
39062306a36Sopenharmony_ci	"RxJumboFramesOk",
39162306a36Sopenharmony_ci	"RxJumboOctetsOk",
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	/* Port stats */
39462306a36Sopenharmony_ci	"RxCsumGood",
39562306a36Sopenharmony_ci	"TxCsumOffload",
39662306a36Sopenharmony_ci	"TxTso",
39762306a36Sopenharmony_ci	"RxVlan",
39862306a36Sopenharmony_ci	"TxVlan",
39962306a36Sopenharmony_ci	"TxNeedHeadroom",
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	/* Interrupt stats */
40262306a36Sopenharmony_ci	"rx drops",
40362306a36Sopenharmony_ci	"pure_rsps",
40462306a36Sopenharmony_ci	"unhandled irqs",
40562306a36Sopenharmony_ci	"respQ_empty",
40662306a36Sopenharmony_ci	"respQ_overflow",
40762306a36Sopenharmony_ci	"freelistQ_empty",
40862306a36Sopenharmony_ci	"pkt_too_big",
40962306a36Sopenharmony_ci	"pkt_mismatch",
41062306a36Sopenharmony_ci	"cmdQ_full0",
41162306a36Sopenharmony_ci	"cmdQ_full1",
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	"espi_DIP2ParityErr",
41462306a36Sopenharmony_ci	"espi_DIP4Err",
41562306a36Sopenharmony_ci	"espi_RxDrops",
41662306a36Sopenharmony_ci	"espi_TxDrops",
41762306a36Sopenharmony_ci	"espi_RxOvfl",
41862306a36Sopenharmony_ci	"espi_ParityErr"
41962306a36Sopenharmony_ci};
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci#define T2_REGMAP_SIZE (3 * 1024)
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic int get_regs_len(struct net_device *dev)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	return T2_REGMAP_SIZE;
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	strscpy(info->driver, DRV_NAME, sizeof(info->driver));
43362306a36Sopenharmony_ci	strscpy(info->bus_info, pci_name(adapter->pdev),
43462306a36Sopenharmony_ci		sizeof(info->bus_info));
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic int get_sset_count(struct net_device *dev, int sset)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	switch (sset) {
44062306a36Sopenharmony_ci	case ETH_SS_STATS:
44162306a36Sopenharmony_ci		return ARRAY_SIZE(stats_strings);
44262306a36Sopenharmony_ci	default:
44362306a36Sopenharmony_ci		return -EOPNOTSUPP;
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic void get_strings(struct net_device *dev, u32 stringset, u8 *data)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	if (stringset == ETH_SS_STATS)
45062306a36Sopenharmony_ci		memcpy(data, stats_strings, sizeof(stats_strings));
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_cistatic void get_stats(struct net_device *dev, struct ethtool_stats *stats,
45462306a36Sopenharmony_ci		      u64 *data)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
45762306a36Sopenharmony_ci	struct cmac *mac = adapter->port[dev->if_port].mac;
45862306a36Sopenharmony_ci	const struct cmac_statistics *s;
45962306a36Sopenharmony_ci	const struct sge_intr_counts *t;
46062306a36Sopenharmony_ci	struct sge_port_stats ss;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	s = mac->ops->statistics_update(mac, MAC_STATS_UPDATE_FULL);
46362306a36Sopenharmony_ci	t = t1_sge_get_intr_counts(adapter->sge);
46462306a36Sopenharmony_ci	t1_sge_get_port_stats(adapter->sge, dev->if_port, &ss);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	*data++ = s->TxOctetsOK;
46762306a36Sopenharmony_ci	*data++ = s->TxOctetsBad;
46862306a36Sopenharmony_ci	*data++ = s->TxUnicastFramesOK;
46962306a36Sopenharmony_ci	*data++ = s->TxMulticastFramesOK;
47062306a36Sopenharmony_ci	*data++ = s->TxBroadcastFramesOK;
47162306a36Sopenharmony_ci	*data++ = s->TxPauseFrames;
47262306a36Sopenharmony_ci	*data++ = s->TxFramesWithDeferredXmissions;
47362306a36Sopenharmony_ci	*data++ = s->TxLateCollisions;
47462306a36Sopenharmony_ci	*data++ = s->TxTotalCollisions;
47562306a36Sopenharmony_ci	*data++ = s->TxFramesAbortedDueToXSCollisions;
47662306a36Sopenharmony_ci	*data++ = s->TxUnderrun;
47762306a36Sopenharmony_ci	*data++ = s->TxLengthErrors;
47862306a36Sopenharmony_ci	*data++ = s->TxInternalMACXmitError;
47962306a36Sopenharmony_ci	*data++ = s->TxFramesWithExcessiveDeferral;
48062306a36Sopenharmony_ci	*data++ = s->TxFCSErrors;
48162306a36Sopenharmony_ci	*data++ = s->TxJumboFramesOK;
48262306a36Sopenharmony_ci	*data++ = s->TxJumboOctetsOK;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	*data++ = s->RxOctetsOK;
48562306a36Sopenharmony_ci	*data++ = s->RxOctetsBad;
48662306a36Sopenharmony_ci	*data++ = s->RxUnicastFramesOK;
48762306a36Sopenharmony_ci	*data++ = s->RxMulticastFramesOK;
48862306a36Sopenharmony_ci	*data++ = s->RxBroadcastFramesOK;
48962306a36Sopenharmony_ci	*data++ = s->RxPauseFrames;
49062306a36Sopenharmony_ci	*data++ = s->RxFCSErrors;
49162306a36Sopenharmony_ci	*data++ = s->RxAlignErrors;
49262306a36Sopenharmony_ci	*data++ = s->RxSymbolErrors;
49362306a36Sopenharmony_ci	*data++ = s->RxDataErrors;
49462306a36Sopenharmony_ci	*data++ = s->RxSequenceErrors;
49562306a36Sopenharmony_ci	*data++ = s->RxRuntErrors;
49662306a36Sopenharmony_ci	*data++ = s->RxJabberErrors;
49762306a36Sopenharmony_ci	*data++ = s->RxInternalMACRcvError;
49862306a36Sopenharmony_ci	*data++ = s->RxInRangeLengthErrors;
49962306a36Sopenharmony_ci	*data++ = s->RxOutOfRangeLengthField;
50062306a36Sopenharmony_ci	*data++ = s->RxFrameTooLongErrors;
50162306a36Sopenharmony_ci	*data++ = s->RxJumboFramesOK;
50262306a36Sopenharmony_ci	*data++ = s->RxJumboOctetsOK;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	*data++ = ss.rx_cso_good;
50562306a36Sopenharmony_ci	*data++ = ss.tx_cso;
50662306a36Sopenharmony_ci	*data++ = ss.tx_tso;
50762306a36Sopenharmony_ci	*data++ = ss.vlan_xtract;
50862306a36Sopenharmony_ci	*data++ = ss.vlan_insert;
50962306a36Sopenharmony_ci	*data++ = ss.tx_need_hdrroom;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	*data++ = t->rx_drops;
51262306a36Sopenharmony_ci	*data++ = t->pure_rsps;
51362306a36Sopenharmony_ci	*data++ = t->unhandled_irqs;
51462306a36Sopenharmony_ci	*data++ = t->respQ_empty;
51562306a36Sopenharmony_ci	*data++ = t->respQ_overflow;
51662306a36Sopenharmony_ci	*data++ = t->freelistQ_empty;
51762306a36Sopenharmony_ci	*data++ = t->pkt_too_big;
51862306a36Sopenharmony_ci	*data++ = t->pkt_mismatch;
51962306a36Sopenharmony_ci	*data++ = t->cmdQ_full[0];
52062306a36Sopenharmony_ci	*data++ = t->cmdQ_full[1];
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (adapter->espi) {
52362306a36Sopenharmony_ci		const struct espi_intr_counts *e;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci		e = t1_espi_get_intr_counts(adapter->espi);
52662306a36Sopenharmony_ci		*data++ = e->DIP2_parity_err;
52762306a36Sopenharmony_ci		*data++ = e->DIP4_err;
52862306a36Sopenharmony_ci		*data++ = e->rx_drops;
52962306a36Sopenharmony_ci		*data++ = e->tx_drops;
53062306a36Sopenharmony_ci		*data++ = e->rx_ovflw;
53162306a36Sopenharmony_ci		*data++ = e->parity_err;
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic inline void reg_block_dump(struct adapter *ap, void *buf,
53662306a36Sopenharmony_ci				  unsigned int start, unsigned int end)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	u32 *p = buf + start;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	for ( ; start <= end; start += sizeof(u32))
54162306a36Sopenharmony_ci		*p++ = readl(ap->regs + start);
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_cistatic void get_regs(struct net_device *dev, struct ethtool_regs *regs,
54562306a36Sopenharmony_ci		     void *buf)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	struct adapter *ap = dev->ml_priv;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	/*
55062306a36Sopenharmony_ci	 * Version scheme: bits 0..9: chip version, bits 10..15: chip revision
55162306a36Sopenharmony_ci	 */
55262306a36Sopenharmony_ci	regs->version = 2;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	memset(buf, 0, T2_REGMAP_SIZE);
55562306a36Sopenharmony_ci	reg_block_dump(ap, buf, 0, A_SG_RESPACCUTIMER);
55662306a36Sopenharmony_ci	reg_block_dump(ap, buf, A_MC3_CFG, A_MC4_INT_CAUSE);
55762306a36Sopenharmony_ci	reg_block_dump(ap, buf, A_TPI_ADDR, A_TPI_PAR);
55862306a36Sopenharmony_ci	reg_block_dump(ap, buf, A_TP_IN_CONFIG, A_TP_TX_DROP_COUNT);
55962306a36Sopenharmony_ci	reg_block_dump(ap, buf, A_RAT_ROUTE_CONTROL, A_RAT_INTR_CAUSE);
56062306a36Sopenharmony_ci	reg_block_dump(ap, buf, A_CSPI_RX_AE_WM, A_CSPI_INTR_ENABLE);
56162306a36Sopenharmony_ci	reg_block_dump(ap, buf, A_ESPI_SCH_TOKEN0, A_ESPI_GOSTAT);
56262306a36Sopenharmony_ci	reg_block_dump(ap, buf, A_ULP_ULIMIT, A_ULP_PIO_CTRL);
56362306a36Sopenharmony_ci	reg_block_dump(ap, buf, A_PL_ENABLE, A_PL_CAUSE);
56462306a36Sopenharmony_ci	reg_block_dump(ap, buf, A_MC5_CONFIG, A_MC5_MASK_WRITE_CMD);
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cistatic int get_link_ksettings(struct net_device *dev,
56862306a36Sopenharmony_ci			      struct ethtool_link_ksettings *cmd)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
57162306a36Sopenharmony_ci	struct port_info *p = &adapter->port[dev->if_port];
57262306a36Sopenharmony_ci	u32 supported, advertising;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	supported = p->link_config.supported;
57562306a36Sopenharmony_ci	advertising = p->link_config.advertising;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	if (netif_carrier_ok(dev)) {
57862306a36Sopenharmony_ci		cmd->base.speed = p->link_config.speed;
57962306a36Sopenharmony_ci		cmd->base.duplex = p->link_config.duplex;
58062306a36Sopenharmony_ci	} else {
58162306a36Sopenharmony_ci		cmd->base.speed = SPEED_UNKNOWN;
58262306a36Sopenharmony_ci		cmd->base.duplex = DUPLEX_UNKNOWN;
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	cmd->base.port = (supported & SUPPORTED_TP) ? PORT_TP : PORT_FIBRE;
58662306a36Sopenharmony_ci	cmd->base.phy_address = p->phy->mdio.prtad;
58762306a36Sopenharmony_ci	cmd->base.autoneg = p->link_config.autoneg;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
59062306a36Sopenharmony_ci						supported);
59162306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
59262306a36Sopenharmony_ci						advertising);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	return 0;
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_cistatic int speed_duplex_to_caps(int speed, int duplex)
59862306a36Sopenharmony_ci{
59962306a36Sopenharmony_ci	int cap = 0;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	switch (speed) {
60262306a36Sopenharmony_ci	case SPEED_10:
60362306a36Sopenharmony_ci		if (duplex == DUPLEX_FULL)
60462306a36Sopenharmony_ci			cap = SUPPORTED_10baseT_Full;
60562306a36Sopenharmony_ci		else
60662306a36Sopenharmony_ci			cap = SUPPORTED_10baseT_Half;
60762306a36Sopenharmony_ci		break;
60862306a36Sopenharmony_ci	case SPEED_100:
60962306a36Sopenharmony_ci		if (duplex == DUPLEX_FULL)
61062306a36Sopenharmony_ci			cap = SUPPORTED_100baseT_Full;
61162306a36Sopenharmony_ci		else
61262306a36Sopenharmony_ci			cap = SUPPORTED_100baseT_Half;
61362306a36Sopenharmony_ci		break;
61462306a36Sopenharmony_ci	case SPEED_1000:
61562306a36Sopenharmony_ci		if (duplex == DUPLEX_FULL)
61662306a36Sopenharmony_ci			cap = SUPPORTED_1000baseT_Full;
61762306a36Sopenharmony_ci		else
61862306a36Sopenharmony_ci			cap = SUPPORTED_1000baseT_Half;
61962306a36Sopenharmony_ci		break;
62062306a36Sopenharmony_ci	case SPEED_10000:
62162306a36Sopenharmony_ci		if (duplex == DUPLEX_FULL)
62262306a36Sopenharmony_ci			cap = SUPPORTED_10000baseT_Full;
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci	return cap;
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci#define ADVERTISED_MASK (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | \
62862306a36Sopenharmony_ci		      ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | \
62962306a36Sopenharmony_ci		      ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full | \
63062306a36Sopenharmony_ci		      ADVERTISED_10000baseT_Full)
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_cistatic int set_link_ksettings(struct net_device *dev,
63362306a36Sopenharmony_ci			      const struct ethtool_link_ksettings *cmd)
63462306a36Sopenharmony_ci{
63562306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
63662306a36Sopenharmony_ci	struct port_info *p = &adapter->port[dev->if_port];
63762306a36Sopenharmony_ci	struct link_config *lc = &p->link_config;
63862306a36Sopenharmony_ci	u32 advertising;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	ethtool_convert_link_mode_to_legacy_u32(&advertising,
64162306a36Sopenharmony_ci						cmd->link_modes.advertising);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	if (!(lc->supported & SUPPORTED_Autoneg))
64462306a36Sopenharmony_ci		return -EOPNOTSUPP;             /* can't change speed/duplex */
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	if (cmd->base.autoneg == AUTONEG_DISABLE) {
64762306a36Sopenharmony_ci		u32 speed = cmd->base.speed;
64862306a36Sopenharmony_ci		int cap = speed_duplex_to_caps(speed, cmd->base.duplex);
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci		if (!(lc->supported & cap) || (speed == SPEED_1000))
65162306a36Sopenharmony_ci			return -EINVAL;
65262306a36Sopenharmony_ci		lc->requested_speed = speed;
65362306a36Sopenharmony_ci		lc->requested_duplex = cmd->base.duplex;
65462306a36Sopenharmony_ci		lc->advertising = 0;
65562306a36Sopenharmony_ci	} else {
65662306a36Sopenharmony_ci		advertising &= ADVERTISED_MASK;
65762306a36Sopenharmony_ci		if (advertising & (advertising - 1))
65862306a36Sopenharmony_ci			advertising = lc->supported;
65962306a36Sopenharmony_ci		advertising &= lc->supported;
66062306a36Sopenharmony_ci		if (!advertising)
66162306a36Sopenharmony_ci			return -EINVAL;
66262306a36Sopenharmony_ci		lc->requested_speed = SPEED_INVALID;
66362306a36Sopenharmony_ci		lc->requested_duplex = DUPLEX_INVALID;
66462306a36Sopenharmony_ci		lc->advertising = advertising | ADVERTISED_Autoneg;
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci	lc->autoneg = cmd->base.autoneg;
66762306a36Sopenharmony_ci	if (netif_running(dev))
66862306a36Sopenharmony_ci		t1_link_start(p->phy, p->mac, lc);
66962306a36Sopenharmony_ci	return 0;
67062306a36Sopenharmony_ci}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_cistatic void get_pauseparam(struct net_device *dev,
67362306a36Sopenharmony_ci			   struct ethtool_pauseparam *epause)
67462306a36Sopenharmony_ci{
67562306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
67662306a36Sopenharmony_ci	struct port_info *p = &adapter->port[dev->if_port];
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	epause->autoneg = (p->link_config.requested_fc & PAUSE_AUTONEG) != 0;
67962306a36Sopenharmony_ci	epause->rx_pause = (p->link_config.fc & PAUSE_RX) != 0;
68062306a36Sopenharmony_ci	epause->tx_pause = (p->link_config.fc & PAUSE_TX) != 0;
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_cistatic int set_pauseparam(struct net_device *dev,
68462306a36Sopenharmony_ci			  struct ethtool_pauseparam *epause)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
68762306a36Sopenharmony_ci	struct port_info *p = &adapter->port[dev->if_port];
68862306a36Sopenharmony_ci	struct link_config *lc = &p->link_config;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	if (epause->autoneg == AUTONEG_DISABLE)
69162306a36Sopenharmony_ci		lc->requested_fc = 0;
69262306a36Sopenharmony_ci	else if (lc->supported & SUPPORTED_Autoneg)
69362306a36Sopenharmony_ci		lc->requested_fc = PAUSE_AUTONEG;
69462306a36Sopenharmony_ci	else
69562306a36Sopenharmony_ci		return -EINVAL;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	if (epause->rx_pause)
69862306a36Sopenharmony_ci		lc->requested_fc |= PAUSE_RX;
69962306a36Sopenharmony_ci	if (epause->tx_pause)
70062306a36Sopenharmony_ci		lc->requested_fc |= PAUSE_TX;
70162306a36Sopenharmony_ci	if (lc->autoneg == AUTONEG_ENABLE) {
70262306a36Sopenharmony_ci		if (netif_running(dev))
70362306a36Sopenharmony_ci			t1_link_start(p->phy, p->mac, lc);
70462306a36Sopenharmony_ci	} else {
70562306a36Sopenharmony_ci		lc->fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX);
70662306a36Sopenharmony_ci		if (netif_running(dev))
70762306a36Sopenharmony_ci			p->mac->ops->set_speed_duplex_fc(p->mac, -1, -1,
70862306a36Sopenharmony_ci							 lc->fc);
70962306a36Sopenharmony_ci	}
71062306a36Sopenharmony_ci	return 0;
71162306a36Sopenharmony_ci}
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_cistatic void get_sge_param(struct net_device *dev, struct ethtool_ringparam *e,
71462306a36Sopenharmony_ci			  struct kernel_ethtool_ringparam *kernel_e,
71562306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
71862306a36Sopenharmony_ci	int jumbo_fl = t1_is_T1B(adapter) ? 1 : 0;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	e->rx_max_pending = MAX_RX_BUFFERS;
72162306a36Sopenharmony_ci	e->rx_jumbo_max_pending = MAX_RX_JUMBO_BUFFERS;
72262306a36Sopenharmony_ci	e->tx_max_pending = MAX_CMDQ_ENTRIES;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	e->rx_pending = adapter->params.sge.freelQ_size[!jumbo_fl];
72562306a36Sopenharmony_ci	e->rx_jumbo_pending = adapter->params.sge.freelQ_size[jumbo_fl];
72662306a36Sopenharmony_ci	e->tx_pending = adapter->params.sge.cmdQ_size[0];
72762306a36Sopenharmony_ci}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_cistatic int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e,
73062306a36Sopenharmony_ci			 struct kernel_ethtool_ringparam *kernel_e,
73162306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
73262306a36Sopenharmony_ci{
73362306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
73462306a36Sopenharmony_ci	int jumbo_fl = t1_is_T1B(adapter) ? 1 : 0;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	if (e->rx_pending > MAX_RX_BUFFERS || e->rx_mini_pending ||
73762306a36Sopenharmony_ci	    e->rx_jumbo_pending > MAX_RX_JUMBO_BUFFERS ||
73862306a36Sopenharmony_ci	    e->tx_pending > MAX_CMDQ_ENTRIES ||
73962306a36Sopenharmony_ci	    e->rx_pending < MIN_FL_ENTRIES ||
74062306a36Sopenharmony_ci	    e->rx_jumbo_pending < MIN_FL_ENTRIES ||
74162306a36Sopenharmony_ci	    e->tx_pending < (adapter->params.nports + 1) * (MAX_SKB_FRAGS + 1))
74262306a36Sopenharmony_ci		return -EINVAL;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	if (adapter->flags & FULL_INIT_DONE)
74562306a36Sopenharmony_ci		return -EBUSY;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	adapter->params.sge.freelQ_size[!jumbo_fl] = e->rx_pending;
74862306a36Sopenharmony_ci	adapter->params.sge.freelQ_size[jumbo_fl] = e->rx_jumbo_pending;
74962306a36Sopenharmony_ci	adapter->params.sge.cmdQ_size[0] = e->tx_pending;
75062306a36Sopenharmony_ci	adapter->params.sge.cmdQ_size[1] = e->tx_pending > MAX_CMDQ1_ENTRIES ?
75162306a36Sopenharmony_ci		MAX_CMDQ1_ENTRIES : e->tx_pending;
75262306a36Sopenharmony_ci	return 0;
75362306a36Sopenharmony_ci}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_cistatic int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c,
75662306a36Sopenharmony_ci			struct kernel_ethtool_coalesce *kernel_coal,
75762306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	adapter->params.sge.rx_coalesce_usecs = c->rx_coalesce_usecs;
76262306a36Sopenharmony_ci	adapter->params.sge.coalesce_enable = c->use_adaptive_rx_coalesce;
76362306a36Sopenharmony_ci	adapter->params.sge.sample_interval_usecs = c->rate_sample_interval;
76462306a36Sopenharmony_ci	t1_sge_set_coalesce_params(adapter->sge, &adapter->params.sge);
76562306a36Sopenharmony_ci	return 0;
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_cistatic int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c,
76962306a36Sopenharmony_ci			struct kernel_ethtool_coalesce *kernel_coal,
77062306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	c->rx_coalesce_usecs = adapter->params.sge.rx_coalesce_usecs;
77562306a36Sopenharmony_ci	c->rate_sample_interval = adapter->params.sge.sample_interval_usecs;
77662306a36Sopenharmony_ci	c->use_adaptive_rx_coalesce = adapter->params.sge.coalesce_enable;
77762306a36Sopenharmony_ci	return 0;
77862306a36Sopenharmony_ci}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_cistatic int get_eeprom_len(struct net_device *dev)
78162306a36Sopenharmony_ci{
78262306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	return t1_is_asic(adapter) ? EEPROM_SIZE : 0;
78562306a36Sopenharmony_ci}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci#define EEPROM_MAGIC(ap) \
78862306a36Sopenharmony_ci	(PCI_VENDOR_ID_CHELSIO | ((ap)->params.chip_version << 16))
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_cistatic int get_eeprom(struct net_device *dev, struct ethtool_eeprom *e,
79162306a36Sopenharmony_ci		      u8 *data)
79262306a36Sopenharmony_ci{
79362306a36Sopenharmony_ci	int i;
79462306a36Sopenharmony_ci	u8 buf[EEPROM_SIZE] __attribute__((aligned(4)));
79562306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	e->magic = EEPROM_MAGIC(adapter);
79862306a36Sopenharmony_ci	for (i = e->offset & ~3; i < e->offset + e->len; i += sizeof(u32))
79962306a36Sopenharmony_ci		t1_seeprom_read(adapter, i, (__le32 *)&buf[i]);
80062306a36Sopenharmony_ci	memcpy(data, buf + e->offset, e->len);
80162306a36Sopenharmony_ci	return 0;
80262306a36Sopenharmony_ci}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_cistatic const struct ethtool_ops t1_ethtool_ops = {
80562306a36Sopenharmony_ci	.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS |
80662306a36Sopenharmony_ci				     ETHTOOL_COALESCE_USE_ADAPTIVE_RX |
80762306a36Sopenharmony_ci				     ETHTOOL_COALESCE_RATE_SAMPLE_INTERVAL,
80862306a36Sopenharmony_ci	.get_drvinfo       = get_drvinfo,
80962306a36Sopenharmony_ci	.get_msglevel      = get_msglevel,
81062306a36Sopenharmony_ci	.set_msglevel      = set_msglevel,
81162306a36Sopenharmony_ci	.get_ringparam     = get_sge_param,
81262306a36Sopenharmony_ci	.set_ringparam     = set_sge_param,
81362306a36Sopenharmony_ci	.get_coalesce      = get_coalesce,
81462306a36Sopenharmony_ci	.set_coalesce      = set_coalesce,
81562306a36Sopenharmony_ci	.get_eeprom_len    = get_eeprom_len,
81662306a36Sopenharmony_ci	.get_eeprom        = get_eeprom,
81762306a36Sopenharmony_ci	.get_pauseparam    = get_pauseparam,
81862306a36Sopenharmony_ci	.set_pauseparam    = set_pauseparam,
81962306a36Sopenharmony_ci	.get_link          = ethtool_op_get_link,
82062306a36Sopenharmony_ci	.get_strings       = get_strings,
82162306a36Sopenharmony_ci	.get_sset_count	   = get_sset_count,
82262306a36Sopenharmony_ci	.get_ethtool_stats = get_stats,
82362306a36Sopenharmony_ci	.get_regs_len      = get_regs_len,
82462306a36Sopenharmony_ci	.get_regs          = get_regs,
82562306a36Sopenharmony_ci	.get_link_ksettings = get_link_ksettings,
82662306a36Sopenharmony_ci	.set_link_ksettings = set_link_ksettings,
82762306a36Sopenharmony_ci};
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_cistatic int t1_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
83062306a36Sopenharmony_ci{
83162306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
83262306a36Sopenharmony_ci	struct mdio_if_info *mdio = &adapter->port[dev->if_port].phy->mdio;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	return mdio_mii_ioctl(mdio, if_mii(req), cmd);
83562306a36Sopenharmony_ci}
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_cistatic int t1_change_mtu(struct net_device *dev, int new_mtu)
83862306a36Sopenharmony_ci{
83962306a36Sopenharmony_ci	int ret;
84062306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
84162306a36Sopenharmony_ci	struct cmac *mac = adapter->port[dev->if_port].mac;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	if (!mac->ops->set_mtu)
84462306a36Sopenharmony_ci		return -EOPNOTSUPP;
84562306a36Sopenharmony_ci	if ((ret = mac->ops->set_mtu(mac, new_mtu)))
84662306a36Sopenharmony_ci		return ret;
84762306a36Sopenharmony_ci	dev->mtu = new_mtu;
84862306a36Sopenharmony_ci	return 0;
84962306a36Sopenharmony_ci}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_cistatic int t1_set_mac_addr(struct net_device *dev, void *p)
85262306a36Sopenharmony_ci{
85362306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
85462306a36Sopenharmony_ci	struct cmac *mac = adapter->port[dev->if_port].mac;
85562306a36Sopenharmony_ci	struct sockaddr *addr = p;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	if (!mac->ops->macaddress_set)
85862306a36Sopenharmony_ci		return -EOPNOTSUPP;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	eth_hw_addr_set(dev, addr->sa_data);
86162306a36Sopenharmony_ci	mac->ops->macaddress_set(mac, dev->dev_addr);
86262306a36Sopenharmony_ci	return 0;
86362306a36Sopenharmony_ci}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_cistatic netdev_features_t t1_fix_features(struct net_device *dev,
86662306a36Sopenharmony_ci	netdev_features_t features)
86762306a36Sopenharmony_ci{
86862306a36Sopenharmony_ci	/*
86962306a36Sopenharmony_ci	 * Since there is no support for separate rx/tx vlan accel
87062306a36Sopenharmony_ci	 * enable/disable make sure tx flag is always in same state as rx.
87162306a36Sopenharmony_ci	 */
87262306a36Sopenharmony_ci	if (features & NETIF_F_HW_VLAN_CTAG_RX)
87362306a36Sopenharmony_ci		features |= NETIF_F_HW_VLAN_CTAG_TX;
87462306a36Sopenharmony_ci	else
87562306a36Sopenharmony_ci		features &= ~NETIF_F_HW_VLAN_CTAG_TX;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	return features;
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_cistatic int t1_set_features(struct net_device *dev, netdev_features_t features)
88162306a36Sopenharmony_ci{
88262306a36Sopenharmony_ci	netdev_features_t changed = dev->features ^ features;
88362306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	if (changed & NETIF_F_HW_VLAN_CTAG_RX)
88662306a36Sopenharmony_ci		t1_vlan_mode(adapter, features);
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	return 0;
88962306a36Sopenharmony_ci}
89062306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
89162306a36Sopenharmony_cistatic void t1_netpoll(struct net_device *dev)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	unsigned long flags;
89462306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	local_irq_save(flags);
89762306a36Sopenharmony_ci	t1_interrupt(adapter->pdev->irq, adapter);
89862306a36Sopenharmony_ci	local_irq_restore(flags);
89962306a36Sopenharmony_ci}
90062306a36Sopenharmony_ci#endif
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci/*
90362306a36Sopenharmony_ci * Periodic accumulation of MAC statistics.  This is used only if the MAC
90462306a36Sopenharmony_ci * does not have any other way to prevent stats counter overflow.
90562306a36Sopenharmony_ci */
90662306a36Sopenharmony_cistatic void mac_stats_task(struct work_struct *work)
90762306a36Sopenharmony_ci{
90862306a36Sopenharmony_ci	int i;
90962306a36Sopenharmony_ci	struct adapter *adapter =
91062306a36Sopenharmony_ci		container_of(work, struct adapter, stats_update_task.work);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	for_each_port(adapter, i) {
91362306a36Sopenharmony_ci		struct port_info *p = &adapter->port[i];
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci		if (netif_running(p->dev))
91662306a36Sopenharmony_ci			p->mac->ops->statistics_update(p->mac,
91762306a36Sopenharmony_ci						       MAC_STATS_UPDATE_FAST);
91862306a36Sopenharmony_ci	}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	/* Schedule the next statistics update if any port is active. */
92162306a36Sopenharmony_ci	spin_lock(&adapter->work_lock);
92262306a36Sopenharmony_ci	if (adapter->open_device_map & PORT_MASK)
92362306a36Sopenharmony_ci		schedule_mac_stats_update(adapter,
92462306a36Sopenharmony_ci					  adapter->params.stats_update_period);
92562306a36Sopenharmony_ci	spin_unlock(&adapter->work_lock);
92662306a36Sopenharmony_ci}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_cistatic const struct net_device_ops cxgb_netdev_ops = {
92962306a36Sopenharmony_ci	.ndo_open		= cxgb_open,
93062306a36Sopenharmony_ci	.ndo_stop		= cxgb_close,
93162306a36Sopenharmony_ci	.ndo_start_xmit		= t1_start_xmit,
93262306a36Sopenharmony_ci	.ndo_get_stats		= t1_get_stats,
93362306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
93462306a36Sopenharmony_ci	.ndo_set_rx_mode	= t1_set_rxmode,
93562306a36Sopenharmony_ci	.ndo_eth_ioctl		= t1_ioctl,
93662306a36Sopenharmony_ci	.ndo_change_mtu		= t1_change_mtu,
93762306a36Sopenharmony_ci	.ndo_set_mac_address	= t1_set_mac_addr,
93862306a36Sopenharmony_ci	.ndo_fix_features	= t1_fix_features,
93962306a36Sopenharmony_ci	.ndo_set_features	= t1_set_features,
94062306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
94162306a36Sopenharmony_ci	.ndo_poll_controller	= t1_netpoll,
94262306a36Sopenharmony_ci#endif
94362306a36Sopenharmony_ci};
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_cistatic int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
94662306a36Sopenharmony_ci{
94762306a36Sopenharmony_ci	unsigned long mmio_start, mmio_len;
94862306a36Sopenharmony_ci	const struct board_info *bi;
94962306a36Sopenharmony_ci	struct adapter *adapter = NULL;
95062306a36Sopenharmony_ci	struct port_info *pi;
95162306a36Sopenharmony_ci	int i, err;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	err = pci_enable_device(pdev);
95462306a36Sopenharmony_ci	if (err)
95562306a36Sopenharmony_ci		return err;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
95862306a36Sopenharmony_ci		pr_err("%s: cannot find PCI device memory base address\n",
95962306a36Sopenharmony_ci		       pci_name(pdev));
96062306a36Sopenharmony_ci		err = -ENODEV;
96162306a36Sopenharmony_ci		goto out_disable_pdev;
96262306a36Sopenharmony_ci	}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
96562306a36Sopenharmony_ci	if (err) {
96662306a36Sopenharmony_ci		pr_err("%s: no usable DMA configuration\n", pci_name(pdev));
96762306a36Sopenharmony_ci		goto out_disable_pdev;
96862306a36Sopenharmony_ci	}
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	err = pci_request_regions(pdev, DRV_NAME);
97162306a36Sopenharmony_ci	if (err) {
97262306a36Sopenharmony_ci		pr_err("%s: cannot obtain PCI resources\n", pci_name(pdev));
97362306a36Sopenharmony_ci		goto out_disable_pdev;
97462306a36Sopenharmony_ci	}
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	pci_set_master(pdev);
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	mmio_start = pci_resource_start(pdev, 0);
97962306a36Sopenharmony_ci	mmio_len = pci_resource_len(pdev, 0);
98062306a36Sopenharmony_ci	bi = t1_get_board_info(ent->driver_data);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	for (i = 0; i < bi->port_number; ++i) {
98362306a36Sopenharmony_ci		struct net_device *netdev;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci		netdev = alloc_etherdev(adapter ? 0 : sizeof(*adapter));
98662306a36Sopenharmony_ci		if (!netdev) {
98762306a36Sopenharmony_ci			err = -ENOMEM;
98862306a36Sopenharmony_ci			goto out_free_dev;
98962306a36Sopenharmony_ci		}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci		SET_NETDEV_DEV(netdev, &pdev->dev);
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci		if (!adapter) {
99462306a36Sopenharmony_ci			adapter = netdev_priv(netdev);
99562306a36Sopenharmony_ci			adapter->pdev = pdev;
99662306a36Sopenharmony_ci			adapter->port[0].dev = netdev;  /* so we don't leak it */
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci			adapter->regs = ioremap(mmio_start, mmio_len);
99962306a36Sopenharmony_ci			if (!adapter->regs) {
100062306a36Sopenharmony_ci				pr_err("%s: cannot map device registers\n",
100162306a36Sopenharmony_ci				       pci_name(pdev));
100262306a36Sopenharmony_ci				err = -ENOMEM;
100362306a36Sopenharmony_ci				goto out_free_dev;
100462306a36Sopenharmony_ci			}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci			if (t1_get_board_rev(adapter, bi, &adapter->params)) {
100762306a36Sopenharmony_ci				err = -ENODEV;	  /* Can't handle this chip rev */
100862306a36Sopenharmony_ci				goto out_free_dev;
100962306a36Sopenharmony_ci			}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci			adapter->name = pci_name(pdev);
101262306a36Sopenharmony_ci			adapter->msg_enable = dflt_msg_enable;
101362306a36Sopenharmony_ci			adapter->mmio_len = mmio_len;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci			spin_lock_init(&adapter->tpi_lock);
101662306a36Sopenharmony_ci			spin_lock_init(&adapter->work_lock);
101762306a36Sopenharmony_ci			spin_lock_init(&adapter->async_lock);
101862306a36Sopenharmony_ci			spin_lock_init(&adapter->mac_lock);
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci			INIT_DELAYED_WORK(&adapter->stats_update_task,
102162306a36Sopenharmony_ci					  mac_stats_task);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci			pci_set_drvdata(pdev, netdev);
102462306a36Sopenharmony_ci		}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci		pi = &adapter->port[i];
102762306a36Sopenharmony_ci		pi->dev = netdev;
102862306a36Sopenharmony_ci		netif_carrier_off(netdev);
102962306a36Sopenharmony_ci		netdev->irq = pdev->irq;
103062306a36Sopenharmony_ci		netdev->if_port = i;
103162306a36Sopenharmony_ci		netdev->mem_start = mmio_start;
103262306a36Sopenharmony_ci		netdev->mem_end = mmio_start + mmio_len - 1;
103362306a36Sopenharmony_ci		netdev->ml_priv = adapter;
103462306a36Sopenharmony_ci		netdev->hw_features |= NETIF_F_SG | NETIF_F_IP_CSUM |
103562306a36Sopenharmony_ci			NETIF_F_RXCSUM;
103662306a36Sopenharmony_ci		netdev->features |= NETIF_F_SG | NETIF_F_IP_CSUM |
103762306a36Sopenharmony_ci			NETIF_F_RXCSUM | NETIF_F_LLTX | NETIF_F_HIGHDMA;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci		if (vlan_tso_capable(adapter)) {
104062306a36Sopenharmony_ci			netdev->features |=
104162306a36Sopenharmony_ci				NETIF_F_HW_VLAN_CTAG_TX |
104262306a36Sopenharmony_ci				NETIF_F_HW_VLAN_CTAG_RX;
104362306a36Sopenharmony_ci			netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci			/* T204: disable TSO */
104662306a36Sopenharmony_ci			if (!(is_T2(adapter)) || bi->port_number != 4) {
104762306a36Sopenharmony_ci				netdev->hw_features |= NETIF_F_TSO;
104862306a36Sopenharmony_ci				netdev->features |= NETIF_F_TSO;
104962306a36Sopenharmony_ci			}
105062306a36Sopenharmony_ci		}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci		netdev->netdev_ops = &cxgb_netdev_ops;
105362306a36Sopenharmony_ci		netdev->hard_header_len += (netdev->hw_features & NETIF_F_TSO) ?
105462306a36Sopenharmony_ci			sizeof(struct cpl_tx_pkt_lso) : sizeof(struct cpl_tx_pkt);
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci		netif_napi_add(netdev, &adapter->napi, t1_poll);
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci		netdev->ethtool_ops = &t1_ethtool_ops;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci		switch (bi->board) {
106162306a36Sopenharmony_ci		case CHBT_BOARD_CHT110:
106262306a36Sopenharmony_ci		case CHBT_BOARD_N110:
106362306a36Sopenharmony_ci		case CHBT_BOARD_N210:
106462306a36Sopenharmony_ci		case CHBT_BOARD_CHT210:
106562306a36Sopenharmony_ci			netdev->max_mtu = PM3393_MAX_FRAME_SIZE -
106662306a36Sopenharmony_ci					  (ETH_HLEN + ETH_FCS_LEN);
106762306a36Sopenharmony_ci			break;
106862306a36Sopenharmony_ci		case CHBT_BOARD_CHN204:
106962306a36Sopenharmony_ci			netdev->max_mtu = VSC7326_MAX_MTU;
107062306a36Sopenharmony_ci			break;
107162306a36Sopenharmony_ci		default:
107262306a36Sopenharmony_ci			netdev->max_mtu = ETH_DATA_LEN;
107362306a36Sopenharmony_ci			break;
107462306a36Sopenharmony_ci		}
107562306a36Sopenharmony_ci	}
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	if (t1_init_sw_modules(adapter, bi) < 0) {
107862306a36Sopenharmony_ci		err = -ENODEV;
107962306a36Sopenharmony_ci		goto out_free_dev;
108062306a36Sopenharmony_ci	}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	/*
108362306a36Sopenharmony_ci	 * The card is now ready to go.  If any errors occur during device
108462306a36Sopenharmony_ci	 * registration we do not fail the whole card but rather proceed only
108562306a36Sopenharmony_ci	 * with the ports we manage to register successfully.  However we must
108662306a36Sopenharmony_ci	 * register at least one net device.
108762306a36Sopenharmony_ci	 */
108862306a36Sopenharmony_ci	for (i = 0; i < bi->port_number; ++i) {
108962306a36Sopenharmony_ci		err = register_netdev(adapter->port[i].dev);
109062306a36Sopenharmony_ci		if (err)
109162306a36Sopenharmony_ci			pr_warn("%s: cannot register net device %s, skipping\n",
109262306a36Sopenharmony_ci				pci_name(pdev), adapter->port[i].dev->name);
109362306a36Sopenharmony_ci		else {
109462306a36Sopenharmony_ci			/*
109562306a36Sopenharmony_ci			 * Change the name we use for messages to the name of
109662306a36Sopenharmony_ci			 * the first successfully registered interface.
109762306a36Sopenharmony_ci			 */
109862306a36Sopenharmony_ci			if (!adapter->registered_device_map)
109962306a36Sopenharmony_ci				adapter->name = adapter->port[i].dev->name;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci			__set_bit(i, &adapter->registered_device_map);
110262306a36Sopenharmony_ci		}
110362306a36Sopenharmony_ci	}
110462306a36Sopenharmony_ci	if (!adapter->registered_device_map) {
110562306a36Sopenharmony_ci		pr_err("%s: could not register any net devices\n",
110662306a36Sopenharmony_ci		       pci_name(pdev));
110762306a36Sopenharmony_ci		err = -EINVAL;
110862306a36Sopenharmony_ci		goto out_release_adapter_res;
110962306a36Sopenharmony_ci	}
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	pr_info("%s: %s (rev %d), %s %dMHz/%d-bit\n",
111262306a36Sopenharmony_ci		adapter->name, bi->desc, adapter->params.chip_revision,
111362306a36Sopenharmony_ci		adapter->params.pci.is_pcix ? "PCIX" : "PCI",
111462306a36Sopenharmony_ci		adapter->params.pci.speed, adapter->params.pci.width);
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	/*
111762306a36Sopenharmony_ci	 * Set the T1B ASIC and memory clocks.
111862306a36Sopenharmony_ci	 */
111962306a36Sopenharmony_ci	if (t1powersave)
112062306a36Sopenharmony_ci		adapter->t1powersave = LCLOCK;	/* HW default is powersave mode. */
112162306a36Sopenharmony_ci	else
112262306a36Sopenharmony_ci		adapter->t1powersave = HCLOCK;
112362306a36Sopenharmony_ci	if (t1_is_T1B(adapter))
112462306a36Sopenharmony_ci		t1_clock(adapter, t1powersave);
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	return 0;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ciout_release_adapter_res:
112962306a36Sopenharmony_ci	t1_free_sw_modules(adapter);
113062306a36Sopenharmony_ciout_free_dev:
113162306a36Sopenharmony_ci	if (adapter) {
113262306a36Sopenharmony_ci		if (adapter->regs)
113362306a36Sopenharmony_ci			iounmap(adapter->regs);
113462306a36Sopenharmony_ci		for (i = bi->port_number - 1; i >= 0; --i)
113562306a36Sopenharmony_ci			if (adapter->port[i].dev)
113662306a36Sopenharmony_ci				free_netdev(adapter->port[i].dev);
113762306a36Sopenharmony_ci	}
113862306a36Sopenharmony_ci	pci_release_regions(pdev);
113962306a36Sopenharmony_ciout_disable_pdev:
114062306a36Sopenharmony_ci	pci_disable_device(pdev);
114162306a36Sopenharmony_ci	return err;
114262306a36Sopenharmony_ci}
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_cistatic void bit_bang(struct adapter *adapter, int bitdata, int nbits)
114562306a36Sopenharmony_ci{
114662306a36Sopenharmony_ci	int data;
114762306a36Sopenharmony_ci	int i;
114862306a36Sopenharmony_ci	u32 val;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	enum {
115162306a36Sopenharmony_ci		S_CLOCK = 1 << 3,
115262306a36Sopenharmony_ci		S_DATA = 1 << 4
115362306a36Sopenharmony_ci	};
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	for (i = (nbits - 1); i > -1; i--) {
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci		udelay(50);
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci		data = ((bitdata >> i) & 0x1);
116062306a36Sopenharmony_ci		__t1_tpi_read(adapter, A_ELMER0_GPO, &val);
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci		if (data)
116362306a36Sopenharmony_ci			val |= S_DATA;
116462306a36Sopenharmony_ci		else
116562306a36Sopenharmony_ci			val &= ~S_DATA;
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci		udelay(50);
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci		/* Set SCLOCK low */
117062306a36Sopenharmony_ci		val &= ~S_CLOCK;
117162306a36Sopenharmony_ci		__t1_tpi_write(adapter, A_ELMER0_GPO, val);
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci		udelay(50);
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci		/* Write SCLOCK high */
117662306a36Sopenharmony_ci		val |= S_CLOCK;
117762306a36Sopenharmony_ci		__t1_tpi_write(adapter, A_ELMER0_GPO, val);
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	}
118062306a36Sopenharmony_ci}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_cistatic int t1_clock(struct adapter *adapter, int mode)
118362306a36Sopenharmony_ci{
118462306a36Sopenharmony_ci	u32 val;
118562306a36Sopenharmony_ci	int M_CORE_VAL;
118662306a36Sopenharmony_ci	int M_MEM_VAL;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	enum {
118962306a36Sopenharmony_ci		M_CORE_BITS	= 9,
119062306a36Sopenharmony_ci		T_CORE_VAL	= 0,
119162306a36Sopenharmony_ci		T_CORE_BITS	= 2,
119262306a36Sopenharmony_ci		N_CORE_VAL	= 0,
119362306a36Sopenharmony_ci		N_CORE_BITS	= 2,
119462306a36Sopenharmony_ci		M_MEM_BITS	= 9,
119562306a36Sopenharmony_ci		T_MEM_VAL	= 0,
119662306a36Sopenharmony_ci		T_MEM_BITS	= 2,
119762306a36Sopenharmony_ci		N_MEM_VAL	= 0,
119862306a36Sopenharmony_ci		N_MEM_BITS	= 2,
119962306a36Sopenharmony_ci		NP_LOAD		= 1 << 17,
120062306a36Sopenharmony_ci		S_LOAD_MEM	= 1 << 5,
120162306a36Sopenharmony_ci		S_LOAD_CORE	= 1 << 6,
120262306a36Sopenharmony_ci		S_CLOCK		= 1 << 3
120362306a36Sopenharmony_ci	};
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	if (!t1_is_T1B(adapter))
120662306a36Sopenharmony_ci		return -ENODEV;	/* Can't re-clock this chip. */
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	if (mode & 2)
120962306a36Sopenharmony_ci		return 0;	/* show current mode. */
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	if ((adapter->t1powersave & 1) == (mode & 1))
121262306a36Sopenharmony_ci		return -EALREADY;	/* ASIC already running in mode. */
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	if ((mode & 1) == HCLOCK) {
121562306a36Sopenharmony_ci		M_CORE_VAL = 0x14;
121662306a36Sopenharmony_ci		M_MEM_VAL = 0x18;
121762306a36Sopenharmony_ci		adapter->t1powersave = HCLOCK;	/* overclock */
121862306a36Sopenharmony_ci	} else {
121962306a36Sopenharmony_ci		M_CORE_VAL = 0xe;
122062306a36Sopenharmony_ci		M_MEM_VAL = 0x10;
122162306a36Sopenharmony_ci		adapter->t1powersave = LCLOCK;	/* underclock */
122262306a36Sopenharmony_ci	}
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	/* Don't interrupt this serial stream! */
122562306a36Sopenharmony_ci	spin_lock(&adapter->tpi_lock);
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	/* Initialize for ASIC core */
122862306a36Sopenharmony_ci	__t1_tpi_read(adapter, A_ELMER0_GPO, &val);
122962306a36Sopenharmony_ci	val |= NP_LOAD;
123062306a36Sopenharmony_ci	udelay(50);
123162306a36Sopenharmony_ci	__t1_tpi_write(adapter, A_ELMER0_GPO, val);
123262306a36Sopenharmony_ci	udelay(50);
123362306a36Sopenharmony_ci	__t1_tpi_read(adapter, A_ELMER0_GPO, &val);
123462306a36Sopenharmony_ci	val &= ~S_LOAD_CORE;
123562306a36Sopenharmony_ci	val &= ~S_CLOCK;
123662306a36Sopenharmony_ci	__t1_tpi_write(adapter, A_ELMER0_GPO, val);
123762306a36Sopenharmony_ci	udelay(50);
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	/* Serial program the ASIC clock synthesizer */
124062306a36Sopenharmony_ci	bit_bang(adapter, T_CORE_VAL, T_CORE_BITS);
124162306a36Sopenharmony_ci	bit_bang(adapter, N_CORE_VAL, N_CORE_BITS);
124262306a36Sopenharmony_ci	bit_bang(adapter, M_CORE_VAL, M_CORE_BITS);
124362306a36Sopenharmony_ci	udelay(50);
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	/* Finish ASIC core */
124662306a36Sopenharmony_ci	__t1_tpi_read(adapter, A_ELMER0_GPO, &val);
124762306a36Sopenharmony_ci	val |= S_LOAD_CORE;
124862306a36Sopenharmony_ci	udelay(50);
124962306a36Sopenharmony_ci	__t1_tpi_write(adapter, A_ELMER0_GPO, val);
125062306a36Sopenharmony_ci	udelay(50);
125162306a36Sopenharmony_ci	__t1_tpi_read(adapter, A_ELMER0_GPO, &val);
125262306a36Sopenharmony_ci	val &= ~S_LOAD_CORE;
125362306a36Sopenharmony_ci	udelay(50);
125462306a36Sopenharmony_ci	__t1_tpi_write(adapter, A_ELMER0_GPO, val);
125562306a36Sopenharmony_ci	udelay(50);
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	/* Initialize for memory */
125862306a36Sopenharmony_ci	__t1_tpi_read(adapter, A_ELMER0_GPO, &val);
125962306a36Sopenharmony_ci	val |= NP_LOAD;
126062306a36Sopenharmony_ci	udelay(50);
126162306a36Sopenharmony_ci	__t1_tpi_write(adapter, A_ELMER0_GPO, val);
126262306a36Sopenharmony_ci	udelay(50);
126362306a36Sopenharmony_ci	__t1_tpi_read(adapter, A_ELMER0_GPO, &val);
126462306a36Sopenharmony_ci	val &= ~S_LOAD_MEM;
126562306a36Sopenharmony_ci	val &= ~S_CLOCK;
126662306a36Sopenharmony_ci	udelay(50);
126762306a36Sopenharmony_ci	__t1_tpi_write(adapter, A_ELMER0_GPO, val);
126862306a36Sopenharmony_ci	udelay(50);
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	/* Serial program the memory clock synthesizer */
127162306a36Sopenharmony_ci	bit_bang(adapter, T_MEM_VAL, T_MEM_BITS);
127262306a36Sopenharmony_ci	bit_bang(adapter, N_MEM_VAL, N_MEM_BITS);
127362306a36Sopenharmony_ci	bit_bang(adapter, M_MEM_VAL, M_MEM_BITS);
127462306a36Sopenharmony_ci	udelay(50);
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	/* Finish memory */
127762306a36Sopenharmony_ci	__t1_tpi_read(adapter, A_ELMER0_GPO, &val);
127862306a36Sopenharmony_ci	val |= S_LOAD_MEM;
127962306a36Sopenharmony_ci	udelay(50);
128062306a36Sopenharmony_ci	__t1_tpi_write(adapter, A_ELMER0_GPO, val);
128162306a36Sopenharmony_ci	udelay(50);
128262306a36Sopenharmony_ci	__t1_tpi_read(adapter, A_ELMER0_GPO, &val);
128362306a36Sopenharmony_ci	val &= ~S_LOAD_MEM;
128462306a36Sopenharmony_ci	udelay(50);
128562306a36Sopenharmony_ci	__t1_tpi_write(adapter, A_ELMER0_GPO, val);
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	spin_unlock(&adapter->tpi_lock);
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	return 0;
129062306a36Sopenharmony_ci}
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_cistatic inline void t1_sw_reset(struct pci_dev *pdev)
129362306a36Sopenharmony_ci{
129462306a36Sopenharmony_ci	pci_write_config_dword(pdev, A_PCICFG_PM_CSR, 3);
129562306a36Sopenharmony_ci	pci_write_config_dword(pdev, A_PCICFG_PM_CSR, 0);
129662306a36Sopenharmony_ci}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_cistatic void remove_one(struct pci_dev *pdev)
129962306a36Sopenharmony_ci{
130062306a36Sopenharmony_ci	struct net_device *dev = pci_get_drvdata(pdev);
130162306a36Sopenharmony_ci	struct adapter *adapter = dev->ml_priv;
130262306a36Sopenharmony_ci	int i;
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	for_each_port(adapter, i) {
130562306a36Sopenharmony_ci		if (test_bit(i, &adapter->registered_device_map))
130662306a36Sopenharmony_ci			unregister_netdev(adapter->port[i].dev);
130762306a36Sopenharmony_ci	}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	t1_free_sw_modules(adapter);
131062306a36Sopenharmony_ci	iounmap(adapter->regs);
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	while (--i >= 0) {
131362306a36Sopenharmony_ci		if (adapter->port[i].dev)
131462306a36Sopenharmony_ci			free_netdev(adapter->port[i].dev);
131562306a36Sopenharmony_ci	}
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	pci_release_regions(pdev);
131862306a36Sopenharmony_ci	pci_disable_device(pdev);
131962306a36Sopenharmony_ci	t1_sw_reset(pdev);
132062306a36Sopenharmony_ci}
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_cistatic struct pci_driver cxgb_pci_driver = {
132362306a36Sopenharmony_ci	.name     = DRV_NAME,
132462306a36Sopenharmony_ci	.id_table = t1_pci_tbl,
132562306a36Sopenharmony_ci	.probe    = init_one,
132662306a36Sopenharmony_ci	.remove   = remove_one,
132762306a36Sopenharmony_ci};
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_cimodule_pci_driver(cxgb_pci_driver);
1330