162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2019 - 2022 Beijing WangXun Technology Co., Ltd. */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/types.h>
562306a36Sopenharmony_ci#include <linux/module.h>
662306a36Sopenharmony_ci#include <linux/pci.h>
762306a36Sopenharmony_ci#include <linux/netdevice.h>
862306a36Sopenharmony_ci#include <linux/string.h>
962306a36Sopenharmony_ci#include <linux/etherdevice.h>
1062306a36Sopenharmony_ci#include <net/ip.h>
1162306a36Sopenharmony_ci#include <linux/phy.h>
1262306a36Sopenharmony_ci#include <linux/if_vlan.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "../libwx/wx_type.h"
1562306a36Sopenharmony_ci#include "../libwx/wx_hw.h"
1662306a36Sopenharmony_ci#include "../libwx/wx_lib.h"
1762306a36Sopenharmony_ci#include "ngbe_type.h"
1862306a36Sopenharmony_ci#include "ngbe_mdio.h"
1962306a36Sopenharmony_ci#include "ngbe_hw.h"
2062306a36Sopenharmony_ci#include "ngbe_ethtool.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cichar ngbe_driver_name[] = "ngbe";
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* ngbe_pci_tbl - PCI Device ID Table
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * { Vendor ID, Device ID, SubVendor ID, SubDevice ID,
2762306a36Sopenharmony_ci *   Class, Class Mask, private data (not used) }
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_cistatic const struct pci_device_id ngbe_pci_tbl[] = {
3062306a36Sopenharmony_ci	{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL_W), 0},
3162306a36Sopenharmony_ci	{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A2), 0},
3262306a36Sopenharmony_ci	{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A2S), 0},
3362306a36Sopenharmony_ci	{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A4), 0},
3462306a36Sopenharmony_ci	{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A4S), 0},
3562306a36Sopenharmony_ci	{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL2), 0},
3662306a36Sopenharmony_ci	{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL2S), 0},
3762306a36Sopenharmony_ci	{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL4), 0},
3862306a36Sopenharmony_ci	{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL4S), 0},
3962306a36Sopenharmony_ci	{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860LC), 0},
4062306a36Sopenharmony_ci	{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A1), 0},
4162306a36Sopenharmony_ci	{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A1L), 0},
4262306a36Sopenharmony_ci	/* required last entry */
4362306a36Sopenharmony_ci	{ .device = 0 }
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/**
4762306a36Sopenharmony_ci *  ngbe_init_type_code - Initialize the shared code
4862306a36Sopenharmony_ci *  @wx: pointer to hardware structure
4962306a36Sopenharmony_ci **/
5062306a36Sopenharmony_cistatic void ngbe_init_type_code(struct wx *wx)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	int wol_mask = 0, ncsi_mask = 0;
5362306a36Sopenharmony_ci	u16 type_mask = 0, val;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	wx->mac.type = wx_mac_em;
5662306a36Sopenharmony_ci	type_mask = (u16)(wx->subsystem_device_id & NGBE_OEM_MASK);
5762306a36Sopenharmony_ci	ncsi_mask = wx->subsystem_device_id & NGBE_NCSI_MASK;
5862306a36Sopenharmony_ci	wol_mask = wx->subsystem_device_id & NGBE_WOL_MASK;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	val = rd32(wx, WX_CFG_PORT_ST);
6162306a36Sopenharmony_ci	wx->mac_type = (val & BIT(7)) >> 7 ?
6262306a36Sopenharmony_ci		       em_mac_type_rgmii :
6362306a36Sopenharmony_ci		       em_mac_type_mdi;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	wx->wol_hw_supported = (wol_mask == NGBE_WOL_SUP) ? 1 : 0;
6662306a36Sopenharmony_ci	wx->ncsi_enabled = (ncsi_mask == NGBE_NCSI_MASK ||
6762306a36Sopenharmony_ci			   type_mask == NGBE_SUBID_OCP_CARD) ? 1 : 0;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	switch (type_mask) {
7062306a36Sopenharmony_ci	case NGBE_SUBID_LY_YT8521S_SFP:
7162306a36Sopenharmony_ci	case NGBE_SUBID_LY_M88E1512_SFP:
7262306a36Sopenharmony_ci	case NGBE_SUBID_YT8521S_SFP_GPIO:
7362306a36Sopenharmony_ci	case NGBE_SUBID_INTERNAL_YT8521S_SFP_GPIO:
7462306a36Sopenharmony_ci		wx->gpio_ctrl = 1;
7562306a36Sopenharmony_ci		break;
7662306a36Sopenharmony_ci	default:
7762306a36Sopenharmony_ci		wx->gpio_ctrl = 0;
7862306a36Sopenharmony_ci		break;
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/**
8362306a36Sopenharmony_ci * ngbe_init_rss_key - Initialize wx RSS key
8462306a36Sopenharmony_ci * @wx: device handle
8562306a36Sopenharmony_ci *
8662306a36Sopenharmony_ci * Allocates and initializes the RSS key if it is not allocated.
8762306a36Sopenharmony_ci **/
8862306a36Sopenharmony_cistatic inline int ngbe_init_rss_key(struct wx *wx)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	u32 *rss_key;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (!wx->rss_key) {
9362306a36Sopenharmony_ci		rss_key = kzalloc(WX_RSS_KEY_SIZE, GFP_KERNEL);
9462306a36Sopenharmony_ci		if (unlikely(!rss_key))
9562306a36Sopenharmony_ci			return -ENOMEM;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci		netdev_rss_key_fill(rss_key, WX_RSS_KEY_SIZE);
9862306a36Sopenharmony_ci		wx->rss_key = rss_key;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return 0;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/**
10562306a36Sopenharmony_ci * ngbe_sw_init - Initialize general software structures
10662306a36Sopenharmony_ci * @wx: board private structure to initialize
10762306a36Sopenharmony_ci **/
10862306a36Sopenharmony_cistatic int ngbe_sw_init(struct wx *wx)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct pci_dev *pdev = wx->pdev;
11162306a36Sopenharmony_ci	u16 msix_count = 0;
11262306a36Sopenharmony_ci	int err = 0;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	wx->mac.num_rar_entries = NGBE_RAR_ENTRIES;
11562306a36Sopenharmony_ci	wx->mac.max_rx_queues = NGBE_MAX_RX_QUEUES;
11662306a36Sopenharmony_ci	wx->mac.max_tx_queues = NGBE_MAX_TX_QUEUES;
11762306a36Sopenharmony_ci	wx->mac.mcft_size = NGBE_MC_TBL_SIZE;
11862306a36Sopenharmony_ci	wx->mac.vft_size = NGBE_SP_VFT_TBL_SIZE;
11962306a36Sopenharmony_ci	wx->mac.rx_pb_size = NGBE_RX_PB_SIZE;
12062306a36Sopenharmony_ci	wx->mac.tx_pb_size = NGBE_TDB_PB_SZ;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/* PCI config space info */
12362306a36Sopenharmony_ci	err = wx_sw_init(wx);
12462306a36Sopenharmony_ci	if (err < 0)
12562306a36Sopenharmony_ci		return err;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	/* mac type, phy type , oem type */
12862306a36Sopenharmony_ci	ngbe_init_type_code(wx);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/* Set common capability flags and settings */
13162306a36Sopenharmony_ci	wx->max_q_vectors = NGBE_MAX_MSIX_VECTORS;
13262306a36Sopenharmony_ci	err = wx_get_pcie_msix_counts(wx, &msix_count, NGBE_MAX_MSIX_VECTORS);
13362306a36Sopenharmony_ci	if (err)
13462306a36Sopenharmony_ci		dev_err(&pdev->dev, "Do not support MSI-X\n");
13562306a36Sopenharmony_ci	wx->mac.max_msix_vectors = msix_count;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (ngbe_init_rss_key(wx))
13862306a36Sopenharmony_ci		return -ENOMEM;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/* enable itr by default in dynamic mode */
14162306a36Sopenharmony_ci	wx->rx_itr_setting = 1;
14262306a36Sopenharmony_ci	wx->tx_itr_setting = 1;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/* set default ring sizes */
14562306a36Sopenharmony_ci	wx->tx_ring_count = NGBE_DEFAULT_TXD;
14662306a36Sopenharmony_ci	wx->rx_ring_count = NGBE_DEFAULT_RXD;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	/* set default work limits */
14962306a36Sopenharmony_ci	wx->tx_work_limit = NGBE_DEFAULT_TX_WORK;
15062306a36Sopenharmony_ci	wx->rx_work_limit = NGBE_DEFAULT_RX_WORK;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	return 0;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/**
15662306a36Sopenharmony_ci * ngbe_irq_enable - Enable default interrupt generation settings
15762306a36Sopenharmony_ci * @wx: board private structure
15862306a36Sopenharmony_ci * @queues: enable all queues interrupts
15962306a36Sopenharmony_ci **/
16062306a36Sopenharmony_cistatic void ngbe_irq_enable(struct wx *wx, bool queues)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	u32 mask;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* enable misc interrupt */
16562306a36Sopenharmony_ci	mask = NGBE_PX_MISC_IEN_MASK;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	wr32(wx, WX_GPIO_DDR, WX_GPIO_DDR_0);
16862306a36Sopenharmony_ci	wr32(wx, WX_GPIO_INTEN, WX_GPIO_INTEN_0 | WX_GPIO_INTEN_1);
16962306a36Sopenharmony_ci	wr32(wx, WX_GPIO_INTTYPE_LEVEL, 0x0);
17062306a36Sopenharmony_ci	wr32(wx, WX_GPIO_POLARITY, wx->gpio_ctrl ? 0 : 0x3);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	wr32(wx, WX_PX_MISC_IEN, mask);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/* mask interrupt */
17562306a36Sopenharmony_ci	if (queues)
17662306a36Sopenharmony_ci		wx_intr_enable(wx, NGBE_INTR_ALL);
17762306a36Sopenharmony_ci	else
17862306a36Sopenharmony_ci		wx_intr_enable(wx, NGBE_INTR_MISC(wx));
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci/**
18262306a36Sopenharmony_ci * ngbe_intr - msi/legacy mode Interrupt Handler
18362306a36Sopenharmony_ci * @irq: interrupt number
18462306a36Sopenharmony_ci * @data: pointer to a network interface device structure
18562306a36Sopenharmony_ci **/
18662306a36Sopenharmony_cistatic irqreturn_t ngbe_intr(int __always_unused irq, void *data)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	struct wx_q_vector *q_vector;
18962306a36Sopenharmony_ci	struct wx *wx  = data;
19062306a36Sopenharmony_ci	struct pci_dev *pdev;
19162306a36Sopenharmony_ci	u32 eicr;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	q_vector = wx->q_vector[0];
19462306a36Sopenharmony_ci	pdev = wx->pdev;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	eicr = wx_misc_isb(wx, WX_ISB_VEC0);
19762306a36Sopenharmony_ci	if (!eicr) {
19862306a36Sopenharmony_ci		/* shared interrupt alert!
19962306a36Sopenharmony_ci		 * the interrupt that we masked before the EICR read.
20062306a36Sopenharmony_ci		 */
20162306a36Sopenharmony_ci		if (netif_running(wx->netdev))
20262306a36Sopenharmony_ci			ngbe_irq_enable(wx, true);
20362306a36Sopenharmony_ci		return IRQ_NONE;        /* Not our interrupt */
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci	wx->isb_mem[WX_ISB_VEC0] = 0;
20662306a36Sopenharmony_ci	if (!(pdev->msi_enabled))
20762306a36Sopenharmony_ci		wr32(wx, WX_PX_INTA, 1);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	wx->isb_mem[WX_ISB_MISC] = 0;
21062306a36Sopenharmony_ci	/* would disable interrupts here but it is auto disabled */
21162306a36Sopenharmony_ci	napi_schedule_irqoff(&q_vector->napi);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (netif_running(wx->netdev))
21462306a36Sopenharmony_ci		ngbe_irq_enable(wx, false);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	return IRQ_HANDLED;
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic irqreturn_t ngbe_msix_other(int __always_unused irq, void *data)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct wx *wx = data;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/* re-enable the original interrupt state, no lsc, no queues */
22462306a36Sopenharmony_ci	if (netif_running(wx->netdev))
22562306a36Sopenharmony_ci		ngbe_irq_enable(wx, false);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	return IRQ_HANDLED;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci/**
23162306a36Sopenharmony_ci * ngbe_request_msix_irqs - Initialize MSI-X interrupts
23262306a36Sopenharmony_ci * @wx: board private structure
23362306a36Sopenharmony_ci *
23462306a36Sopenharmony_ci * ngbe_request_msix_irqs allocates MSI-X vectors and requests
23562306a36Sopenharmony_ci * interrupts from the kernel.
23662306a36Sopenharmony_ci **/
23762306a36Sopenharmony_cistatic int ngbe_request_msix_irqs(struct wx *wx)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct net_device *netdev = wx->netdev;
24062306a36Sopenharmony_ci	int vector, err;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	for (vector = 0; vector < wx->num_q_vectors; vector++) {
24362306a36Sopenharmony_ci		struct wx_q_vector *q_vector = wx->q_vector[vector];
24462306a36Sopenharmony_ci		struct msix_entry *entry = &wx->msix_entries[vector];
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci		if (q_vector->tx.ring && q_vector->rx.ring)
24762306a36Sopenharmony_ci			snprintf(q_vector->name, sizeof(q_vector->name) - 1,
24862306a36Sopenharmony_ci				 "%s-TxRx-%d", netdev->name, entry->entry);
24962306a36Sopenharmony_ci		else
25062306a36Sopenharmony_ci			/* skip this unused q_vector */
25162306a36Sopenharmony_ci			continue;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		err = request_irq(entry->vector, wx_msix_clean_rings, 0,
25462306a36Sopenharmony_ci				  q_vector->name, q_vector);
25562306a36Sopenharmony_ci		if (err) {
25662306a36Sopenharmony_ci			wx_err(wx, "request_irq failed for MSIX interrupt %s Error: %d\n",
25762306a36Sopenharmony_ci			       q_vector->name, err);
25862306a36Sopenharmony_ci			goto free_queue_irqs;
25962306a36Sopenharmony_ci		}
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	err = request_irq(wx->msix_entries[vector].vector,
26362306a36Sopenharmony_ci			  ngbe_msix_other, 0, netdev->name, wx);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (err) {
26662306a36Sopenharmony_ci		wx_err(wx, "request_irq for msix_other failed: %d\n", err);
26762306a36Sopenharmony_ci		goto free_queue_irqs;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return 0;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cifree_queue_irqs:
27362306a36Sopenharmony_ci	while (vector) {
27462306a36Sopenharmony_ci		vector--;
27562306a36Sopenharmony_ci		free_irq(wx->msix_entries[vector].vector,
27662306a36Sopenharmony_ci			 wx->q_vector[vector]);
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci	wx_reset_interrupt_capability(wx);
27962306a36Sopenharmony_ci	return err;
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci/**
28362306a36Sopenharmony_ci * ngbe_request_irq - initialize interrupts
28462306a36Sopenharmony_ci * @wx: board private structure
28562306a36Sopenharmony_ci *
28662306a36Sopenharmony_ci * Attempts to configure interrupts using the best available
28762306a36Sopenharmony_ci * capabilities of the hardware and kernel.
28862306a36Sopenharmony_ci **/
28962306a36Sopenharmony_cistatic int ngbe_request_irq(struct wx *wx)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	struct net_device *netdev = wx->netdev;
29262306a36Sopenharmony_ci	struct pci_dev *pdev = wx->pdev;
29362306a36Sopenharmony_ci	int err;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (pdev->msix_enabled)
29662306a36Sopenharmony_ci		err = ngbe_request_msix_irqs(wx);
29762306a36Sopenharmony_ci	else if (pdev->msi_enabled)
29862306a36Sopenharmony_ci		err = request_irq(pdev->irq, ngbe_intr, 0,
29962306a36Sopenharmony_ci				  netdev->name, wx);
30062306a36Sopenharmony_ci	else
30162306a36Sopenharmony_ci		err = request_irq(pdev->irq, ngbe_intr, IRQF_SHARED,
30262306a36Sopenharmony_ci				  netdev->name, wx);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	if (err)
30562306a36Sopenharmony_ci		wx_err(wx, "request_irq failed, Error %d\n", err);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	return err;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic void ngbe_disable_device(struct wx *wx)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	struct net_device *netdev = wx->netdev;
31362306a36Sopenharmony_ci	u32 i;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/* disable all enabled rx queues */
31662306a36Sopenharmony_ci	for (i = 0; i < wx->num_rx_queues; i++)
31762306a36Sopenharmony_ci		/* this call also flushes the previous write */
31862306a36Sopenharmony_ci		wx_disable_rx_queue(wx, wx->rx_ring[i]);
31962306a36Sopenharmony_ci	/* disable receives */
32062306a36Sopenharmony_ci	wx_disable_rx(wx);
32162306a36Sopenharmony_ci	wx_napi_disable_all(wx);
32262306a36Sopenharmony_ci	netif_tx_stop_all_queues(netdev);
32362306a36Sopenharmony_ci	netif_tx_disable(netdev);
32462306a36Sopenharmony_ci	if (wx->gpio_ctrl)
32562306a36Sopenharmony_ci		ngbe_sfp_modules_txrx_powerctl(wx, false);
32662306a36Sopenharmony_ci	wx_irq_disable(wx);
32762306a36Sopenharmony_ci	/* disable transmits in the hardware now that interrupts are off */
32862306a36Sopenharmony_ci	for (i = 0; i < wx->num_tx_queues; i++) {
32962306a36Sopenharmony_ci		u8 reg_idx = wx->tx_ring[i]->reg_idx;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci		wr32(wx, WX_PX_TR_CFG(reg_idx), WX_PX_TR_CFG_SWFLSH);
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic void ngbe_down(struct wx *wx)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	phy_stop(wx->phydev);
33862306a36Sopenharmony_ci	ngbe_disable_device(wx);
33962306a36Sopenharmony_ci	wx_clean_all_tx_rings(wx);
34062306a36Sopenharmony_ci	wx_clean_all_rx_rings(wx);
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic void ngbe_up(struct wx *wx)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	wx_configure_vectors(wx);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	/* make sure to complete pre-operations */
34862306a36Sopenharmony_ci	smp_mb__before_atomic();
34962306a36Sopenharmony_ci	wx_napi_enable_all(wx);
35062306a36Sopenharmony_ci	/* enable transmits */
35162306a36Sopenharmony_ci	netif_tx_start_all_queues(wx->netdev);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	/* clear any pending interrupts, may auto mask */
35462306a36Sopenharmony_ci	rd32(wx, WX_PX_IC(0));
35562306a36Sopenharmony_ci	rd32(wx, WX_PX_MISC_IC);
35662306a36Sopenharmony_ci	ngbe_irq_enable(wx, true);
35762306a36Sopenharmony_ci	if (wx->gpio_ctrl)
35862306a36Sopenharmony_ci		ngbe_sfp_modules_txrx_powerctl(wx, true);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	phy_start(wx->phydev);
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci/**
36462306a36Sopenharmony_ci * ngbe_open - Called when a network interface is made active
36562306a36Sopenharmony_ci * @netdev: network interface device structure
36662306a36Sopenharmony_ci *
36762306a36Sopenharmony_ci * Returns 0 on success, negative value on failure
36862306a36Sopenharmony_ci *
36962306a36Sopenharmony_ci * The open entry point is called when a network interface is made
37062306a36Sopenharmony_ci * active by the system (IFF_UP).
37162306a36Sopenharmony_ci **/
37262306a36Sopenharmony_cistatic int ngbe_open(struct net_device *netdev)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	struct wx *wx = netdev_priv(netdev);
37562306a36Sopenharmony_ci	int err;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	wx_control_hw(wx, true);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	err = wx_setup_resources(wx);
38062306a36Sopenharmony_ci	if (err)
38162306a36Sopenharmony_ci		return err;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	wx_configure(wx);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	err = ngbe_request_irq(wx);
38662306a36Sopenharmony_ci	if (err)
38762306a36Sopenharmony_ci		goto err_free_resources;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	err = ngbe_phy_connect(wx);
39062306a36Sopenharmony_ci	if (err)
39162306a36Sopenharmony_ci		goto err_free_irq;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	err = netif_set_real_num_tx_queues(netdev, wx->num_tx_queues);
39462306a36Sopenharmony_ci	if (err)
39562306a36Sopenharmony_ci		goto err_dis_phy;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	err = netif_set_real_num_rx_queues(netdev, wx->num_rx_queues);
39862306a36Sopenharmony_ci	if (err)
39962306a36Sopenharmony_ci		goto err_dis_phy;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	ngbe_up(wx);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	return 0;
40462306a36Sopenharmony_cierr_dis_phy:
40562306a36Sopenharmony_ci	phy_disconnect(wx->phydev);
40662306a36Sopenharmony_cierr_free_irq:
40762306a36Sopenharmony_ci	wx_free_irq(wx);
40862306a36Sopenharmony_cierr_free_resources:
40962306a36Sopenharmony_ci	wx_free_resources(wx);
41062306a36Sopenharmony_ci	return err;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci/**
41462306a36Sopenharmony_ci * ngbe_close - Disables a network interface
41562306a36Sopenharmony_ci * @netdev: network interface device structure
41662306a36Sopenharmony_ci *
41762306a36Sopenharmony_ci * Returns 0, this is not allowed to fail
41862306a36Sopenharmony_ci *
41962306a36Sopenharmony_ci * The close entry point is called when an interface is de-activated
42062306a36Sopenharmony_ci * by the OS.  The hardware is still under the drivers control, but
42162306a36Sopenharmony_ci * needs to be disabled.  A global MAC reset is issued to stop the
42262306a36Sopenharmony_ci * hardware, and all transmit and receive resources are freed.
42362306a36Sopenharmony_ci **/
42462306a36Sopenharmony_cistatic int ngbe_close(struct net_device *netdev)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct wx *wx = netdev_priv(netdev);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	ngbe_down(wx);
42962306a36Sopenharmony_ci	wx_free_irq(wx);
43062306a36Sopenharmony_ci	wx_free_resources(wx);
43162306a36Sopenharmony_ci	phy_disconnect(wx->phydev);
43262306a36Sopenharmony_ci	wx_control_hw(wx, false);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	return 0;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic void ngbe_dev_shutdown(struct pci_dev *pdev, bool *enable_wake)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	struct wx *wx = pci_get_drvdata(pdev);
44062306a36Sopenharmony_ci	struct net_device *netdev;
44162306a36Sopenharmony_ci	u32 wufc = wx->wol;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	netdev = wx->netdev;
44462306a36Sopenharmony_ci	rtnl_lock();
44562306a36Sopenharmony_ci	netif_device_detach(netdev);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	if (netif_running(netdev))
44862306a36Sopenharmony_ci		ngbe_close(netdev);
44962306a36Sopenharmony_ci	wx_clear_interrupt_scheme(wx);
45062306a36Sopenharmony_ci	rtnl_unlock();
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	if (wufc) {
45362306a36Sopenharmony_ci		wx_set_rx_mode(netdev);
45462306a36Sopenharmony_ci		wx_configure_rx(wx);
45562306a36Sopenharmony_ci		wr32(wx, NGBE_PSR_WKUP_CTL, wufc);
45662306a36Sopenharmony_ci	} else {
45762306a36Sopenharmony_ci		wr32(wx, NGBE_PSR_WKUP_CTL, 0);
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci	pci_wake_from_d3(pdev, !!wufc);
46062306a36Sopenharmony_ci	*enable_wake = !!wufc;
46162306a36Sopenharmony_ci	wx_control_hw(wx, false);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	pci_disable_device(pdev);
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic void ngbe_shutdown(struct pci_dev *pdev)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	struct wx *wx = pci_get_drvdata(pdev);
46962306a36Sopenharmony_ci	bool wake;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	wake = !!wx->wol;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	ngbe_dev_shutdown(pdev, &wake);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	if (system_state == SYSTEM_POWER_OFF) {
47662306a36Sopenharmony_ci		pci_wake_from_d3(pdev, wake);
47762306a36Sopenharmony_ci		pci_set_power_state(pdev, PCI_D3hot);
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_cistatic const struct net_device_ops ngbe_netdev_ops = {
48262306a36Sopenharmony_ci	.ndo_open               = ngbe_open,
48362306a36Sopenharmony_ci	.ndo_stop               = ngbe_close,
48462306a36Sopenharmony_ci	.ndo_change_mtu         = wx_change_mtu,
48562306a36Sopenharmony_ci	.ndo_start_xmit         = wx_xmit_frame,
48662306a36Sopenharmony_ci	.ndo_set_rx_mode        = wx_set_rx_mode,
48762306a36Sopenharmony_ci	.ndo_set_features       = wx_set_features,
48862306a36Sopenharmony_ci	.ndo_validate_addr      = eth_validate_addr,
48962306a36Sopenharmony_ci	.ndo_set_mac_address    = wx_set_mac,
49062306a36Sopenharmony_ci	.ndo_get_stats64        = wx_get_stats64,
49162306a36Sopenharmony_ci	.ndo_vlan_rx_add_vid    = wx_vlan_rx_add_vid,
49262306a36Sopenharmony_ci	.ndo_vlan_rx_kill_vid   = wx_vlan_rx_kill_vid,
49362306a36Sopenharmony_ci};
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci/**
49662306a36Sopenharmony_ci * ngbe_probe - Device Initialization Routine
49762306a36Sopenharmony_ci * @pdev: PCI device information struct
49862306a36Sopenharmony_ci * @ent: entry in ngbe_pci_tbl
49962306a36Sopenharmony_ci *
50062306a36Sopenharmony_ci * Returns 0 on success, negative on failure
50162306a36Sopenharmony_ci *
50262306a36Sopenharmony_ci * ngbe_probe initializes an wx identified by a pci_dev structure.
50362306a36Sopenharmony_ci * The OS initialization, configuring of the wx private structure,
50462306a36Sopenharmony_ci * and a hardware reset occur.
50562306a36Sopenharmony_ci **/
50662306a36Sopenharmony_cistatic int ngbe_probe(struct pci_dev *pdev,
50762306a36Sopenharmony_ci		      const struct pci_device_id __always_unused *ent)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	struct net_device *netdev;
51062306a36Sopenharmony_ci	u32 e2rom_cksum_cap = 0;
51162306a36Sopenharmony_ci	struct wx *wx = NULL;
51262306a36Sopenharmony_ci	static int func_nums;
51362306a36Sopenharmony_ci	u16 e2rom_ver = 0;
51462306a36Sopenharmony_ci	u32 etrack_id = 0;
51562306a36Sopenharmony_ci	u32 saved_ver = 0;
51662306a36Sopenharmony_ci	int err;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	err = pci_enable_device_mem(pdev);
51962306a36Sopenharmony_ci	if (err)
52062306a36Sopenharmony_ci		return err;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
52362306a36Sopenharmony_ci	if (err) {
52462306a36Sopenharmony_ci		dev_err(&pdev->dev,
52562306a36Sopenharmony_ci			"No usable DMA configuration, aborting\n");
52662306a36Sopenharmony_ci		goto err_pci_disable_dev;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	err = pci_request_selected_regions(pdev,
53062306a36Sopenharmony_ci					   pci_select_bars(pdev, IORESOURCE_MEM),
53162306a36Sopenharmony_ci					   ngbe_driver_name);
53262306a36Sopenharmony_ci	if (err) {
53362306a36Sopenharmony_ci		dev_err(&pdev->dev,
53462306a36Sopenharmony_ci			"pci_request_selected_regions failed %d\n", err);
53562306a36Sopenharmony_ci		goto err_pci_disable_dev;
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	pci_set_master(pdev);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	netdev = devm_alloc_etherdev_mqs(&pdev->dev,
54162306a36Sopenharmony_ci					 sizeof(struct wx),
54262306a36Sopenharmony_ci					 NGBE_MAX_TX_QUEUES,
54362306a36Sopenharmony_ci					 NGBE_MAX_RX_QUEUES);
54462306a36Sopenharmony_ci	if (!netdev) {
54562306a36Sopenharmony_ci		err = -ENOMEM;
54662306a36Sopenharmony_ci		goto err_pci_release_regions;
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	SET_NETDEV_DEV(netdev, &pdev->dev);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	wx = netdev_priv(netdev);
55262306a36Sopenharmony_ci	wx->netdev = netdev;
55362306a36Sopenharmony_ci	wx->pdev = pdev;
55462306a36Sopenharmony_ci	wx->msg_enable = BIT(3) - 1;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	wx->hw_addr = devm_ioremap(&pdev->dev,
55762306a36Sopenharmony_ci				   pci_resource_start(pdev, 0),
55862306a36Sopenharmony_ci				   pci_resource_len(pdev, 0));
55962306a36Sopenharmony_ci	if (!wx->hw_addr) {
56062306a36Sopenharmony_ci		err = -EIO;
56162306a36Sopenharmony_ci		goto err_pci_release_regions;
56262306a36Sopenharmony_ci	}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	wx->driver_name = ngbe_driver_name;
56562306a36Sopenharmony_ci	ngbe_set_ethtool_ops(netdev);
56662306a36Sopenharmony_ci	netdev->netdev_ops = &ngbe_netdev_ops;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	netdev->features = NETIF_F_SG | NETIF_F_IP_CSUM |
56962306a36Sopenharmony_ci			   NETIF_F_TSO | NETIF_F_TSO6 |
57062306a36Sopenharmony_ci			   NETIF_F_RXHASH | NETIF_F_RXCSUM;
57162306a36Sopenharmony_ci	netdev->features |= NETIF_F_SCTP_CRC | NETIF_F_TSO_MANGLEID;
57262306a36Sopenharmony_ci	netdev->vlan_features |= netdev->features;
57362306a36Sopenharmony_ci	netdev->features |= NETIF_F_IPV6_CSUM | NETIF_F_VLAN_FEATURES;
57462306a36Sopenharmony_ci	/* copy netdev features into list of user selectable features */
57562306a36Sopenharmony_ci	netdev->hw_features |= netdev->features | NETIF_F_RXALL;
57662306a36Sopenharmony_ci	netdev->hw_features |= NETIF_F_NTUPLE | NETIF_F_HW_TC;
57762306a36Sopenharmony_ci	netdev->features |= NETIF_F_HIGHDMA;
57862306a36Sopenharmony_ci	netdev->hw_features |= NETIF_F_GRO;
57962306a36Sopenharmony_ci	netdev->features |= NETIF_F_GRO;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	netdev->priv_flags |= IFF_UNICAST_FLT;
58262306a36Sopenharmony_ci	netdev->priv_flags |= IFF_SUPP_NOFCS;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	netdev->min_mtu = ETH_MIN_MTU;
58562306a36Sopenharmony_ci	netdev->max_mtu = WX_MAX_JUMBO_FRAME_SIZE -
58662306a36Sopenharmony_ci			  (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	wx->bd_number = func_nums;
58962306a36Sopenharmony_ci	/* setup the private structure */
59062306a36Sopenharmony_ci	err = ngbe_sw_init(wx);
59162306a36Sopenharmony_ci	if (err)
59262306a36Sopenharmony_ci		goto err_free_mac_table;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	/* check if flash load is done after hw power up */
59562306a36Sopenharmony_ci	err = wx_check_flash_load(wx, NGBE_SPI_ILDR_STATUS_PERST);
59662306a36Sopenharmony_ci	if (err)
59762306a36Sopenharmony_ci		goto err_free_mac_table;
59862306a36Sopenharmony_ci	err = wx_check_flash_load(wx, NGBE_SPI_ILDR_STATUS_PWRRST);
59962306a36Sopenharmony_ci	if (err)
60062306a36Sopenharmony_ci		goto err_free_mac_table;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	err = wx_mng_present(wx);
60362306a36Sopenharmony_ci	if (err) {
60462306a36Sopenharmony_ci		dev_err(&pdev->dev, "Management capability is not present\n");
60562306a36Sopenharmony_ci		goto err_free_mac_table;
60662306a36Sopenharmony_ci	}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	err = ngbe_reset_hw(wx);
60962306a36Sopenharmony_ci	if (err) {
61062306a36Sopenharmony_ci		dev_err(&pdev->dev, "HW Init failed: %d\n", err);
61162306a36Sopenharmony_ci		goto err_free_mac_table;
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	if (wx->bus.func == 0) {
61562306a36Sopenharmony_ci		wr32(wx, NGBE_CALSUM_CAP_STATUS, 0x0);
61662306a36Sopenharmony_ci		wr32(wx, NGBE_EEPROM_VERSION_STORE_REG, 0x0);
61762306a36Sopenharmony_ci	} else {
61862306a36Sopenharmony_ci		e2rom_cksum_cap = rd32(wx, NGBE_CALSUM_CAP_STATUS);
61962306a36Sopenharmony_ci		saved_ver = rd32(wx, NGBE_EEPROM_VERSION_STORE_REG);
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	wx_init_eeprom_params(wx);
62362306a36Sopenharmony_ci	if (wx->bus.func == 0 || e2rom_cksum_cap == 0) {
62462306a36Sopenharmony_ci		/* make sure the EEPROM is ready */
62562306a36Sopenharmony_ci		err = ngbe_eeprom_chksum_hostif(wx);
62662306a36Sopenharmony_ci		if (err) {
62762306a36Sopenharmony_ci			dev_err(&pdev->dev, "The EEPROM Checksum Is Not Valid\n");
62862306a36Sopenharmony_ci			err = -EIO;
62962306a36Sopenharmony_ci			goto err_free_mac_table;
63062306a36Sopenharmony_ci		}
63162306a36Sopenharmony_ci	}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	wx->wol = 0;
63462306a36Sopenharmony_ci	if (wx->wol_hw_supported)
63562306a36Sopenharmony_ci		wx->wol = NGBE_PSR_WKUP_CTL_MAG;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	netdev->wol_enabled = !!(wx->wol);
63862306a36Sopenharmony_ci	wr32(wx, NGBE_PSR_WKUP_CTL, wx->wol);
63962306a36Sopenharmony_ci	device_set_wakeup_enable(&pdev->dev, wx->wol);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	/* Save off EEPROM version number and Option Rom version which
64262306a36Sopenharmony_ci	 * together make a unique identify for the eeprom
64362306a36Sopenharmony_ci	 */
64462306a36Sopenharmony_ci	if (saved_ver) {
64562306a36Sopenharmony_ci		etrack_id = saved_ver;
64662306a36Sopenharmony_ci	} else {
64762306a36Sopenharmony_ci		wx_read_ee_hostif(wx,
64862306a36Sopenharmony_ci				  wx->eeprom.sw_region_offset + NGBE_EEPROM_VERSION_H,
64962306a36Sopenharmony_ci				  &e2rom_ver);
65062306a36Sopenharmony_ci		etrack_id = e2rom_ver << 16;
65162306a36Sopenharmony_ci		wx_read_ee_hostif(wx,
65262306a36Sopenharmony_ci				  wx->eeprom.sw_region_offset + NGBE_EEPROM_VERSION_L,
65362306a36Sopenharmony_ci				  &e2rom_ver);
65462306a36Sopenharmony_ci		etrack_id |= e2rom_ver;
65562306a36Sopenharmony_ci		wr32(wx, NGBE_EEPROM_VERSION_STORE_REG, etrack_id);
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci	snprintf(wx->eeprom_id, sizeof(wx->eeprom_id),
65862306a36Sopenharmony_ci		 "0x%08x", etrack_id);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	eth_hw_addr_set(netdev, wx->mac.perm_addr);
66162306a36Sopenharmony_ci	wx_mac_set_default_filter(wx, wx->mac.perm_addr);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	err = wx_init_interrupt_scheme(wx);
66462306a36Sopenharmony_ci	if (err)
66562306a36Sopenharmony_ci		goto err_free_mac_table;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	/* phy Interface Configuration */
66862306a36Sopenharmony_ci	err = ngbe_mdio_init(wx);
66962306a36Sopenharmony_ci	if (err)
67062306a36Sopenharmony_ci		goto err_clear_interrupt_scheme;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	err = register_netdev(netdev);
67362306a36Sopenharmony_ci	if (err)
67462306a36Sopenharmony_ci		goto err_register;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	pci_set_drvdata(pdev, wx);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	netif_info(wx, probe, netdev,
67962306a36Sopenharmony_ci		   "PHY: %s, PBA No: Wang Xun GbE Family Controller\n",
68062306a36Sopenharmony_ci		   wx->mac_type == em_mac_type_mdi ? "Internal" : "External");
68162306a36Sopenharmony_ci	netif_info(wx, probe, netdev, "%pM\n", netdev->dev_addr);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	return 0;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_cierr_register:
68662306a36Sopenharmony_ci	wx_control_hw(wx, false);
68762306a36Sopenharmony_cierr_clear_interrupt_scheme:
68862306a36Sopenharmony_ci	wx_clear_interrupt_scheme(wx);
68962306a36Sopenharmony_cierr_free_mac_table:
69062306a36Sopenharmony_ci	kfree(wx->mac_table);
69162306a36Sopenharmony_cierr_pci_release_regions:
69262306a36Sopenharmony_ci	pci_release_selected_regions(pdev,
69362306a36Sopenharmony_ci				     pci_select_bars(pdev, IORESOURCE_MEM));
69462306a36Sopenharmony_cierr_pci_disable_dev:
69562306a36Sopenharmony_ci	pci_disable_device(pdev);
69662306a36Sopenharmony_ci	return err;
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci/**
70062306a36Sopenharmony_ci * ngbe_remove - Device Removal Routine
70162306a36Sopenharmony_ci * @pdev: PCI device information struct
70262306a36Sopenharmony_ci *
70362306a36Sopenharmony_ci * ngbe_remove is called by the PCI subsystem to alert the driver
70462306a36Sopenharmony_ci * that it should release a PCI device.  The could be caused by a
70562306a36Sopenharmony_ci * Hot-Plug event, or because the driver is going to be removed from
70662306a36Sopenharmony_ci * memory.
70762306a36Sopenharmony_ci **/
70862306a36Sopenharmony_cistatic void ngbe_remove(struct pci_dev *pdev)
70962306a36Sopenharmony_ci{
71062306a36Sopenharmony_ci	struct wx *wx = pci_get_drvdata(pdev);
71162306a36Sopenharmony_ci	struct net_device *netdev;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	netdev = wx->netdev;
71462306a36Sopenharmony_ci	unregister_netdev(netdev);
71562306a36Sopenharmony_ci	pci_release_selected_regions(pdev,
71662306a36Sopenharmony_ci				     pci_select_bars(pdev, IORESOURCE_MEM));
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	kfree(wx->mac_table);
71962306a36Sopenharmony_ci	wx_clear_interrupt_scheme(wx);
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	pci_disable_device(pdev);
72262306a36Sopenharmony_ci}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_cistatic int ngbe_suspend(struct pci_dev *pdev, pm_message_t state)
72562306a36Sopenharmony_ci{
72662306a36Sopenharmony_ci	bool wake;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	ngbe_dev_shutdown(pdev, &wake);
72962306a36Sopenharmony_ci	device_set_wakeup_enable(&pdev->dev, wake);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	return 0;
73262306a36Sopenharmony_ci}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_cistatic int ngbe_resume(struct pci_dev *pdev)
73562306a36Sopenharmony_ci{
73662306a36Sopenharmony_ci	struct net_device *netdev;
73762306a36Sopenharmony_ci	struct wx *wx;
73862306a36Sopenharmony_ci	u32 err;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	wx = pci_get_drvdata(pdev);
74162306a36Sopenharmony_ci	netdev = wx->netdev;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	err = pci_enable_device_mem(pdev);
74462306a36Sopenharmony_ci	if (err) {
74562306a36Sopenharmony_ci		wx_err(wx, "Cannot enable PCI device from suspend\n");
74662306a36Sopenharmony_ci		return err;
74762306a36Sopenharmony_ci	}
74862306a36Sopenharmony_ci	pci_set_master(pdev);
74962306a36Sopenharmony_ci	device_wakeup_disable(&pdev->dev);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	ngbe_reset_hw(wx);
75262306a36Sopenharmony_ci	rtnl_lock();
75362306a36Sopenharmony_ci	err = wx_init_interrupt_scheme(wx);
75462306a36Sopenharmony_ci	if (!err && netif_running(netdev))
75562306a36Sopenharmony_ci		err = ngbe_open(netdev);
75662306a36Sopenharmony_ci	if (!err)
75762306a36Sopenharmony_ci		netif_device_attach(netdev);
75862306a36Sopenharmony_ci	rtnl_unlock();
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	return 0;
76162306a36Sopenharmony_ci}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_cistatic struct pci_driver ngbe_driver = {
76462306a36Sopenharmony_ci	.name     = ngbe_driver_name,
76562306a36Sopenharmony_ci	.id_table = ngbe_pci_tbl,
76662306a36Sopenharmony_ci	.probe    = ngbe_probe,
76762306a36Sopenharmony_ci	.remove   = ngbe_remove,
76862306a36Sopenharmony_ci	.suspend  = ngbe_suspend,
76962306a36Sopenharmony_ci	.resume   = ngbe_resume,
77062306a36Sopenharmony_ci	.shutdown = ngbe_shutdown,
77162306a36Sopenharmony_ci};
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_cimodule_pci_driver(ngbe_driver);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ngbe_pci_tbl);
77662306a36Sopenharmony_ciMODULE_AUTHOR("Beijing WangXun Technology Co., Ltd, <software@net-swift.com>");
77762306a36Sopenharmony_ciMODULE_DESCRIPTION("WangXun(R) Gigabit PCI Express Network Driver");
77862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
779