1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00
linux/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
Jiawen Wu ac71ab7816 net: wangxun: match VLAN CTAG and STAG features
Hardware requires VLAN CTAG and STAG configuration always matches. And
whether VLAN CTAG or STAG changes, the configuration needs to be changed
as well.

Fixes: 6670f1ece2 ("net: txgbe: Add netdev features support")
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
Reviewed-by: Sai Krishna <saikrishnag@marvell.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2024-05-17 10:17:36 +01:00

792 lines
19 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2019 - 2022 Beijing WangXun Technology Co., Ltd. */
#include <linux/types.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/string.h>
#include <linux/etherdevice.h>
#include <net/ip.h>
#include <linux/phy.h>
#include <linux/if_vlan.h>
#include "../libwx/wx_type.h"
#include "../libwx/wx_hw.h"
#include "../libwx/wx_lib.h"
#include "ngbe_type.h"
#include "ngbe_mdio.h"
#include "ngbe_hw.h"
#include "ngbe_ethtool.h"
char ngbe_driver_name[] = "ngbe";
/* ngbe_pci_tbl - PCI Device ID Table
*
* { Vendor ID, Device ID, SubVendor ID, SubDevice ID,
* Class, Class Mask, private data (not used) }
*/
static const struct pci_device_id ngbe_pci_tbl[] = {
{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL_W), 0},
{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A2), 0},
{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A2S), 0},
{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A4), 0},
{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A4S), 0},
{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL2), 0},
{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL2S), 0},
{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL4), 0},
{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860AL4S), 0},
{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860LC), 0},
{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A1), 0},
{ PCI_VDEVICE(WANGXUN, NGBE_DEV_ID_EM_WX1860A1L), 0},
/* required last entry */
{ .device = 0 }
};
/**
* ngbe_init_type_code - Initialize the shared code
* @wx: pointer to hardware structure
**/
static void ngbe_init_type_code(struct wx *wx)
{
int wol_mask = 0, ncsi_mask = 0;
u16 type_mask = 0, val;
wx->mac.type = wx_mac_em;
type_mask = (u16)(wx->subsystem_device_id & NGBE_OEM_MASK);
ncsi_mask = wx->subsystem_device_id & NGBE_NCSI_MASK;
wol_mask = wx->subsystem_device_id & NGBE_WOL_MASK;
val = rd32(wx, WX_CFG_PORT_ST);
wx->mac_type = (val & BIT(7)) >> 7 ?
em_mac_type_rgmii :
em_mac_type_mdi;
wx->wol_hw_supported = (wol_mask == NGBE_WOL_SUP) ? 1 : 0;
wx->ncsi_enabled = (ncsi_mask == NGBE_NCSI_MASK ||
type_mask == NGBE_SUBID_OCP_CARD) ? 1 : 0;
switch (type_mask) {
case NGBE_SUBID_LY_YT8521S_SFP:
case NGBE_SUBID_LY_M88E1512_SFP:
case NGBE_SUBID_YT8521S_SFP_GPIO:
case NGBE_SUBID_INTERNAL_YT8521S_SFP_GPIO:
wx->gpio_ctrl = 1;
break;
default:
wx->gpio_ctrl = 0;
break;
}
}
/**
* ngbe_sw_init - Initialize general software structures
* @wx: board private structure to initialize
**/
static int ngbe_sw_init(struct wx *wx)
{
struct pci_dev *pdev = wx->pdev;
u16 msix_count = 0;
int err = 0;
wx->mac.num_rar_entries = NGBE_RAR_ENTRIES;
wx->mac.max_rx_queues = NGBE_MAX_RX_QUEUES;
wx->mac.max_tx_queues = NGBE_MAX_TX_QUEUES;
wx->mac.mcft_size = NGBE_MC_TBL_SIZE;
wx->mac.vft_size = NGBE_SP_VFT_TBL_SIZE;
wx->mac.rx_pb_size = NGBE_RX_PB_SIZE;
wx->mac.tx_pb_size = NGBE_TDB_PB_SZ;
/* PCI config space info */
err = wx_sw_init(wx);
if (err < 0)
return err;
/* mac type, phy type , oem type */
ngbe_init_type_code(wx);
/* Set common capability flags and settings */
wx->max_q_vectors = NGBE_MAX_MSIX_VECTORS;
err = wx_get_pcie_msix_counts(wx, &msix_count, NGBE_MAX_MSIX_VECTORS);
if (err)
dev_err(&pdev->dev, "Do not support MSI-X\n");
wx->mac.max_msix_vectors = msix_count;
wx->ring_feature[RING_F_RSS].limit = min_t(int, NGBE_MAX_RSS_INDICES,
num_online_cpus());
wx->rss_enabled = true;
/* enable itr by default in dynamic mode */
wx->rx_itr_setting = 1;
wx->tx_itr_setting = 1;
/* set default ring sizes */
wx->tx_ring_count = NGBE_DEFAULT_TXD;
wx->rx_ring_count = NGBE_DEFAULT_RXD;
/* set default work limits */
wx->tx_work_limit = NGBE_DEFAULT_TX_WORK;
wx->rx_work_limit = NGBE_DEFAULT_RX_WORK;
return 0;
}
/**
* ngbe_irq_enable - Enable default interrupt generation settings
* @wx: board private structure
* @queues: enable all queues interrupts
**/
static void ngbe_irq_enable(struct wx *wx, bool queues)
{
u32 mask;
/* enable misc interrupt */
mask = NGBE_PX_MISC_IEN_MASK;
wr32(wx, WX_GPIO_DDR, WX_GPIO_DDR_0);
wr32(wx, WX_GPIO_INTEN, WX_GPIO_INTEN_0 | WX_GPIO_INTEN_1);
wr32(wx, WX_GPIO_INTTYPE_LEVEL, 0x0);
wr32(wx, WX_GPIO_POLARITY, wx->gpio_ctrl ? 0 : 0x3);
wr32(wx, WX_PX_MISC_IEN, mask);
/* mask interrupt */
if (queues)
wx_intr_enable(wx, NGBE_INTR_ALL);
else
wx_intr_enable(wx, NGBE_INTR_MISC);
}
/**
* ngbe_intr - msi/legacy mode Interrupt Handler
* @irq: interrupt number
* @data: pointer to a network interface device structure
**/
static irqreturn_t ngbe_intr(int __always_unused irq, void *data)
{
struct wx_q_vector *q_vector;
struct wx *wx = data;
struct pci_dev *pdev;
u32 eicr;
q_vector = wx->q_vector[0];
pdev = wx->pdev;
eicr = wx_misc_isb(wx, WX_ISB_VEC0);
if (!eicr) {
/* shared interrupt alert!
* the interrupt that we masked before the EICR read.
*/
if (netif_running(wx->netdev))
ngbe_irq_enable(wx, true);
return IRQ_NONE; /* Not our interrupt */
}
wx->isb_mem[WX_ISB_VEC0] = 0;
if (!(pdev->msi_enabled))
wr32(wx, WX_PX_INTA, 1);
wx->isb_mem[WX_ISB_MISC] = 0;
/* would disable interrupts here but it is auto disabled */
napi_schedule_irqoff(&q_vector->napi);
if (netif_running(wx->netdev))
ngbe_irq_enable(wx, false);
return IRQ_HANDLED;
}
static irqreturn_t ngbe_msix_other(int __always_unused irq, void *data)
{
struct wx *wx = data;
/* re-enable the original interrupt state, no lsc, no queues */
if (netif_running(wx->netdev))
ngbe_irq_enable(wx, false);
return IRQ_HANDLED;
}
/**
* ngbe_request_msix_irqs - Initialize MSI-X interrupts
* @wx: board private structure
*
* ngbe_request_msix_irqs allocates MSI-X vectors and requests
* interrupts from the kernel.
**/
static int ngbe_request_msix_irqs(struct wx *wx)
{
struct net_device *netdev = wx->netdev;
int vector, err;
for (vector = 0; vector < wx->num_q_vectors; vector++) {
struct wx_q_vector *q_vector = wx->q_vector[vector];
struct msix_entry *entry = &wx->msix_q_entries[vector];
if (q_vector->tx.ring && q_vector->rx.ring)
snprintf(q_vector->name, sizeof(q_vector->name) - 1,
"%s-TxRx-%d", netdev->name, entry->entry);
else
/* skip this unused q_vector */
continue;
err = request_irq(entry->vector, wx_msix_clean_rings, 0,
q_vector->name, q_vector);
if (err) {
wx_err(wx, "request_irq failed for MSIX interrupt %s Error: %d\n",
q_vector->name, err);
goto free_queue_irqs;
}
}
err = request_irq(wx->msix_entry->vector,
ngbe_msix_other, 0, netdev->name, wx);
if (err) {
wx_err(wx, "request_irq for msix_other failed: %d\n", err);
goto free_queue_irqs;
}
return 0;
free_queue_irqs:
while (vector) {
vector--;
free_irq(wx->msix_q_entries[vector].vector,
wx->q_vector[vector]);
}
wx_reset_interrupt_capability(wx);
return err;
}
/**
* ngbe_request_irq - initialize interrupts
* @wx: board private structure
*
* Attempts to configure interrupts using the best available
* capabilities of the hardware and kernel.
**/
static int ngbe_request_irq(struct wx *wx)
{
struct net_device *netdev = wx->netdev;
struct pci_dev *pdev = wx->pdev;
int err;
if (pdev->msix_enabled)
err = ngbe_request_msix_irqs(wx);
else if (pdev->msi_enabled)
err = request_irq(pdev->irq, ngbe_intr, 0,
netdev->name, wx);
else
err = request_irq(pdev->irq, ngbe_intr, IRQF_SHARED,
netdev->name, wx);
if (err)
wx_err(wx, "request_irq failed, Error %d\n", err);
return err;
}
static void ngbe_disable_device(struct wx *wx)
{
struct net_device *netdev = wx->netdev;
u32 i;
/* disable all enabled rx queues */
for (i = 0; i < wx->num_rx_queues; i++)
/* this call also flushes the previous write */
wx_disable_rx_queue(wx, wx->rx_ring[i]);
/* disable receives */
wx_disable_rx(wx);
wx_napi_disable_all(wx);
netif_tx_stop_all_queues(netdev);
netif_tx_disable(netdev);
if (wx->gpio_ctrl)
ngbe_sfp_modules_txrx_powerctl(wx, false);
wx_irq_disable(wx);
/* disable transmits in the hardware now that interrupts are off */
for (i = 0; i < wx->num_tx_queues; i++) {
u8 reg_idx = wx->tx_ring[i]->reg_idx;
wr32(wx, WX_PX_TR_CFG(reg_idx), WX_PX_TR_CFG_SWFLSH);
}
wx_update_stats(wx);
}
void ngbe_down(struct wx *wx)
{
phylink_stop(wx->phylink);
ngbe_disable_device(wx);
wx_clean_all_tx_rings(wx);
wx_clean_all_rx_rings(wx);
}
void ngbe_up(struct wx *wx)
{
wx_configure_vectors(wx);
/* make sure to complete pre-operations */
smp_mb__before_atomic();
wx_napi_enable_all(wx);
/* enable transmits */
netif_tx_start_all_queues(wx->netdev);
/* clear any pending interrupts, may auto mask */
rd32(wx, WX_PX_IC(0));
rd32(wx, WX_PX_MISC_IC);
ngbe_irq_enable(wx, true);
if (wx->gpio_ctrl)
ngbe_sfp_modules_txrx_powerctl(wx, true);
phylink_start(wx->phylink);
}
/**
* ngbe_open - Called when a network interface is made active
* @netdev: network interface device structure
*
* Returns 0 on success, negative value on failure
*
* The open entry point is called when a network interface is made
* active by the system (IFF_UP).
**/
static int ngbe_open(struct net_device *netdev)
{
struct wx *wx = netdev_priv(netdev);
int err;
wx_control_hw(wx, true);
err = wx_setup_resources(wx);
if (err)
return err;
wx_configure(wx);
err = ngbe_request_irq(wx);
if (err)
goto err_free_resources;
err = phylink_connect_phy(wx->phylink, wx->phydev);
if (err)
goto err_free_irq;
err = netif_set_real_num_tx_queues(netdev, wx->num_tx_queues);
if (err)
goto err_dis_phy;
err = netif_set_real_num_rx_queues(netdev, wx->num_rx_queues);
if (err)
goto err_dis_phy;
ngbe_up(wx);
return 0;
err_dis_phy:
phylink_disconnect_phy(wx->phylink);
err_free_irq:
wx_free_irq(wx);
err_free_resources:
wx_free_resources(wx);
return err;
}
/**
* ngbe_close - Disables a network interface
* @netdev: network interface device structure
*
* Returns 0, this is not allowed to fail
*
* The close entry point is called when an interface is de-activated
* by the OS. The hardware is still under the drivers control, but
* needs to be disabled. A global MAC reset is issued to stop the
* hardware, and all transmit and receive resources are freed.
**/
static int ngbe_close(struct net_device *netdev)
{
struct wx *wx = netdev_priv(netdev);
ngbe_down(wx);
wx_free_irq(wx);
wx_free_resources(wx);
phylink_disconnect_phy(wx->phylink);
wx_control_hw(wx, false);
return 0;
}
static void ngbe_dev_shutdown(struct pci_dev *pdev, bool *enable_wake)
{
struct wx *wx = pci_get_drvdata(pdev);
struct net_device *netdev;
u32 wufc = wx->wol;
netdev = wx->netdev;
rtnl_lock();
netif_device_detach(netdev);
if (netif_running(netdev))
ngbe_close(netdev);
wx_clear_interrupt_scheme(wx);
rtnl_unlock();
if (wufc) {
wx_set_rx_mode(netdev);
wx_configure_rx(wx);
wr32(wx, NGBE_PSR_WKUP_CTL, wufc);
} else {
wr32(wx, NGBE_PSR_WKUP_CTL, 0);
}
pci_wake_from_d3(pdev, !!wufc);
*enable_wake = !!wufc;
wx_control_hw(wx, false);
pci_disable_device(pdev);
}
static void ngbe_shutdown(struct pci_dev *pdev)
{
struct wx *wx = pci_get_drvdata(pdev);
bool wake;
wake = !!wx->wol;
ngbe_dev_shutdown(pdev, &wake);
if (system_state == SYSTEM_POWER_OFF) {
pci_wake_from_d3(pdev, wake);
pci_set_power_state(pdev, PCI_D3hot);
}
}
/**
* ngbe_setup_tc - routine to configure net_device for multiple traffic
* classes.
*
* @dev: net device to configure
* @tc: number of traffic classes to enable
*/
int ngbe_setup_tc(struct net_device *dev, u8 tc)
{
struct wx *wx = netdev_priv(dev);
/* Hardware has to reinitialize queues and interrupts to
* match packet buffer alignment. Unfortunately, the
* hardware is not flexible enough to do this dynamically.
*/
if (netif_running(dev))
ngbe_close(dev);
wx_clear_interrupt_scheme(wx);
if (tc)
netdev_set_num_tc(dev, tc);
else
netdev_reset_tc(dev);
wx_init_interrupt_scheme(wx);
if (netif_running(dev))
ngbe_open(dev);
return 0;
}
static const struct net_device_ops ngbe_netdev_ops = {
.ndo_open = ngbe_open,
.ndo_stop = ngbe_close,
.ndo_change_mtu = wx_change_mtu,
.ndo_start_xmit = wx_xmit_frame,
.ndo_set_rx_mode = wx_set_rx_mode,
.ndo_set_features = wx_set_features,
.ndo_fix_features = wx_fix_features,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = wx_set_mac,
.ndo_get_stats64 = wx_get_stats64,
.ndo_vlan_rx_add_vid = wx_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = wx_vlan_rx_kill_vid,
};
/**
* ngbe_probe - Device Initialization Routine
* @pdev: PCI device information struct
* @ent: entry in ngbe_pci_tbl
*
* Returns 0 on success, negative on failure
*
* ngbe_probe initializes an wx identified by a pci_dev structure.
* The OS initialization, configuring of the wx private structure,
* and a hardware reset occur.
**/
static int ngbe_probe(struct pci_dev *pdev,
const struct pci_device_id __always_unused *ent)
{
struct net_device *netdev;
u32 e2rom_cksum_cap = 0;
struct wx *wx = NULL;
static int func_nums;
u16 e2rom_ver = 0;
u32 etrack_id = 0;
u32 saved_ver = 0;
int err;
err = pci_enable_device_mem(pdev);
if (err)
return err;
err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (err) {
dev_err(&pdev->dev,
"No usable DMA configuration, aborting\n");
goto err_pci_disable_dev;
}
err = pci_request_selected_regions(pdev,
pci_select_bars(pdev, IORESOURCE_MEM),
ngbe_driver_name);
if (err) {
dev_err(&pdev->dev,
"pci_request_selected_regions failed %d\n", err);
goto err_pci_disable_dev;
}
pci_set_master(pdev);
netdev = devm_alloc_etherdev_mqs(&pdev->dev,
sizeof(struct wx),
NGBE_MAX_TX_QUEUES,
NGBE_MAX_RX_QUEUES);
if (!netdev) {
err = -ENOMEM;
goto err_pci_release_regions;
}
SET_NETDEV_DEV(netdev, &pdev->dev);
wx = netdev_priv(netdev);
wx->netdev = netdev;
wx->pdev = pdev;
wx->msg_enable = BIT(3) - 1;
wx->hw_addr = devm_ioremap(&pdev->dev,
pci_resource_start(pdev, 0),
pci_resource_len(pdev, 0));
if (!wx->hw_addr) {
err = -EIO;
goto err_pci_release_regions;
}
wx->driver_name = ngbe_driver_name;
ngbe_set_ethtool_ops(netdev);
netdev->netdev_ops = &ngbe_netdev_ops;
netdev->features = NETIF_F_SG | NETIF_F_IP_CSUM |
NETIF_F_TSO | NETIF_F_TSO6 |
NETIF_F_RXHASH | NETIF_F_RXCSUM;
netdev->features |= NETIF_F_SCTP_CRC | NETIF_F_TSO_MANGLEID;
netdev->vlan_features |= netdev->features;
netdev->features |= NETIF_F_IPV6_CSUM | NETIF_F_VLAN_FEATURES;
/* copy netdev features into list of user selectable features */
netdev->hw_features |= netdev->features | NETIF_F_RXALL;
netdev->hw_features |= NETIF_F_NTUPLE | NETIF_F_HW_TC;
netdev->features |= NETIF_F_HIGHDMA;
netdev->hw_features |= NETIF_F_GRO;
netdev->features |= NETIF_F_GRO;
netdev->priv_flags |= IFF_UNICAST_FLT;
netdev->priv_flags |= IFF_SUPP_NOFCS;
netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
netdev->min_mtu = ETH_MIN_MTU;
netdev->max_mtu = WX_MAX_JUMBO_FRAME_SIZE -
(ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN);
wx->bd_number = func_nums;
/* setup the private structure */
err = ngbe_sw_init(wx);
if (err)
goto err_free_mac_table;
/* check if flash load is done after hw power up */
err = wx_check_flash_load(wx, NGBE_SPI_ILDR_STATUS_PERST);
if (err)
goto err_free_mac_table;
err = wx_check_flash_load(wx, NGBE_SPI_ILDR_STATUS_PWRRST);
if (err)
goto err_free_mac_table;
err = wx_mng_present(wx);
if (err) {
dev_err(&pdev->dev, "Management capability is not present\n");
goto err_free_mac_table;
}
err = ngbe_reset_hw(wx);
if (err) {
dev_err(&pdev->dev, "HW Init failed: %d\n", err);
goto err_free_mac_table;
}
if (wx->bus.func == 0) {
wr32(wx, NGBE_CALSUM_CAP_STATUS, 0x0);
wr32(wx, NGBE_EEPROM_VERSION_STORE_REG, 0x0);
} else {
e2rom_cksum_cap = rd32(wx, NGBE_CALSUM_CAP_STATUS);
saved_ver = rd32(wx, NGBE_EEPROM_VERSION_STORE_REG);
}
wx_init_eeprom_params(wx);
if (wx->bus.func == 0 || e2rom_cksum_cap == 0) {
/* make sure the EEPROM is ready */
err = ngbe_eeprom_chksum_hostif(wx);
if (err) {
dev_err(&pdev->dev, "The EEPROM Checksum Is Not Valid\n");
err = -EIO;
goto err_free_mac_table;
}
}
wx->wol = 0;
if (wx->wol_hw_supported)
wx->wol = NGBE_PSR_WKUP_CTL_MAG;
netdev->wol_enabled = !!(wx->wol);
wr32(wx, NGBE_PSR_WKUP_CTL, wx->wol);
device_set_wakeup_enable(&pdev->dev, wx->wol);
/* Save off EEPROM version number and Option Rom version which
* together make a unique identify for the eeprom
*/
if (saved_ver) {
etrack_id = saved_ver;
} else {
wx_read_ee_hostif(wx,
wx->eeprom.sw_region_offset + NGBE_EEPROM_VERSION_H,
&e2rom_ver);
etrack_id = e2rom_ver << 16;
wx_read_ee_hostif(wx,
wx->eeprom.sw_region_offset + NGBE_EEPROM_VERSION_L,
&e2rom_ver);
etrack_id |= e2rom_ver;
wr32(wx, NGBE_EEPROM_VERSION_STORE_REG, etrack_id);
}
snprintf(wx->eeprom_id, sizeof(wx->eeprom_id),
"0x%08x", etrack_id);
eth_hw_addr_set(netdev, wx->mac.perm_addr);
wx_mac_set_default_filter(wx, wx->mac.perm_addr);
err = wx_init_interrupt_scheme(wx);
if (err)
goto err_free_mac_table;
/* phy Interface Configuration */
err = ngbe_mdio_init(wx);
if (err)
goto err_clear_interrupt_scheme;
err = register_netdev(netdev);
if (err)
goto err_register;
pci_set_drvdata(pdev, wx);
return 0;
err_register:
phylink_destroy(wx->phylink);
wx_control_hw(wx, false);
err_clear_interrupt_scheme:
wx_clear_interrupt_scheme(wx);
err_free_mac_table:
kfree(wx->mac_table);
err_pci_release_regions:
pci_release_selected_regions(pdev,
pci_select_bars(pdev, IORESOURCE_MEM));
err_pci_disable_dev:
pci_disable_device(pdev);
return err;
}
/**
* ngbe_remove - Device Removal Routine
* @pdev: PCI device information struct
*
* ngbe_remove is called by the PCI subsystem to alert the driver
* that it should release a PCI device. The could be caused by a
* Hot-Plug event, or because the driver is going to be removed from
* memory.
**/
static void ngbe_remove(struct pci_dev *pdev)
{
struct wx *wx = pci_get_drvdata(pdev);
struct net_device *netdev;
netdev = wx->netdev;
unregister_netdev(netdev);
phylink_destroy(wx->phylink);
pci_release_selected_regions(pdev,
pci_select_bars(pdev, IORESOURCE_MEM));
kfree(wx->rss_key);
kfree(wx->mac_table);
wx_clear_interrupt_scheme(wx);
pci_disable_device(pdev);
}
static int ngbe_suspend(struct pci_dev *pdev, pm_message_t state)
{
bool wake;
ngbe_dev_shutdown(pdev, &wake);
device_set_wakeup_enable(&pdev->dev, wake);
return 0;
}
static int ngbe_resume(struct pci_dev *pdev)
{
struct net_device *netdev;
struct wx *wx;
u32 err;
wx = pci_get_drvdata(pdev);
netdev = wx->netdev;
err = pci_enable_device_mem(pdev);
if (err) {
wx_err(wx, "Cannot enable PCI device from suspend\n");
return err;
}
pci_set_master(pdev);
device_wakeup_disable(&pdev->dev);
ngbe_reset_hw(wx);
rtnl_lock();
err = wx_init_interrupt_scheme(wx);
if (!err && netif_running(netdev))
err = ngbe_open(netdev);
if (!err)
netif_device_attach(netdev);
rtnl_unlock();
return 0;
}
static struct pci_driver ngbe_driver = {
.name = ngbe_driver_name,
.id_table = ngbe_pci_tbl,
.probe = ngbe_probe,
.remove = ngbe_remove,
.suspend = ngbe_suspend,
.resume = ngbe_resume,
.shutdown = ngbe_shutdown,
};
module_pci_driver(ngbe_driver);
MODULE_DEVICE_TABLE(pci, ngbe_pci_tbl);
MODULE_AUTHOR("Beijing WangXun Technology Co., Ltd, <software@net-swift.com>");
MODULE_DESCRIPTION("WangXun(R) Gigabit PCI Express Network Driver");
MODULE_LICENSE("GPL");