Remove duplicate-defined register macros, move the WOL implementation to the library module. So that the WOL functions can be reused in txgbe later. Signed-off-by: Jiawen Wu --- .../net/ethernet/wangxun/libwx/wx_ethtool.c | 34 ++++++++++++++++++ .../net/ethernet/wangxun/libwx/wx_ethtool.h | 4 +++ .../net/ethernet/wangxun/ngbe/ngbe_ethtool.c | 36 ++----------------- drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 12 +++---- drivers/net/ethernet/wangxun/ngbe/ngbe_type.h | 14 -------- 5 files changed, 46 insertions(+), 54 deletions(-) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c index f362e51c73ee..9e940ea9cb8b 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c @@ -262,6 +262,40 @@ int wx_set_link_ksettings(struct net_device *netdev, } EXPORT_SYMBOL(wx_set_link_ksettings); +void wx_get_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol) +{ + struct wx *wx = netdev_priv(netdev); + + if (!wx->wol_hw_supported) + return; + wol->supported = WAKE_MAGIC; + wol->wolopts = 0; + if (wx->wol & WX_PSR_WKUP_CTL_MAG) + wol->wolopts |= WAKE_MAGIC; +} +EXPORT_SYMBOL(wx_get_wol); + +int wx_set_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol) +{ + struct wx *wx = netdev_priv(netdev); + struct pci_dev *pdev = wx->pdev; + + if (!wx->wol_hw_supported) + return -EOPNOTSUPP; + + wx->wol = 0; + if (wol->wolopts & WAKE_MAGIC) + wx->wol = WX_PSR_WKUP_CTL_MAG; + netdev->ethtool->wol_enabled = !!(wx->wol); + wr32(wx, WX_PSR_WKUP_CTL, wx->wol); + device_set_wakeup_enable(&pdev->dev, netdev->ethtool->wol_enabled); + + return 0; +} +EXPORT_SYMBOL(wx_set_wol); + void wx_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) { diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.h b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.h index 727093970462..5b187d1587b1 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.h @@ -18,6 +18,10 @@ int wx_get_link_ksettings(struct net_device *netdev, struct ethtool_link_ksettings *cmd); int wx_set_link_ksettings(struct net_device *netdev, const struct ethtool_link_ksettings *cmd); +void wx_get_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol); +int wx_set_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol); void wx_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause); int wx_set_pauseparam(struct net_device *netdev, diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c index fc04040957bf..2b6356622a13 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c @@ -12,38 +12,6 @@ #include "ngbe_ethtool.h" #include "ngbe_type.h" -static void ngbe_get_wol(struct net_device *netdev, - struct ethtool_wolinfo *wol) -{ - struct wx *wx = netdev_priv(netdev); - - if (!wx->wol_hw_supported) - return; - wol->supported = WAKE_MAGIC; - wol->wolopts = 0; - if (wx->wol & WX_PSR_WKUP_CTL_MAG) - wol->wolopts |= WAKE_MAGIC; -} - -static int ngbe_set_wol(struct net_device *netdev, - struct ethtool_wolinfo *wol) -{ - struct wx *wx = netdev_priv(netdev); - struct pci_dev *pdev = wx->pdev; - - if (!wx->wol_hw_supported) - return -EOPNOTSUPP; - - wx->wol = 0; - if (wol->wolopts & WAKE_MAGIC) - wx->wol = WX_PSR_WKUP_CTL_MAG; - netdev->ethtool->wol_enabled = !!(wx->wol); - wr32(wx, WX_PSR_WKUP_CTL, wx->wol); - device_set_wakeup_enable(&pdev->dev, netdev->ethtool->wol_enabled); - - return 0; -} - static int ngbe_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, struct kernel_ethtool_ringparam *kernel_ring, @@ -122,8 +90,8 @@ static const struct ethtool_ops ngbe_ethtool_ops = { .get_link_ksettings = wx_get_link_ksettings, .set_link_ksettings = wx_set_link_ksettings, .nway_reset = wx_nway_reset, - .get_wol = ngbe_get_wol, - .set_wol = ngbe_set_wol, + .get_wol = wx_get_wol, + .set_wol = wx_set_wol, .get_sset_count = wx_get_sset_count, .get_strings = wx_get_strings, .get_ethtool_stats = wx_get_ethtool_stats, diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c index 58488e138beb..e28ddf684a06 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c @@ -58,14 +58,14 @@ static void ngbe_init_type_code(struct wx *wx) 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; + wol_mask = wx->subsystem_device_id & WX_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->wol_hw_supported = (wol_mask == WX_WOL_SUP) ? 1 : 0; wx->ncsi_enabled = (ncsi_mask == NGBE_NCSI_MASK || type_mask == NGBE_SUBID_OCP_CARD) ? 1 : 0; @@ -520,9 +520,9 @@ static void ngbe_dev_shutdown(struct pci_dev *pdev, bool *enable_wake) if (wufc) { wx_set_rx_mode(netdev); wx_configure_rx(wx); - wr32(wx, NGBE_PSR_WKUP_CTL, wufc); + wr32(wx, WX_PSR_WKUP_CTL, wufc); } else { - wr32(wx, NGBE_PSR_WKUP_CTL, 0); + wr32(wx, WX_PSR_WKUP_CTL, 0); } pci_wake_from_d3(pdev, !!wufc); *enable_wake = !!wufc; @@ -742,10 +742,10 @@ static int ngbe_probe(struct pci_dev *pdev, wx->wol = 0; if (wx->wol_hw_supported) - wx->wol = NGBE_PSR_WKUP_CTL_MAG; + wx->wol = WX_PSR_WKUP_CTL_MAG; netdev->ethtool->wol_enabled = !!(wx->wol); - wr32(wx, NGBE_PSR_WKUP_CTL, wx->wol); + wr32(wx, WX_PSR_WKUP_CTL, wx->wol); device_set_wakeup_enable(&pdev->dev, wx->wol); /* Save off EEPROM version number and Option Rom version which diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h index 3b2ca7f47e33..f1957fa0add4 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h @@ -39,8 +39,6 @@ #define NGBE_NCSI_SUP 0x8000 #define NGBE_NCSI_MASK 0x8000 -#define NGBE_WOL_SUP 0x4000 -#define NGBE_WOL_MASK 0x4000 /**************** EM Registers ****************************/ /* chip control Registers */ @@ -93,18 +91,6 @@ #define NGBE_CFG_LAN_SPEED 0x14440 #define NGBE_CFG_PORT_ST 0x14404 -/* Wake up registers */ -#define NGBE_PSR_WKUP_CTL 0x15B80 -/* Wake Up Filter Control Bit */ -#define NGBE_PSR_WKUP_CTL_LNKC BIT(0) /* Link Status Change Wakeup Enable*/ -#define NGBE_PSR_WKUP_CTL_MAG BIT(1) /* Magic Packet Wakeup Enable */ -#define NGBE_PSR_WKUP_CTL_EX BIT(2) /* Directed Exact Wakeup Enable */ -#define NGBE_PSR_WKUP_CTL_MC BIT(3) /* Directed Multicast Wakeup Enable*/ -#define NGBE_PSR_WKUP_CTL_BC BIT(4) /* Broadcast Wakeup Enable */ -#define NGBE_PSR_WKUP_CTL_ARP BIT(5) /* ARP Request Packet Wakeup Enable*/ -#define NGBE_PSR_WKUP_CTL_IPV4 BIT(6) /* Directed IPv4 Pkt Wakeup Enable */ -#define NGBE_PSR_WKUP_CTL_IPV6 BIT(7) /* Directed IPv6 Pkt Wakeup Enable */ - #define NGBE_FW_EEPROM_CHECKSUM_CMD 0xE9 #define NGBE_FW_NVM_DATA_OFFSET 3 #define NGBE_FW_CMD_DEFAULT_CHECKSUM 0xFF /* checksum always 0xFF */ -- 2.48.1 NGBE_NCSI_SUP and NGBE_NCSI_MASK are duplicate-defined, they can be replaced by the macros defined in libwx. Just remove them. Signed-off-by: Jiawen Wu --- drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 4 ++-- drivers/net/ethernet/wangxun/ngbe/ngbe_type.h | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c index e28ddf684a06..8c9d505721b1 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c @@ -57,7 +57,7 @@ static void ngbe_init_type_code(struct wx *wx) 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; + ncsi_mask = wx->subsystem_device_id & WX_NCSI_MASK; wol_mask = wx->subsystem_device_id & WX_WOL_MASK; val = rd32(wx, WX_CFG_PORT_ST); @@ -66,7 +66,7 @@ static void ngbe_init_type_code(struct wx *wx) em_mac_type_mdi; wx->wol_hw_supported = (wol_mask == WX_WOL_SUP) ? 1 : 0; - wx->ncsi_enabled = (ncsi_mask == NGBE_NCSI_MASK || + wx->ncsi_enabled = (ncsi_mask == WX_NCSI_SUP || type_mask == NGBE_SUBID_OCP_CARD) ? 1 : 0; switch (type_mask) { diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h index f1957fa0add4..7077a0da4c98 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h @@ -37,9 +37,6 @@ #define NGBE_OEM_MASK 0x00FF -#define NGBE_NCSI_SUP 0x8000 -#define NGBE_NCSI_MASK 0x8000 - /**************** EM Registers ****************************/ /* chip control Registers */ #define NGBE_MIS_PRB_CTL 0x10010 -- 2.48.1 Implement wx->do_reset() for ngbe driver, and improve the specific reset flow. Signed-off-by: Jiawen Wu --- .../net/ethernet/wangxun/ngbe/ngbe_ethtool.c | 1 - drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 49 +++++++++++++++++-- drivers/net/ethernet/wangxun/ngbe/ngbe_type.h | 1 + 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c index 2b6356622a13..b86980c2418f 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c @@ -60,7 +60,6 @@ static int ngbe_set_ringparam(struct net_device *netdev, wx_set_ring(wx, new_tx_count, new_rx_count, temp_ring); kvfree(temp_ring); - wx_configure(wx); ngbe_up(wx); clear_reset: diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c index 8c9d505721b1..f25a5fcab319 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c @@ -133,6 +133,7 @@ static int ngbe_sw_init(struct wx *wx) wx->mbx.size = WX_VXMAILBOX_SIZE; wx->setup_tc = ngbe_setup_tc; + wx->do_reset = ngbe_do_reset; set_bit(0, &wx->fwd_bitmask); return 0; @@ -383,6 +384,12 @@ static void ngbe_disable_device(struct wx *wx) static void ngbe_reset(struct wx *wx) { + int err; + + err = ngbe_reset_hw(wx); + if (err) + wx_err(wx, "Hardware Error: %d\n", err); + wx_flush_sw_mac_table(wx); wx_mac_set_default_filter(wx, wx->mac.addr); if (test_bit(WX_STATE_PTP_RUNNING, wx->state)) @@ -398,7 +405,7 @@ void ngbe_down(struct wx *wx) wx_clean_all_rx_rings(wx); } -void ngbe_up(struct wx *wx) +static void ngbe_up_complete(struct wx *wx) { wx_configure_vectors(wx); @@ -463,7 +470,7 @@ static int ngbe_open(struct net_device *netdev) wx_ptp_init(wx); - ngbe_up(wx); + ngbe_up_complete(wx); return 0; err_dis_phy: @@ -502,6 +509,12 @@ static int ngbe_close(struct net_device *netdev) return 0; } +void ngbe_up(struct wx *wx) +{ + wx_configure(wx); + ngbe_up_complete(wx); +} + static void ngbe_dev_shutdown(struct pci_dev *pdev, bool *enable_wake) { struct wx *wx = pci_get_drvdata(pdev); @@ -563,6 +576,8 @@ int ngbe_setup_tc(struct net_device *dev, u8 tc) */ if (netif_running(dev)) ngbe_close(dev); + else + ngbe_reset(wx); wx_clear_interrupt_scheme(wx); @@ -579,6 +594,34 @@ int ngbe_setup_tc(struct net_device *dev, u8 tc) return 0; } +static void ngbe_reinit_locked(struct wx *wx) +{ + int err = 0; + + netif_trans_update(wx->netdev); + + err = wx_set_state_reset(wx); + if (err) { + wx_err(wx, "wait device reset timeout\n"); + return; + } + + ngbe_down(wx); + ngbe_up(wx); + + clear_bit(WX_STATE_RESETTING, wx->state); +} + +void ngbe_do_reset(struct net_device *netdev) +{ + struct wx *wx = netdev_priv(netdev); + + if (netif_running(netdev)) + ngbe_reinit_locked(wx); + else + ngbe_reset(wx); +} + static const struct net_device_ops ngbe_netdev_ops = { .ndo_open = ngbe_open, .ndo_stop = ngbe_close, @@ -858,7 +901,7 @@ static int ngbe_resume(struct pci_dev *pdev) pci_set_master(pdev); device_wakeup_disable(&pdev->dev); - ngbe_reset_hw(wx); + ngbe_reset(wx); rtnl_lock(); err = wx_init_interrupt_scheme(wx); if (!err && netif_running(netdev)) diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h index 7077a0da4c98..4f648f272c08 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h @@ -125,5 +125,6 @@ extern char ngbe_driver_name[]; void ngbe_down(struct wx *wx); void ngbe_up(struct wx *wx); int ngbe_setup_tc(struct net_device *dev, u8 tc); +void ngbe_do_reset(struct net_device *netdev); #endif /* _NGBE_TYPE_H_ */ -- 2.48.1 Add function pointer wx->close_suspend() and adjust wx->do_reset(), so that PCI driver ops such as .shutdown can be called in libwx. Signed-off-by: Jiawen Wu --- .../net/ethernet/wangxun/libwx/wx_ethtool.c | 2 +- drivers/net/ethernet/wangxun/libwx/wx_lib.c | 89 +++++++++++++- drivers/net/ethernet/wangxun/libwx/wx_lib.h | 3 + drivers/net/ethernet/wangxun/libwx/wx_type.h | 3 +- drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 113 ++++-------------- drivers/net/ethernet/wangxun/ngbe/ngbe_type.h | 3 +- .../net/ethernet/wangxun/txgbe/txgbe_main.c | 17 +-- .../net/ethernet/wangxun/txgbe/txgbe_type.h | 3 +- 8 files changed, 126 insertions(+), 107 deletions(-) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c index 9e940ea9cb8b..9fd8cbe62f0c 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c @@ -396,7 +396,7 @@ static void wx_update_rsc(struct wx *wx) /* reset the device to apply the new RSC setting */ if (need_reset && wx->do_reset) - wx->do_reset(netdev); + wx->do_reset(netdev, true); } int wx_set_coalesce(struct net_device *netdev, diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c index 746623fa59b4..c65240ca3c28 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c @@ -3109,7 +3109,7 @@ int wx_set_features(struct net_device *netdev, netdev_features_t features) netdev->features = features; if (changed & NETIF_F_HW_VLAN_CTAG_RX && wx->do_reset) - wx->do_reset(netdev); + wx->do_reset(netdev, true); else if (changed & (NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER)) wx_set_rx_mode(netdev); @@ -3159,7 +3159,7 @@ int wx_set_features(struct net_device *netdev, netdev_features_t features) out: if (need_reset && wx->do_reset) - wx->do_reset(netdev); + wx->do_reset(netdev, true); return 0; } @@ -3342,5 +3342,90 @@ void wx_service_timer(struct timer_list *t) } EXPORT_SYMBOL(wx_service_timer); +static void wx_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)) + wx->close_suspend(wx); + + wx_clear_interrupt_scheme(wx); + rtnl_unlock(); + + if (wufc) { + wx_set_rx_mode(netdev); + wx_configure_rx(wx); + wr32(wx, WX_PSR_WKUP_CTL, wufc); + } else { + wr32(wx, WX_PSR_WKUP_CTL, 0); + } + pci_wake_from_d3(pdev, !!wufc); + *enable_wake = !!wufc; + wx_control_hw(wx, false); + + pci_disable_device(pdev); +} + +int wx_suspend(struct pci_dev *pdev, pm_message_t state) +{ + bool wake; + + wx_dev_shutdown(pdev, &wake); + device_set_wakeup_enable(&pdev->dev, wake); + + return 0; +} +EXPORT_SYMBOL(wx_suspend); + +int wx_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); + + wx->do_reset(netdev, false); + rtnl_lock(); + + err = wx_init_interrupt_scheme(wx); + if (!err && netif_running(netdev)) + err = netdev->netdev_ops->ndo_open(netdev); + if (!err) + netif_device_attach(netdev); + rtnl_unlock(); + + return err; +} +EXPORT_SYMBOL(wx_resume); + +void wx_shutdown(struct pci_dev *pdev) +{ + bool wake; + + wx_dev_shutdown(pdev, &wake); + + if (system_state == SYSTEM_POWER_OFF) { + pci_wake_from_d3(pdev, wake); + pci_set_power_state(pdev, PCI_D3hot); + } +} +EXPORT_SYMBOL(wx_shutdown); + MODULE_DESCRIPTION("Common library for Wangxun(R) Ethernet drivers."); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.h b/drivers/net/ethernet/wangxun/libwx/wx_lib.h index aed6ea8cf0d6..42461723a400 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.h @@ -41,5 +41,8 @@ void wx_set_ring(struct wx *wx, u32 new_tx_count, void wx_service_event_schedule(struct wx *wx); void wx_service_event_complete(struct wx *wx); void wx_service_timer(struct timer_list *t); +int wx_suspend(struct pci_dev *pdev, pm_message_t state); +int wx_resume(struct pci_dev *pdev); +void wx_shutdown(struct pci_dev *pdev); #endif /* _WX_LIB_H_ */ diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index 29e5c5470c94..e013f05d2cfe 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -1372,7 +1372,8 @@ struct wx { void (*atr)(struct wx_ring *ring, struct wx_tx_buffer *first, u8 ptype); void (*configure_fdir)(struct wx *wx); int (*setup_tc)(struct net_device *netdev, u8 tc); - void (*do_reset)(struct net_device *netdev); + void (*do_reset)(struct net_device *netdev, bool reinit); + void (*close_suspend)(struct wx *wx); int (*ptp_setup_sdp)(struct wx *wx); void (*set_num_queues)(struct wx *wx); diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c index f25a5fcab319..4dd7de79431c 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c @@ -134,6 +134,7 @@ static int ngbe_sw_init(struct wx *wx) wx->mbx.size = WX_VXMAILBOX_SIZE; wx->setup_tc = ngbe_setup_tc; wx->do_reset = ngbe_do_reset; + wx->close_suspend = ngbe_close_suspend; set_bit(0, &wx->fwd_bitmask); return 0; @@ -483,6 +484,16 @@ static int ngbe_open(struct net_device *netdev) return err; } +void ngbe_close_suspend(struct wx *wx) +{ + wx_ptp_suspend(wx); + ngbe_down(wx); + wx_free_irq(wx); + wx_free_isb_resources(wx); + wx_free_resources(wx); + phylink_disconnect_phy(wx->phylink); +} + /** * ngbe_close - Disables a network interface * @netdev: network interface device structure @@ -499,11 +510,10 @@ static int ngbe_close(struct net_device *netdev) struct wx *wx = netdev_priv(netdev); wx_ptp_stop(wx); - ngbe_down(wx); - wx_free_irq(wx); - wx_free_isb_resources(wx); - wx_free_resources(wx); - phylink_disconnect_phy(wx->phylink); + + if (netif_device_present(netdev)) + ngbe_close_suspend(wx); + wx_control_hw(wx, false); return 0; @@ -515,50 +525,6 @@ void ngbe_up(struct wx *wx) ngbe_up_complete(wx); } -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, WX_PSR_WKUP_CTL, wufc); - } else { - wr32(wx, WX_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. @@ -612,11 +578,11 @@ static void ngbe_reinit_locked(struct wx *wx) clear_bit(WX_STATE_RESETTING, wx->state); } -void ngbe_do_reset(struct net_device *netdev) +void ngbe_do_reset(struct net_device *netdev, bool reinit) { struct wx *wx = netdev_priv(netdev); - if (netif_running(netdev)) + if (netif_running(netdev) && reinit) ngbe_reinit_locked(wx); else ngbe_reset(wx); @@ -874,53 +840,14 @@ static void ngbe_remove(struct pci_dev *pdev) 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(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, + .suspend = wx_suspend, + .resume = wx_resume, + .shutdown = wx_shutdown, .sriov_configure = wx_pci_sriov_configure, }; diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h index 4f648f272c08..eb5c92edae06 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h @@ -125,6 +125,7 @@ extern char ngbe_driver_name[]; void ngbe_down(struct wx *wx); void ngbe_up(struct wx *wx); int ngbe_setup_tc(struct net_device *dev, u8 tc); -void ngbe_do_reset(struct net_device *netdev); +void ngbe_do_reset(struct net_device *netdev, bool reinit); +void ngbe_close_suspend(struct wx *wx); #endif /* _NGBE_TYPE_H_ */ diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index 0de051450a82..30f66507809b 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -412,6 +412,7 @@ static int txgbe_sw_init(struct wx *wx) wx->setup_tc = txgbe_setup_tc; wx->do_reset = txgbe_do_reset; + wx->close_suspend = txgbe_close_suspend; set_bit(0, &wx->fwd_bitmask); switch (wx->mac.type) { @@ -500,10 +501,12 @@ static int txgbe_open(struct net_device *netdev) * This function should contain the necessary work common to both suspending * and closing of the device. */ -static void txgbe_close_suspend(struct wx *wx) +void txgbe_close_suspend(struct wx *wx) { wx_ptp_suspend(wx); - txgbe_disable_device(wx); + txgbe_down(wx); + wx_free_irq(wx); + txgbe_free_misc_irq(wx->priv); wx_free_resources(wx); } @@ -523,10 +526,8 @@ static int txgbe_close(struct net_device *netdev) struct wx *wx = netdev_priv(netdev); wx_ptp_stop(wx); - txgbe_down(wx); - wx_free_irq(wx); - txgbe_free_misc_irq(wx->priv); - wx_free_resources(wx); + if (netif_device_present(netdev)) + txgbe_close_suspend(wx); txgbe_fdir_filter_exit(wx); wx_control_hw(wx, false); @@ -614,11 +615,11 @@ static void txgbe_reinit_locked(struct wx *wx) clear_bit(WX_STATE_RESETTING, wx->state); } -void txgbe_do_reset(struct net_device *netdev) +void txgbe_do_reset(struct net_device *netdev, bool reinit) { struct wx *wx = netdev_priv(netdev); - if (netif_running(netdev)) + if (netif_running(netdev) && reinit) txgbe_reinit_locked(wx); else txgbe_reset(wx); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h index 82433e9cb0e3..e9360e935682 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h @@ -313,7 +313,8 @@ extern char txgbe_driver_name[]; void txgbe_down(struct wx *wx); void txgbe_up(struct wx *wx); int txgbe_setup_tc(struct net_device *dev, u8 tc); -void txgbe_do_reset(struct net_device *netdev); +void txgbe_do_reset(struct net_device *netdev, bool reinit); +void txgbe_close_suspend(struct wx *wx); #define TXGBE_LINK_SPEED_UNKNOWN 0 #define TXGBE_LINK_SPEED_10GB_FULL 4 -- 2.48.1 Support .suspend and .resume and Wake-on-LAN. Implement ethtool ops .get_wol and .set_wol to support Wake-on-LAN function. WOL requires hardware board support which is identified by subsystem device ID. Magic packets are checked by firmware, and currently only WAKE_MAGIC is supported. And WOL related operations are added to txgbe_shutdown(), it matches the implementation of wx_shutdown(). So change to call wx_shutdown() directly in txgbe driver. Signed-off-by: Jiawen Wu --- drivers/net/ethernet/wangxun/libwx/wx_hw.c | 3 +- .../ethernet/wangxun/txgbe/txgbe_ethtool.c | 2 + .../net/ethernet/wangxun/txgbe/txgbe_main.c | 49 +++++++------------ 3 files changed, 21 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c index bee9e245e792..150a063daafd 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c @@ -1313,8 +1313,7 @@ void wx_disable_rx(struct wx *wx) rxctrl &= ~WX_RDB_PB_CTL_RXEN; wr32(wx, WX_RDB_PB_CTL, rxctrl); - if (!(((wx->subsystem_device_id & WX_NCSI_MASK) == WX_NCSI_SUP) || - ((wx->subsystem_device_id & WX_WOL_MASK) == WX_WOL_SUP))) { + if (!(wx->ncsi_enabled || wx->wol_hw_supported)) { /* disable mac receiver */ wr32m(wx, WX_MAC_RX_CFG, WX_MAC_RX_CFG_RE, 0); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c index 9157b8275be1..c92eb71e0c15 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c @@ -575,6 +575,8 @@ static const struct ethtool_ops txgbe_ethtool_ops = { .get_link = ethtool_op_get_link, .get_link_ksettings = txgbe_get_link_ksettings, .set_link_ksettings = wx_set_link_ksettings, + .get_wol = wx_get_wol, + .set_wol = wx_set_wol, .get_sset_count = wx_get_sset_count, .get_strings = wx_get_strings, .get_ethtool_stats = wx_get_ethtool_stats, diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index 30f66507809b..ddc7cea9d7c2 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -245,8 +245,7 @@ static void txgbe_disable_device(struct wx *wx) wx_set_all_vfs(wx); } - if (!(((wx->subsystem_device_id & WX_NCSI_MASK) == WX_NCSI_SUP) || - ((wx->subsystem_device_id & WX_WOL_MASK) == WX_WOL_SUP))) { + if (!(wx->ncsi_enabled || wx->wol_hw_supported)) { /* disable mac transmiter */ wr32m(wx, WX_MAC_TX_CFG, WX_MAC_TX_CFG_TE, 0); } @@ -303,6 +302,7 @@ void txgbe_up(struct wx *wx) static void txgbe_init_type_code(struct wx *wx) { u8 device_type = wx->subsystem_device_id & 0xF0; + u16 wol_mask, ncsi_mask; switch (wx->device_id) { case TXGBE_DEV_ID_SP1000: @@ -347,6 +347,11 @@ static void txgbe_init_type_code(struct wx *wx) wx->media_type = wx_media_unknown; break; } + + wol_mask = wx->subsystem_device_id & WX_WOL_MASK; + ncsi_mask = wx->subsystem_device_id & WX_NCSI_MASK; + wx->wol_hw_supported = (wol_mask == WX_WOL_SUP) ? 1 : 0; + wx->ncsi_enabled = (ncsi_mask == WX_NCSI_SUP) ? 1 : 0; } /** @@ -534,34 +539,6 @@ static int txgbe_close(struct net_device *netdev) return 0; } -static void txgbe_dev_shutdown(struct pci_dev *pdev) -{ - struct wx *wx = pci_get_drvdata(pdev); - struct net_device *netdev; - - netdev = wx->netdev; - netif_device_detach(netdev); - - rtnl_lock(); - if (netif_running(netdev)) - txgbe_close_suspend(wx); - rtnl_unlock(); - - wx_control_hw(wx, false); - - pci_disable_device(pdev); -} - -static void txgbe_shutdown(struct pci_dev *pdev) -{ - txgbe_dev_shutdown(pdev); - - if (system_state == SYSTEM_POWER_OFF) { - pci_wake_from_d3(pdev, false); - pci_set_power_state(pdev, PCI_D3hot); - } -} - /** * txgbe_setup_tc - routine to configure net_device for multiple traffic * classes. @@ -824,6 +801,14 @@ static int txgbe_probe(struct pci_dev *pdev, eth_hw_addr_set(netdev, wx->mac.perm_addr); wx_mac_set_default_filter(wx, wx->mac.perm_addr); + wx->wol = 0; + if (wx->wol_hw_supported) + wx->wol = WX_PSR_WKUP_CTL_MAG; + + netdev->ethtool->wol_enabled = !!(wx->wol); + wr32(wx, WX_PSR_WKUP_CTL, wx->wol); + device_set_wakeup_enable(&pdev->dev, wx->wol); + txgbe_init_service(wx); err = wx_init_interrupt_scheme(wx); @@ -975,7 +960,9 @@ static struct pci_driver txgbe_driver = { .id_table = txgbe_pci_tbl, .probe = txgbe_probe, .remove = txgbe_remove, - .shutdown = txgbe_shutdown, + .suspend = wx_suspend, + .resume = wx_resume, + .shutdown = wx_shutdown, .sriov_configure = wx_pci_sriov_configure, }; -- 2.48.1 Since function ops wx->setup_tc() is set in txgbe and ngbe, ethtool_ops.set_channels can be implemented in libwx to reduce duplicated code. Signed-off-by: Jiawen Wu --- drivers/net/ethernet/wangxun/libwx/wx_ethtool.c | 2 +- drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c | 15 +-------------- .../net/ethernet/wangxun/txgbe/txgbe_ethtool.c | 15 +-------------- 3 files changed, 3 insertions(+), 29 deletions(-) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c index 9fd8cbe62f0c..27221e259aad 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c @@ -556,7 +556,7 @@ int wx_set_channels(struct net_device *dev, wx->ring_feature[RING_F_RSS].limit = count; - return 0; + return wx->setup_tc(dev, netdev_get_num_tc(dev)); } EXPORT_SYMBOL(wx_set_channels); diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c index b86980c2418f..c37d108a85a1 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c @@ -67,19 +67,6 @@ static int ngbe_set_ringparam(struct net_device *netdev, return err; } -static int ngbe_set_channels(struct net_device *dev, - struct ethtool_channels *ch) -{ - int err; - - err = wx_set_channels(dev, ch); - if (err < 0) - return err; - - /* use setup TC to update any traffic class queue mapping */ - return ngbe_setup_tc(dev, netdev_get_num_tc(dev)); -} - static const struct ethtool_ops ngbe_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_USECS | ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ | @@ -103,7 +90,7 @@ static const struct ethtool_ops ngbe_ethtool_ops = { .get_coalesce = wx_get_coalesce, .set_coalesce = wx_set_coalesce, .get_channels = wx_get_channels, - .set_channels = ngbe_set_channels, + .set_channels = wx_set_channels, .get_rxfh_fields = wx_get_rxfh_fields, .set_rxfh_fields = wx_set_rxfh_fields, .get_rxfh_indir_size = wx_rss_indir_size, diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c index c92eb71e0c15..fc5d98f38f66 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c @@ -91,19 +91,6 @@ static int txgbe_set_ringparam(struct net_device *netdev, return err; } -static int txgbe_set_channels(struct net_device *dev, - struct ethtool_channels *ch) -{ - int err; - - err = wx_set_channels(dev, ch); - if (err < 0) - return err; - - /* use setup TC to update any traffic class queue mapping */ - return txgbe_setup_tc(dev, netdev_get_num_tc(dev)); -} - static int txgbe_get_ethtool_fdir_entry(struct txgbe *txgbe, struct ethtool_rxnfc *cmd) { @@ -589,7 +576,7 @@ static const struct ethtool_ops txgbe_ethtool_ops = { .get_coalesce = wx_get_coalesce, .set_coalesce = wx_set_coalesce, .get_channels = wx_get_channels, - .set_channels = txgbe_set_channels, + .set_channels = wx_set_channels, .get_rxnfc = txgbe_get_rxnfc, .set_rxnfc = txgbe_set_rxnfc, .get_rx_ring_count = txgbe_get_rx_ring_count, -- 2.48.1 Implement .ndo_tx_timeout to handle Tx side timeout event. When Tx timeout event occur, it will triger driver into reset process. Signed-off-by: Jiawen Wu --- drivers/net/ethernet/wangxun/libwx/Makefile | 2 +- drivers/net/ethernet/wangxun/libwx/wx_err.c | 126 ++++++++++++++++++ drivers/net/ethernet/wangxun/libwx/wx_err.h | 14 ++ drivers/net/ethernet/wangxun/libwx/wx_hw.c | 1 + drivers/net/ethernet/wangxun/libwx/wx_lib.c | 49 +++++++ drivers/net/ethernet/wangxun/libwx/wx_lib.h | 1 + drivers/net/ethernet/wangxun/libwx/wx_type.h | 12 +- drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 33 ++++- .../net/ethernet/wangxun/txgbe/txgbe_main.c | 3 + 9 files changed, 238 insertions(+), 3 deletions(-) create mode 100644 drivers/net/ethernet/wangxun/libwx/wx_err.c create mode 100644 drivers/net/ethernet/wangxun/libwx/wx_err.h diff --git a/drivers/net/ethernet/wangxun/libwx/Makefile b/drivers/net/ethernet/wangxun/libwx/Makefile index a71b0ad77de3..c8724bb129aa 100644 --- a/drivers/net/ethernet/wangxun/libwx/Makefile +++ b/drivers/net/ethernet/wangxun/libwx/Makefile @@ -4,5 +4,5 @@ obj-$(CONFIG_LIBWX) += libwx.o -libwx-objs := wx_hw.o wx_lib.o wx_ethtool.o wx_ptp.o wx_mbx.o wx_sriov.o +libwx-objs := wx_hw.o wx_lib.o wx_ethtool.o wx_ptp.o wx_mbx.o wx_sriov.o wx_err.o libwx-objs += wx_vf.o wx_vf_lib.o wx_vf_common.o diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.c b/drivers/net/ethernet/wangxun/libwx/wx_err.c new file mode 100644 index 000000000000..bb77bdce69d2 --- /dev/null +++ b/drivers/net/ethernet/wangxun/libwx/wx_err.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2015 - 2026 Beijing WangXun Technology Co., Ltd. */ + +#include +#include + +#include "wx_type.h" +#include "wx_lib.h" +#include "wx_err.h" + +static void wx_reset_subtask(struct wx *wx) +{ + if (!test_bit(WX_FLAG_NEED_PF_RESET, wx->flags)) + return; + + if (!netif_running(wx->netdev) || + test_bit(WX_STATE_RESETTING, wx->state)) + return; + + rtnl_lock(); + + wx_warn(wx, "Reset adapter.\n"); + + if (test_bit(WX_FLAG_NEED_PF_RESET, wx->flags)) { + if (wx->do_reset) + wx->do_reset(wx->netdev, true); + clear_bit(WX_FLAG_NEED_PF_RESET, wx->flags); + } + + rtnl_unlock(); +} + +/* + * wx_check_tx_hang_subtask - check for hung queues and dropped interrupts + * @wx - pointer to the device wx structure + * + * This function serves two purposes. First it strobes the interrupt lines + * in order to make certain interrupts are occurring. Secondly it sets the + * bits needed to check for TX hangs. As a result we should immediately + * determine if a hang has occurred. + */ +static void wx_check_tx_hang_subtask(struct wx *wx) +{ + int i; + + /* If we're down or resetting, just bail */ + if (!netif_running(wx->netdev) || + test_bit(WX_STATE_RESETTING, wx->state)) + return; + + /* Force detection of hung controller */ + if (netif_carrier_ok(wx->netdev)) { + for (i = 0; i < wx->num_tx_queues; i++) + set_bit(WX_TX_DETECT_HANG, wx->tx_ring[i]->state); + } +} + +void wx_handle_errors_subtask(struct wx *wx) +{ + wx_reset_subtask(wx); + wx_check_tx_hang_subtask(wx); +} +EXPORT_SYMBOL(wx_handle_errors_subtask); + +static void wx_tx_timeout_reset(struct wx *wx) +{ + if (!netif_running(wx->netdev)) + return; + + set_bit(WX_FLAG_NEED_PF_RESET, wx->flags); + wx_warn(wx, "initiating reset due to tx timeout\n"); + wx_service_event_schedule(wx); +} + +void wx_tx_timeout(struct net_device *netdev, unsigned int txqueue) +{ + struct wx *wx = netdev_priv(netdev); + u32 head, tail; + int i; + + for (i = 0; i < wx->num_tx_queues; i++) { + struct wx_ring *tx_ring = wx->tx_ring[i]; + + if (test_bit(WX_TX_DETECT_HANG, tx_ring->state) && + wx_check_tx_hang(tx_ring)) + wx_warn(wx, "Real tx hang detected on queue %d\n", i); + + head = rd32(wx, WX_PX_TR_RP(tx_ring->reg_idx)); + tail = rd32(wx, WX_PX_TR_WP(tx_ring->reg_idx)); + wx_warn(wx, + "tx ring %d next_to_use is %d, next_to_clean is %d\n", + i, tx_ring->next_to_use, + tx_ring->next_to_clean); + wx_warn(wx, "tx ring %d hw rp is 0x%x, wp is 0x%x\n", + i, head, tail); + } + + wx_tx_timeout_reset(wx); +} +EXPORT_SYMBOL(wx_tx_timeout); + +void wx_handle_tx_hang(struct wx_ring *tx_ring, unsigned int next) +{ + struct wx *wx = netdev_priv(tx_ring->netdev); + + wx_warn(wx, "Detected Tx Unit Hang\n" + " Tx Queue <%d>\n" + " TDH, TDT <%x>, <%x>\n" + " next_to_use <%x>\n" + " next_to_clean <%x>\n" + "tx_buffer_info[next_to_clean]\n" + " time_stamp <%lx>\n" + " jiffies <%lx>\n", + tx_ring->queue_index, + rd32(wx, WX_PX_TR_RP(tx_ring->reg_idx)), + rd32(wx, WX_PX_TR_WP(tx_ring->reg_idx)), + tx_ring->next_to_use, next, + tx_ring->tx_buffer_info[next].time_stamp, jiffies); + + netif_stop_subqueue(tx_ring->netdev, tx_ring->queue_index); + + wx_warn(wx, "tx hang detected on queue %d, resetting adapter\n", + tx_ring->queue_index); + + wx_tx_timeout_reset(wx); +} diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.h b/drivers/net/ethernet/wangxun/libwx/wx_err.h new file mode 100644 index 000000000000..e317e6c8d928 --- /dev/null +++ b/drivers/net/ethernet/wangxun/libwx/wx_err.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * WangXun Gigabit PCI Express Linux driver + * Copyright (c) 2015 - 2026 Beijing WangXun Technology Co., Ltd. + */ + +#ifndef _WX_ERR_H_ +#define _WX_ERR_H_ + +void wx_handle_errors_subtask(struct wx *wx); +void wx_tx_timeout(struct net_device *netdev, unsigned int txqueue); +void wx_handle_tx_hang(struct wx_ring *tx_ring, unsigned int next); + +#endif /* _WX_ERR_H_ */ diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c index 150a063daafd..2f71dc24306d 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c @@ -1931,6 +1931,7 @@ static void wx_configure_tx_ring(struct wx *wx, else ring->atr_sample_rate = 0; + bitmap_zero(ring->state, WX_RING_STATE_NBITS); /* reinitialize tx_buffer_info */ memset(ring->tx_buffer_info, 0, sizeof(struct wx_tx_buffer) * ring->count); diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c index c65240ca3c28..5a21478a592b 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c @@ -14,6 +14,7 @@ #include "wx_type.h" #include "wx_lib.h" +#include "wx_err.h" #include "wx_ptp.h" #include "wx_hw.h" #include "wx_vf_lib.h" @@ -742,6 +743,48 @@ static struct netdev_queue *wx_txring_txq(const struct wx_ring *ring) return netdev_get_tx_queue(ring->netdev, ring->queue_index); } +static u64 wx_get_tx_pending(struct wx_ring *ring) +{ + unsigned int head, tail; + + head = ring->next_to_clean; + tail = ring->next_to_use; + + return ((head <= tail) ? tail : tail + ring->count) - head; +} + +bool wx_check_tx_hang(struct wx_ring *ring) +{ + u64 tx_done_old = ring->tx_stats.tx_done_old; + u64 tx_pending = wx_get_tx_pending(ring); + u64 tx_done = ring->stats.packets; + + clear_bit(WX_TX_DETECT_HANG, ring->state); + + /* + * Check for a hung queue, but be thorough. This verifies + * that a transmit has been completed since the previous + * check AND there is at least one packet pending. The + * ARMED bit is set to indicate a potential hang. The + * bit is cleared if a pause frame is received to remove + * false hang detection due to PFC or 802.3x frames. By + * requiring this to fail twice we avoid races with + * pfc clearing the ARMED bit and conditions where we + * run the check_tx_hang logic with a transmit completion + * pending but without time to complete it yet. + */ + if (tx_done_old == tx_done && tx_pending) + /* make sure it is true for two checks in a row */ + return test_and_set_bit(WX_HANG_CHECK_ARMED, ring->state); + + /* update completed stats and continue */ + ring->tx_stats.tx_done_old = tx_done; + /* reset the countdown */ + clear_bit(WX_HANG_CHECK_ARMED, ring->state); + + return false; +} + /** * wx_clean_tx_irq - Reclaim resources after transmit completes * @q_vector: structure containing interrupt and ring information @@ -866,6 +909,12 @@ static bool wx_clean_tx_irq(struct wx_q_vector *q_vector, netdev_tx_completed_queue(wx_txring_txq(tx_ring), total_packets, total_bytes); + if (test_bit(WX_TX_DETECT_HANG, tx_ring->state) && + wx_check_tx_hang(tx_ring)) { + wx_handle_tx_hang(tx_ring, i); + return true; + } + #define TX_WAKE_THRESHOLD (DESC_NEEDED * 2) if (unlikely(total_packets && netif_carrier_ok(tx_ring->netdev) && (wx_desc_unused(tx_ring) >= TX_WAKE_THRESHOLD))) { diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.h b/drivers/net/ethernet/wangxun/libwx/wx_lib.h index 42461723a400..85f70ea5a64f 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.h @@ -10,6 +10,7 @@ struct wx_dec_ptype wx_decode_ptype(const u8 ptype); void wx_alloc_rx_buffers(struct wx_ring *rx_ring, u16 cleaned_count); u16 wx_desc_unused(struct wx_ring *ring); +bool wx_check_tx_hang(struct wx_ring *ring); netdev_tx_t wx_xmit_frame(struct sk_buff *skb, struct net_device *netdev); void wx_napi_enable_all(struct wx *wx); diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index e013f05d2cfe..434a582393d7 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -1036,6 +1036,7 @@ struct wx_queue_stats { struct wx_tx_queue_stats { u64 restart_queue; u64 tx_busy; + u64 tx_done_old; }; struct wx_rx_queue_stats { @@ -1051,6 +1052,12 @@ struct wx_rx_queue_stats { #define wx_for_each_ring(posm, headm) \ for (posm = (headm).ring; posm; posm = posm->next) +enum wx_ring_state { + WX_TX_DETECT_HANG, + WX_HANG_CHECK_ARMED, + WX_RING_STATE_NBITS +}; + struct wx_ring_container { struct wx_ring *ring; /* pointer to linked list of rings */ unsigned int total_bytes; /* total bytes processed this int */ @@ -1070,6 +1077,7 @@ struct wx_ring { struct wx_tx_buffer *tx_buffer_info; struct wx_rx_buffer *rx_buffer_info; }; + DECLARE_BITMAP(state, WX_RING_STATE_NBITS); u8 __iomem *tail; dma_addr_t dma; /* phys. address of descriptor ring */ dma_addr_t headwb_dma; @@ -1254,6 +1262,7 @@ enum wx_pf_flags { WX_FLAG_NEED_DO_RESET, WX_FLAG_RX_MERGE_ENABLED, WX_FLAG_TXHEAD_WB_ENABLED, + WX_FLAG_NEED_PF_RESET, WX_PF_FLAGS_NBITS /* must be last */ }; @@ -1470,7 +1479,8 @@ wr32ptp(struct wx *wx, u32 reg, u32 value) #define wx_err(wx, fmt, arg...) \ dev_err(&(wx)->pdev->dev, fmt, ##arg) - +#define wx_warn(wx, fmt, arg...) \ + dev_warn(&(wx)->pdev->dev, fmt, ##arg) #define wx_dbg(wx, fmt, arg...) \ dev_dbg(&(wx)->pdev->dev, fmt, ##arg) diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c index 4dd7de79431c..0ed33480b4f4 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c @@ -14,6 +14,7 @@ #include "../libwx/wx_type.h" #include "../libwx/wx_hw.h" #include "../libwx/wx_lib.h" +#include "../libwx/wx_err.h" #include "../libwx/wx_ptp.h" #include "../libwx/wx_mbx.h" #include "../libwx/wx_sriov.h" @@ -140,6 +141,26 @@ static int ngbe_sw_init(struct wx *wx) return 0; } +/** + * ngbe_service_task - manages and runs subtasks + * @work: pointer to work_struct containing our data + **/ +static void ngbe_service_task(struct work_struct *work) +{ + struct wx *wx = container_of(work, struct wx, service_task); + + wx_handle_errors_subtask(wx); + + wx_service_event_complete(wx); +} + +static void ngbe_init_service(struct wx *wx) +{ + timer_setup(&wx->service_timer, wx_service_timer, 0); + INIT_WORK(&wx->service_task, ngbe_service_task); + clear_bit(WX_STATE_SERVICE_SCHED, wx->state); +} + /** * ngbe_irq_enable - Enable default interrupt generation settings * @wx: board private structure @@ -370,6 +391,7 @@ static void ngbe_disable_device(struct wx *wx) wx_napi_disable_all(wx); netif_tx_stop_all_queues(netdev); netif_tx_disable(netdev); + timer_delete_sync(&wx->service_timer); if (wx->gpio_ctrl) ngbe_sfp_modules_txrx_powerctl(wx, false); wx_irq_disable(wx); @@ -415,6 +437,7 @@ static void ngbe_up_complete(struct wx *wx) wx_napi_enable_all(wx); /* enable transmits */ netif_tx_start_all_queues(wx->netdev); + mod_timer(&wx->service_timer, jiffies); /* clear any pending interrupts, may auto mask */ rd32(wx, WX_PX_IC(0)); @@ -593,6 +616,7 @@ static const struct net_device_ops ngbe_netdev_ops = { .ndo_stop = ngbe_close, .ndo_change_mtu = wx_change_mtu, .ndo_start_xmit = wx_xmit_frame, + .ndo_tx_timeout = wx_tx_timeout, .ndo_set_rx_mode = wx_set_rx_mode, .ndo_set_features = wx_set_features, .ndo_fix_features = wx_fix_features, @@ -779,9 +803,11 @@ static int ngbe_probe(struct pci_dev *pdev, eth_hw_addr_set(netdev, wx->mac.perm_addr); wx_mac_set_default_filter(wx, wx->mac.perm_addr); + ngbe_init_service(wx); + err = wx_init_interrupt_scheme(wx); if (err) - goto err_free_mac_table; + goto err_cancel_service; /* phy Interface Configuration */ err = ngbe_mdio_init(wx); @@ -801,6 +827,9 @@ static int ngbe_probe(struct pci_dev *pdev, wx_control_hw(wx, false); err_clear_interrupt_scheme: wx_clear_interrupt_scheme(wx); +err_cancel_service: + timer_delete_sync(&wx->service_timer); + cancel_work_sync(&wx->service_task); err_free_mac_table: kfree(wx->rss_key); kfree(wx->mac_table); @@ -826,6 +855,8 @@ static void ngbe_remove(struct pci_dev *pdev) struct wx *wx = pci_get_drvdata(pdev); struct net_device *netdev; + cancel_work_sync(&wx->service_task); + netdev = wx->netdev; wx_disable_sriov(wx); unregister_netdev(netdev); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index ddc7cea9d7c2..8d2302c62ebf 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -14,6 +14,7 @@ #include "../libwx/wx_type.h" #include "../libwx/wx_lib.h" +#include "../libwx/wx_err.h" #include "../libwx/wx_ptp.h" #include "../libwx/wx_hw.h" #include "../libwx/wx_mbx.h" @@ -128,6 +129,7 @@ static void txgbe_service_task(struct work_struct *work) { struct wx *wx = container_of(work, struct wx, service_task); + wx_handle_errors_subtask(wx); txgbe_module_detection_subtask(wx); txgbe_link_config_subtask(wx); @@ -640,6 +642,7 @@ static const struct net_device_ops txgbe_netdev_ops = { .ndo_stop = txgbe_close, .ndo_change_mtu = wx_change_mtu, .ndo_start_xmit = wx_xmit_frame, + .ndo_tx_timeout = wx_tx_timeout, .ndo_set_rx_mode = wx_set_rx_mode, .ndo_set_features = wx_set_features, .ndo_fix_features = wx_fix_features, -- 2.48.1 Support AER driver to handle the PCIe errors. Signed-off-by: Jiawen Wu --- drivers/net/ethernet/wangxun/libwx/wx_err.c | 98 +++++++++++++++++++ drivers/net/ethernet/wangxun/libwx/wx_err.h | 2 + drivers/net/ethernet/wangxun/libwx/wx_lib.c | 3 +- drivers/net/ethernet/wangxun/libwx/wx_type.h | 1 + drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 5 +- .../net/ethernet/wangxun/txgbe/txgbe_main.c | 5 +- 6 files changed, 111 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.c b/drivers/net/ethernet/wangxun/libwx/wx_err.c index bb77bdce69d2..644d2d7bd183 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_err.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_err.c @@ -3,11 +3,109 @@ #include #include +#include #include "wx_type.h" #include "wx_lib.h" #include "wx_err.h" +/** + * wx_io_error_detected - called when PCI error is detected + * @pdev: Pointer to PCI device + * @state: The current pci connection state + * + * This function is called after a PCI bus error affecting + * this device has been detected. + */ +static pci_ers_result_t wx_io_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + struct wx *wx = pci_get_drvdata(pdev); + struct net_device *netdev; + + netdev = wx->netdev; + if (!netif_device_present(netdev)) + return PCI_ERS_RESULT_DISCONNECT; + + rtnl_lock(); + netif_device_detach(netdev); + + if (netif_running(netdev)) + wx->close_suspend(wx); + + if (state == pci_channel_io_perm_failure) { + rtnl_unlock(); + return PCI_ERS_RESULT_DISCONNECT; + } + + if (!test_and_set_bit(WX_STATE_DISABLED, wx->state)) + pci_disable_device(pdev); + rtnl_unlock(); + + /* Request a slot reset. */ + return PCI_ERS_RESULT_NEED_RESET; +} + +/** + * wx_io_slot_reset - called after the pci bus has been reset. + * @pdev: Pointer to PCI device + * + * Restart the card from scratch, as if from a cold-boot. + */ +static pci_ers_result_t wx_io_slot_reset(struct pci_dev *pdev) +{ + struct wx *wx = pci_get_drvdata(pdev); + pci_ers_result_t result; + + if (pci_enable_device_mem(pdev)) { + wx_err(wx, "Cannot re-enable PCI device after reset.\n"); + result = PCI_ERS_RESULT_DISCONNECT; + } else { + /* make all bar access done before reset. */ + smp_mb__before_atomic(); + clear_bit(WX_STATE_DISABLED, wx->state); + pci_set_master(pdev); + pci_restore_state(pdev); + pci_save_state(pdev); + pci_wake_from_d3(pdev, false); + + wx->do_reset(wx->netdev, false); + result = PCI_ERS_RESULT_RECOVERED; + } + + pci_aer_clear_nonfatal_status(pdev); + + return result; +} + +/** + * wx_io_resume - called when traffic can start flowing again. + * @pdev: Pointer to PCI device + * + * This callback is called when the error recovery driver tells us that + * its OK to resume normal operation. + */ +static void wx_io_resume(struct pci_dev *pdev) +{ + struct wx *wx = pci_get_drvdata(pdev); + struct net_device *netdev; + + netdev = wx->netdev; + rtnl_lock(); + if (netif_running(netdev)) + netdev->netdev_ops->ndo_open(netdev); + + netif_device_attach(netdev); + rtnl_unlock(); +} + +const struct pci_error_handlers wx_err_handler = { + .error_detected = wx_io_error_detected, + .slot_reset = wx_io_slot_reset, + .resume = wx_io_resume, +}; +EXPORT_SYMBOL(wx_err_handler); + static void wx_reset_subtask(struct wx *wx) { if (!test_bit(WX_FLAG_NEED_PF_RESET, wx->flags)) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.h b/drivers/net/ethernet/wangxun/libwx/wx_err.h index e317e6c8d928..8b1a7863b5b1 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_err.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_err.h @@ -7,6 +7,8 @@ #ifndef _WX_ERR_H_ #define _WX_ERR_H_ +extern const struct pci_error_handlers wx_err_handler; + void wx_handle_errors_subtask(struct wx *wx); void wx_tx_timeout(struct net_device *netdev, unsigned int txqueue); void wx_handle_tx_hang(struct wx_ring *tx_ring, unsigned int next); diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c index 5a21478a592b..1cd73b424d7f 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c @@ -3418,7 +3418,8 @@ static void wx_dev_shutdown(struct pci_dev *pdev, bool *enable_wake) *enable_wake = !!wufc; wx_control_hw(wx, false); - pci_disable_device(pdev); + if (!test_and_set_bit(WX_STATE_DISABLED, wx->state)) + pci_disable_device(pdev); } int wx_suspend(struct pci_dev *pdev, pm_message_t state) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index 434a582393d7..142b166117fa 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -1196,6 +1196,7 @@ enum wx_state { WX_STATE_PTP_RUNNING, WX_STATE_PTP_TX_IN_PROGRESS, WX_STATE_SERVICE_SCHED, + WX_STATE_DISABLED, WX_STATE_NBITS /* must be last */ }; diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c index 0ed33480b4f4..772f4188c00d 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c @@ -819,6 +819,7 @@ static int ngbe_probe(struct pci_dev *pdev, goto err_register; pci_set_drvdata(pdev, wx); + pci_save_state(pdev); return 0; @@ -868,7 +869,8 @@ static void ngbe_remove(struct pci_dev *pdev) kfree(wx->mac_table); wx_clear_interrupt_scheme(wx); - pci_disable_device(pdev); + if (!test_and_set_bit(WX_STATE_DISABLED, wx->state)) + pci_disable_device(pdev); } static struct pci_driver ngbe_driver = { @@ -880,6 +882,7 @@ static struct pci_driver ngbe_driver = { .resume = wx_resume, .shutdown = wx_shutdown, .sriov_configure = wx_pci_sriov_configure, + .err_handler = &wx_err_handler, }; module_pci_driver(ngbe_driver); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index 8d2302c62ebf..1dd5c08d5187 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -886,6 +886,7 @@ static int txgbe_probe(struct pci_dev *pdev, goto err_remove_phy; pci_set_drvdata(pdev, wx); + pci_save_state(pdev); netif_tx_stop_all_queues(netdev); @@ -955,7 +956,8 @@ static void txgbe_remove(struct pci_dev *pdev) kfree(wx->mac_table); wx_clear_interrupt_scheme(wx); - pci_disable_device(pdev); + if (!test_and_set_bit(WX_STATE_DISABLED, wx->state)) + pci_disable_device(pdev); } static struct pci_driver txgbe_driver = { @@ -967,6 +969,7 @@ static struct pci_driver txgbe_driver = { .resume = wx_resume, .shutdown = wx_shutdown, .sriov_configure = wx_pci_sriov_configure, + .err_handler = &wx_err_handler, }; module_pci_driver(txgbe_driver); -- 2.48.1