* [PATCH net-next v1 1/7] net: ngbe: move the WOL functions to libwx
2026-02-03 7:57 [PATCH net-next v1 0/7] Wangxun improvement and new support Jiawen Wu
@ 2026-02-03 7:57 ` Jiawen Wu
2026-02-03 13:52 ` Andrew Lunn
2026-02-03 7:57 ` [PATCH net-next v1 2/7] net: ngbe: improve the reset flow Jiawen Wu
` (5 subsequent siblings)
6 siblings, 1 reply; 18+ messages in thread
From: Jiawen Wu @ 2026-02-03 7:57 UTC (permalink / raw)
To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman
Cc: Mengyuan Lou, Jiawen Wu
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 <jiawenwu@trustnetic.com>
---
.../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 | 16 ++++-----
drivers/net/ethernet/wangxun/ngbe/ngbe_type.h | 17 ---------
5 files changed, 48 insertions(+), 59 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 662f28bdde8a..427d4c00b739 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..8c9d505721b1 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
@@ -57,16 +57,16 @@ 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;
+ 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);
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 ||
+ wx->wol_hw_supported = (wol_mask == WX_WOL_SUP) ? 1 : 0;
+ wx->ncsi_enabled = (ncsi_mask == WX_NCSI_SUP ||
type_mask == NGBE_SUBID_OCP_CARD) ? 1 : 0;
switch (type_mask) {
@@ -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..7077a0da4c98 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
@@ -37,11 +37,6 @@
#define NGBE_OEM_MASK 0x00FF
-#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 */
#define NGBE_MIS_PRB_CTL 0x10010
@@ -93,18 +88,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
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH net-next v1 1/7] net: ngbe: move the WOL functions to libwx
2026-02-03 7:57 ` [PATCH net-next v1 1/7] net: ngbe: move the WOL functions to libwx Jiawen Wu
@ 2026-02-03 13:52 ` Andrew Lunn
2026-02-04 2:05 ` Jiawen Wu
0 siblings, 1 reply; 18+ messages in thread
From: Andrew Lunn @ 2026-02-03 13:52 UTC (permalink / raw)
To: Jiawen Wu
Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Mengyuan Lou
> - ncsi_mask = wx->subsystem_device_id & NGBE_NCSI_MASK;
> - wol_mask = wx->subsystem_device_id & NGBE_WOL_MASK;
> + ncsi_mask = wx->subsystem_device_id & WX_NCSI_MASK;
> + wol_mask = wx->subsystem_device_id & WX_WOL_MASK;
The ncsi_mask change seems like it should not be here. This patch is
about WoL.
Andrew
---
pw-bot: cr
^ permalink raw reply [flat|nested] 18+ messages in thread* RE: [PATCH net-next v1 1/7] net: ngbe: move the WOL functions to libwx
2026-02-03 13:52 ` Andrew Lunn
@ 2026-02-04 2:05 ` Jiawen Wu
0 siblings, 0 replies; 18+ messages in thread
From: Jiawen Wu @ 2026-02-04 2:05 UTC (permalink / raw)
To: 'Andrew Lunn'
Cc: netdev, 'Andrew Lunn', 'David S. Miller',
'Eric Dumazet', 'Jakub Kicinski',
'Paolo Abeni', 'Simon Horman',
'Mengyuan Lou', netdev, 'Andrew Lunn',
'David S. Miller', 'Eric Dumazet',
'Jakub Kicinski', 'Paolo Abeni',
'Simon Horman', 'Mengyuan Lou'
On Tue, Feb 3, 2026 9:53 PM, Andrew Lunn wrote:
> > - ncsi_mask = wx->subsystem_device_id & NGBE_NCSI_MASK;
> > - wol_mask = wx->subsystem_device_id & NGBE_WOL_MASK;
> > + ncsi_mask = wx->subsystem_device_id & WX_NCSI_MASK;
> > + wol_mask = wx->subsystem_device_id & WX_WOL_MASK;
>
> The ncsi_mask change seems like it should not be here. This patch is
> about WoL.
>
I'll split it, thanks.
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH net-next v1 2/7] net: ngbe: improve the reset flow
2026-02-03 7:57 [PATCH net-next v1 0/7] Wangxun improvement and new support Jiawen Wu
2026-02-03 7:57 ` [PATCH net-next v1 1/7] net: ngbe: move the WOL functions to libwx Jiawen Wu
@ 2026-02-03 7:57 ` Jiawen Wu
2026-02-03 13:56 ` Andrew Lunn
2026-02-03 7:57 ` [PATCH net-next v1 3/7] net: wangxun: move reusable PCI driver ops functions into libwx Jiawen Wu
` (4 subsequent siblings)
6 siblings, 1 reply; 18+ messages in thread
From: Jiawen Wu @ 2026-02-03 7:57 UTC (permalink / raw)
To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman
Cc: Mengyuan Lou, Jiawen Wu
Implement wx->do_reset() for ngbe driver, and improve the specific reset
flow.
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
.../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 427d4c00b739..1a3082c1d15c 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..fd6339db222c 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 != 0)
+ 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
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH net-next v1 2/7] net: ngbe: improve the reset flow
2026-02-03 7:57 ` [PATCH net-next v1 2/7] net: ngbe: improve the reset flow Jiawen Wu
@ 2026-02-03 13:56 ` Andrew Lunn
2026-02-04 2:20 ` Jiawen Wu
0 siblings, 1 reply; 18+ messages in thread
From: Andrew Lunn @ 2026-02-03 13:56 UTC (permalink / raw)
To: Jiawen Wu
Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Mengyuan Lou
> static void ngbe_reset(struct wx *wx)
> {
> + int err;
> +
> + err = ngbe_reset_hw(wx);
> + if (err != 0)
The != 0 is a bit odd. It looks like rest of the driver uses if (err)
> + wx_err(wx, "Hardware Error: %d\n", err);
I also wounder if this should be fatal? Should ngbe_reset() be made an
int function so the error code can be returned?
Andrew
^ permalink raw reply [flat|nested] 18+ messages in thread* RE: [PATCH net-next v1 2/7] net: ngbe: improve the reset flow
2026-02-03 13:56 ` Andrew Lunn
@ 2026-02-04 2:20 ` Jiawen Wu
0 siblings, 0 replies; 18+ messages in thread
From: Jiawen Wu @ 2026-02-04 2:20 UTC (permalink / raw)
To: 'Andrew Lunn'
Cc: netdev, 'Andrew Lunn', 'David S. Miller',
'Eric Dumazet', 'Jakub Kicinski',
'Paolo Abeni', 'Simon Horman',
'Mengyuan Lou', netdev, 'Andrew Lunn',
'David S. Miller', 'Eric Dumazet',
'Jakub Kicinski', 'Paolo Abeni',
'Simon Horman', 'Mengyuan Lou'
On Tue, Feb 3, 2026 9:56 PM, Andrew Lunn wrote:
> > static void ngbe_reset(struct wx *wx)
> > {
> > + int err;
> > +
> > + err = ngbe_reset_hw(wx);
> > + if (err != 0)
>
> The != 0 is a bit odd. It looks like rest of the driver uses if (err)
>
> > + wx_err(wx, "Hardware Error: %d\n", err);
>
> I also wounder if this should be fatal? Should ngbe_reset() be made an
> int function so the error code can be returned?
The error returned by ngbe_reset_hw() indicates that there is a hardware
issue, and the recovery process will be handled in another work queue.
We hope that the driver can continue to complete the remaining software
reset.
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH net-next v1 3/7] net: wangxun: move reusable PCI driver ops functions into libwx
2026-02-03 7:57 [PATCH net-next v1 0/7] Wangxun improvement and new support Jiawen Wu
2026-02-03 7:57 ` [PATCH net-next v1 1/7] net: ngbe: move the WOL functions to libwx Jiawen Wu
2026-02-03 7:57 ` [PATCH net-next v1 2/7] net: ngbe: improve the reset flow Jiawen Wu
@ 2026-02-03 7:57 ` Jiawen Wu
2026-02-04 2:22 ` [net-next,v1,3/7] " Jakub Kicinski
2026-02-03 7:57 ` [PATCH net-next v1 4/7] net: txgbe: add power management support Jiawen Wu
` (3 subsequent siblings)
6 siblings, 1 reply; 18+ messages in thread
From: Jiawen Wu @ 2026-02-03 7:57 UTC (permalink / raw)
To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman
Cc: Mengyuan Lou, Jiawen Wu
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 <jiawenwu@trustnetic.com>
---
.../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 b31b48d26575..1361f4461046 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
@@ -3112,7 +3112,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);
@@ -3162,7 +3162,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;
}
@@ -3345,5 +3345,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 0;
+}
+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 fd6339db222c..5c85ce09e387 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
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [net-next,v1,3/7] net: wangxun: move reusable PCI driver ops functions into libwx
2026-02-03 7:57 ` [PATCH net-next v1 3/7] net: wangxun: move reusable PCI driver ops functions into libwx Jiawen Wu
@ 2026-02-04 2:22 ` Jakub Kicinski
2026-02-04 6:12 ` Jiawen Wu
0 siblings, 1 reply; 18+ messages in thread
From: Jakub Kicinski @ 2026-02-04 2:22 UTC (permalink / raw)
To: jiawenwu
Cc: Jakub Kicinski, mengyuanlou, horms, netdev, edumazet, davem,
andrew+netdev, pabeni
This is an AI-generated review of your patch. The human sending this
email says "Sending this out without checking since you have to
repost. Please note that the AI code review may be wrong especially
around defensive programming (which we recommend against)."
For local reproduction steps see https://netdev-ai.bots.linux.dev/ai-local.html
---
net: wangxun: move reusable PCI driver ops functions into libwx
This commit refactors PCI driver ops functions (suspend, resume, shutdown)
from ngbe_main.c into the shared libwx library, adding function pointers
wx->close_suspend() and adjusting wx->do_reset() to support this.
> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
> --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
[ ... ]
> +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 0;
> +}
Does wx_resume() need to return the error value stored in err?
The function assigns errors from wx_init_interrupt_scheme() and ndo_open()
to the err variable, and correctly skips netif_device_attach() when errors
occur, but then unconditionally returns 0 instead of returning err.
The early error handling for pci_enable_device_mem() correctly returns err,
but errors from the later calls are lost. This would tell the PM core that
resume succeeded when it actually failed.
--
pw-bot: cr
^ permalink raw reply [flat|nested] 18+ messages in thread* RE: [net-next,v1,3/7] net: wangxun: move reusable PCI driver ops functions into libwx
2026-02-04 2:22 ` [net-next,v1,3/7] " Jakub Kicinski
@ 2026-02-04 6:12 ` Jiawen Wu
0 siblings, 0 replies; 18+ messages in thread
From: Jiawen Wu @ 2026-02-04 6:12 UTC (permalink / raw)
To: 'Jakub Kicinski'
Cc: mengyuanlou, horms, netdev, edumazet, davem, andrew+netdev,
pabeni, mengyuanlou, horms, netdev, edumazet, davem,
andrew+netdev, pabeni
On Wed, Feb 4, 2026 10:23 AM, Jakub Kicinski wrote:
> This is an AI-generated review of your patch. The human sending this
> email says "Sending this out without checking since you have to
> repost. Please note that the AI code review may be wrong especially
> around defensive programming (which we recommend against)."
>
> For local reproduction steps see https://netdev-ai.bots.linux.dev/ai-local.html
> ---
> net: wangxun: move reusable PCI driver ops functions into libwx
>
> This commit refactors PCI driver ops functions (suspend, resume, shutdown)
> from ngbe_main.c into the shared libwx library, adding function pointers
> wx->close_suspend() and adjusting wx->do_reset() to support this.
>
> > diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
> > --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
> > +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
>
> [ ... ]
>
> > +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 0;
> > +}
>
> Does wx_resume() need to return the error value stored in err?
>
> The function assigns errors from wx_init_interrupt_scheme() and ndo_open()
> to the err variable, and correctly skips netif_device_attach() when errors
> occur, but then unconditionally returns 0 instead of returning err.
>
> The early error handling for pci_enable_device_mem() correctly returns err,
> but errors from the later calls are lost. This would tell the PM core that
> resume succeeded when it actually failed.
OK. I'll correct it.
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH net-next v1 4/7] net: txgbe: add power management support
2026-02-03 7:57 [PATCH net-next v1 0/7] Wangxun improvement and new support Jiawen Wu
` (2 preceding siblings ...)
2026-02-03 7:57 ` [PATCH net-next v1 3/7] net: wangxun: move reusable PCI driver ops functions into libwx Jiawen Wu
@ 2026-02-03 7:57 ` Jiawen Wu
2026-02-03 7:57 ` [PATCH net-next v1 5/7] net: wangxun: move ethtool_ops.set_channels into libwx Jiawen Wu
` (2 subsequent siblings)
6 siblings, 0 replies; 18+ messages in thread
From: Jiawen Wu @ 2026-02-03 7:57 UTC (permalink / raw)
To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman
Cc: Mengyuan Lou, Jiawen Wu
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 <jiawenwu@trustnetic.com>
---
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 58b8300e3d2c..2725c6d5d338 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 59d758acccf0..183ec7f0166a 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
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH net-next v1 5/7] net: wangxun: move ethtool_ops.set_channels into libwx
2026-02-03 7:57 [PATCH net-next v1 0/7] Wangxun improvement and new support Jiawen Wu
` (3 preceding siblings ...)
2026-02-03 7:57 ` [PATCH net-next v1 4/7] net: txgbe: add power management support Jiawen Wu
@ 2026-02-03 7:57 ` Jiawen Wu
2026-02-03 7:57 ` [PATCH net-next v1 6/7] net: wangxun: add Tx timeout process Jiawen Wu
2026-02-03 7:57 ` [PATCH net-next v1 7/7] net: wangxun: add pcie error handler Jiawen Wu
6 siblings, 0 replies; 18+ messages in thread
From: Jiawen Wu @ 2026-02-03 7:57 UTC (permalink / raw)
To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman
Cc: Mengyuan Lou, Jiawen Wu
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 <jiawenwu@trustnetic.com>
---
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 1a3082c1d15c..8d23b1b248db 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 183ec7f0166a..9e664279be12 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
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH net-next v1 6/7] net: wangxun: add Tx timeout process
2026-02-03 7:57 [PATCH net-next v1 0/7] Wangxun improvement and new support Jiawen Wu
` (4 preceding siblings ...)
2026-02-03 7:57 ` [PATCH net-next v1 5/7] net: wangxun: move ethtool_ops.set_channels into libwx Jiawen Wu
@ 2026-02-03 7:57 ` Jiawen Wu
2026-02-03 7:57 ` [PATCH net-next v1 7/7] net: wangxun: add pcie error handler Jiawen Wu
6 siblings, 0 replies; 18+ messages in thread
From: Jiawen Wu @ 2026-02-03 7:57 UTC (permalink / raw)
To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman
Cc: Mengyuan Lou, Jiawen Wu
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 <jiawenwu@trustnetic.com>
---
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 <linux/netdevice.h>
+#include <linux/pci.h>
+
+#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 2725c6d5d338..8e3e02ffb5a4 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 1361f4461046..76bda834c59f 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 5c85ce09e387..3171a98d81a6 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
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH net-next v1 7/7] net: wangxun: add pcie error handler
2026-02-03 7:57 [PATCH net-next v1 0/7] Wangxun improvement and new support Jiawen Wu
` (5 preceding siblings ...)
2026-02-03 7:57 ` [PATCH net-next v1 6/7] net: wangxun: add Tx timeout process Jiawen Wu
@ 2026-02-03 7:57 ` Jiawen Wu
2026-02-04 1:34 ` kernel test robot
` (2 more replies)
6 siblings, 3 replies; 18+ messages in thread
From: Jiawen Wu @ 2026-02-03 7:57 UTC (permalink / raw)
To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman
Cc: Mengyuan Lou, Jiawen Wu
Support to check pcie error and invoke aer driver to recover pcie.
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
drivers/net/ethernet/wangxun/libwx/wx_err.c | 235 +++++++++++++++++-
drivers/net/ethernet/wangxun/libwx/wx_err.h | 3 +
drivers/net/ethernet/wangxun/libwx/wx_hw.c | 56 ++++-
drivers/net/ethernet/wangxun/libwx/wx_hw.h | 2 +
drivers/net/ethernet/wangxun/libwx/wx_lib.c | 3 +-
drivers/net/ethernet/wangxun/libwx/wx_type.h | 11 +
drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 14 +-
drivers/net/ethernet/wangxun/ngbe/ngbe_type.h | 2 +
.../net/ethernet/wangxun/txgbe/txgbe_irq.c | 7 +
.../net/ethernet/wangxun/txgbe/txgbe_main.c | 13 +-
.../net/ethernet/wangxun/txgbe/txgbe_type.h | 5 +-
11 files changed, 339 insertions(+), 12 deletions(-)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.c b/drivers/net/ethernet/wangxun/libwx/wx_err.c
index bb77bdce69d2..36eb06bd1798 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_err.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_err.c
@@ -3,10 +3,228 @@
#include <linux/netdevice.h>
#include <linux/pci.h>
+#include <linux/aer.h>
#include "wx_type.h"
#include "wx_lib.h"
#include "wx_err.h"
+#include "wx_hw.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);
+
+ wx->io_err = true;
+ 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);
+
+ wx->io_err = false;
+ 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 bool wx_check_pcie_error(struct wx *wx)
+{
+ u16 vid, pci_cmd, devctl2;
+ u32 value;
+
+ pci_read_config_word(wx->pdev, PCI_VENDOR_ID, &vid);
+ wx_warn(wx, "PCI vendor id is 0x%x\n", vid);
+ pci_read_config_word(wx->pdev, PCI_COMMAND, &pci_cmd);
+ wx_warn(wx, "PCI command reg is 0x%x\n", pci_cmd);
+ pcie_capability_read_word(wx->pdev, PCI_EXP_DEVCTL2, &devctl2);
+ wx_warn(wx, "Device Control2 Register: 0x%04x\n", devctl2);
+
+ value = rd32(wx, WX_MIS_PWR);
+ wx_warn(wx, "MIS_PWR value is 0x%08x\n", value);
+ value = rd32(wx, WX_PX_IMS(0));
+ wx_warn(wx, "PX_IMS0 value is 0x%08x\n", value);
+ if (test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags)) {
+ value = rd32(wx, WX_PX_IMS(1));
+ wx_warn(wx, "PX_IMS1 value is 0x%08x\n", value);
+ }
+ value = rd32(wx, WX_TDB_TFCS);
+ wx_warn(wx, "Tx flow control Status[TDB_TFCS 0xCE00]: 0x%x\n", value);
+
+ /* PCIe link loss or memory space can't access */
+ if (vid == WX_FAILED_READ_CFG_WORD || !(pci_cmd & 0x2))
+ return true;
+
+ return false;
+}
+
+static void wx_check_error_subtask(struct wx *wx)
+{
+ u32 sm;
+
+ if (test_bit(WX_FLAG_ERROR_CHECK, wx->flags)) {
+ /* get PF semaphore */
+ wr32(wx, WX_MIS_PF_SM, 1);
+ clear_bit(WX_FLAG_ERROR_CHECK, wx->flags);
+ }
+
+ sm = rd32(wx, WX_MIS_PF_SM);
+ /* PCIe memory space access error */
+ if (sm == U32_MAX)
+ goto out;
+
+ /* PCIe error may be occurred in another port */
+ if ((sm == 1 && wx_check_first_lan_up(wx)))
+ goto out;
+
+ return;
+out:
+ set_bit(WX_FLAG_NEED_PCIE_RECOVER, wx->flags);
+ wx_warn(wx, "Set PCIe recover on LAN %d\n", wx->bus.func);
+}
+
+static bool wx_check_recovery_capability(struct pci_dev *dev)
+{
+#if defined(__i386__) || defined(__x86_64__)
+ return true;
+#else
+ /* check upstream bridge is root or PLX bridge,
+ * or if CPU is kunpeng 920
+ */
+ if (dev->bus->self->vendor == PCI_VENDOR_ID_PLX ||
+ dev->bus->self->vendor == PCI_VENDOR_ID_HUAWEI)
+ return true;
+ else
+ return false;
+#endif
+}
+
+static void wx_pcie_do_recovery(struct pci_dev *dev)
+{
+ struct wx *wx = pci_get_drvdata(dev);
+ struct aer_capability_regs *regs = &wx->aer_info;
+ int pos;
+
+ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+ if (!pos)
+ return;
+
+ memset(regs, 0, sizeof(*regs));
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, ®s->uncor_status);
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, ®s->uncor_mask);
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, ®s->uncor_severity);
+ pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, ®s->cor_status);
+ pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, ®s->cor_mask);
+ pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®s->cap_control);
+
+ aer_recover_queue(pci_domain_nr(dev->bus), dev->bus->number,
+ dev->devfn, AER_FATAL, regs);
+}
+
+static void wx_pcie_recovery_subtask(struct wx *wx)
+{
+ if (!test_bit(WX_FLAG_NEED_PCIE_RECOVER, wx->flags))
+ return;
+
+ /* release PF semaphore */
+ wr32(wx, WX_MIS_PF_SM, 0);
+
+ if (test_bit(WX_FLAG_SRIOV_ENABLED, wx->flags)) {
+ wx_warn(wx, "PCIe recovery skipped in SR-IOV mode\n");
+ goto out;
+ }
+
+ if (wx_check_recovery_capability(wx->pdev)) {
+ wx_warn(wx, "Do PCIe recovery\n");
+ wx_pcie_do_recovery(wx->pdev);
+ } else {
+ wx_warn(wx, "This platform can't support PCIe recovery, skip it\n");
+ }
+
+out:
+ clear_bit(WX_FLAG_NEED_PCIE_RECOVER, wx->flags);
+}
static void wx_reset_subtask(struct wx *wx)
{
@@ -57,11 +275,20 @@ static void wx_check_tx_hang_subtask(struct wx *wx)
void wx_handle_errors_subtask(struct wx *wx)
{
+ wx_check_error_subtask(wx);
+ wx_pcie_recovery_subtask(wx);
wx_reset_subtask(wx);
wx_check_tx_hang_subtask(wx);
}
EXPORT_SYMBOL(wx_handle_errors_subtask);
+void wx_pcie_error_handler(struct wx *wx)
+{
+ set_bit(WX_FLAG_ERROR_CHECK, wx->flags);
+ wx_service_event_schedule(wx);
+}
+EXPORT_SYMBOL(wx_pcie_error_handler);
+
static void wx_tx_timeout_reset(struct wx *wx)
{
if (!netif_running(wx->netdev))
@@ -75,9 +302,12 @@ static void wx_tx_timeout_reset(struct wx *wx)
void wx_tx_timeout(struct net_device *netdev, unsigned int txqueue)
{
struct wx *wx = netdev_priv(netdev);
+ bool pcie_error;
u32 head, tail;
int i;
+ pcie_error = wx_check_pcie_error(wx);
+
for (i = 0; i < wx->num_tx_queues; i++) {
struct wx_ring *tx_ring = wx->tx_ring[i];
@@ -95,7 +325,10 @@ void wx_tx_timeout(struct net_device *netdev, unsigned int txqueue)
i, head, tail);
}
- wx_tx_timeout_reset(wx);
+ if (pcie_error)
+ wx_pcie_error_handler(wx);
+ else
+ wx_tx_timeout_reset(wx);
}
EXPORT_SYMBOL(wx_tx_timeout);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.h b/drivers/net/ethernet/wangxun/libwx/wx_err.h
index e317e6c8d928..f1d14d622f14 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_err.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_err.h
@@ -7,7 +7,10 @@
#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_pcie_error_handler(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_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
index 8e3e02ffb5a4..ec0c8e2ba511 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
@@ -10,6 +10,7 @@
#include "wx_type.h"
#include "wx_lib.h"
+#include "wx_err.h"
#include "wx_sriov.h"
#include "wx_vf.h"
#include "wx_hw.h"
@@ -2308,8 +2309,11 @@ int wx_disable_pcie_master(struct wx *wx)
/* Poll for master request bit to clear */
status = read_poll_timeout(rd32, val, !val, 100, WX_PCI_MASTER_DISABLE_TIMEOUT,
false, wx, WX_PX_TRANSACTION_PENDING);
- if (status < 0)
+ if (status < 0) {
wx_err(wx, "PCIe transaction pending bit did not clear.\n");
+ if (!wx->io_err)
+ wx_pcie_error_handler(wx);
+ }
return status;
}
@@ -2517,6 +2521,7 @@ int wx_sw_init(struct wx *wx)
bitmap_zero(wx->state, WX_STATE_NBITS);
bitmap_zero(wx->flags, WX_PF_FLAGS_NBITS);
wx->misc_irq_domain = false;
+ wx->io_err = false;
return 0;
}
@@ -2974,4 +2979,53 @@ void wx_start_hw(struct wx *wx)
}
EXPORT_SYMBOL(wx_start_hw);
+void wx_set_pci_lan_up(struct wx *wx, bool up)
+{
+ u8 max_lan;
+ u32 reg;
+
+ if (wx->mac.type == wx_mac_em)
+ max_lan = 3;
+ else
+ max_lan = 1;
+
+ if (wx->bus.func > max_lan) {
+ wx_err(wx, "%s: invalid bus lan id %d\n",
+ __func__, wx->bus.func);
+ return;
+ }
+
+ reg = rd32(wx, WX_MIS_PRB_CTL);
+ if (up)
+ reg |= BIT(max_lan - wx->bus.func);
+ else
+ reg &= ~BIT(max_lan - wx->bus.func);
+ wr32(wx, WX_MIS_PRB_CTL, reg);
+}
+EXPORT_SYMBOL(wx_set_pci_lan_up);
+
+bool wx_check_first_lan_up(struct wx *wx)
+{
+ u8 max_lan, i;
+ u32 reg;
+
+ if (wx->mac.type == wx_mac_em)
+ max_lan = 3;
+ else
+ max_lan = 1;
+
+ /* Check whether the current port is the first (smallest number)
+ * among the ports up on this board.
+ */
+ reg = rd32(wx, WX_MIS_PRB_CTL);
+ for (i = 0; i <= max_lan; i++) {
+ if (reg & BIT(max_lan - i))
+ break;
+ }
+ if (i == wx->bus.func)
+ return true;
+
+ return false;
+}
+
MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.h b/drivers/net/ethernet/wangxun/libwx/wx_hw.h
index 13857376bbad..1ab30a983b68 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_hw.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.h
@@ -59,5 +59,7 @@ int wx_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid);
int wx_fc_enable(struct wx *wx, bool tx_pause, bool rx_pause);
void wx_update_stats(struct wx *wx);
void wx_clear_hw_cntrs(struct wx *wx);
+void wx_set_pci_lan_up(struct wx *wx, bool up);
+bool wx_check_first_lan_up(struct wx *wx);
#endif /* _WX_HW_H_ */
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
index 76bda834c59f..cbd391f1c027 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
@@ -3421,7 +3421,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..68d06d6619ab 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_type.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h
@@ -11,6 +11,7 @@
#include <linux/if_vlan.h>
#include <linux/phylink.h>
#include <linux/dim.h>
+#include <linux/aer.h>
#include <net/ip.h>
#define WX_NCSI_SUP 0x8000
@@ -30,9 +31,11 @@
/* chip control Registers */
#define WX_MIS_PWR 0x10000
+#define WX_MIS_PF_SM 0x10008
#define WX_MIS_RST 0x1000C
#define WX_MIS_RST_LAN_RST(_i) BIT((_i) + 1)
#define WX_MIS_RST_SW_RST BIT(0)
+#define WX_MIS_PRB_CTL 0x10010
#define WX_MIS_ST 0x10028
#define WX_MIS_ST_MNG_INIT_DN BIT(0)
#define WX_MIS_SWSM 0x1002C
@@ -308,6 +311,7 @@
#define WX_TDM_VLAN_INS_VLANA_DEFAULT BIT(30) /* Always use default VLAN*/
/****************************** TDB ******************************************/
+#define WX_TDB_TFCS 0x1CE00
#define WX_TDB_PB_SZ(_i) (0x1CC00 + ((_i) * 4))
#define WX_TXPKT_SIZE_MAX 0xA /* Max Tx Packet size */
@@ -1196,6 +1200,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 */
};
@@ -1263,6 +1268,8 @@ enum wx_pf_flags {
WX_FLAG_RX_MERGE_ENABLED,
WX_FLAG_TXHEAD_WB_ENABLED,
WX_FLAG_NEED_PF_RESET,
+ WX_FLAG_ERROR_CHECK,
+ WX_FLAG_NEED_PCIE_RECOVER,
WX_PF_FLAGS_NBITS /* must be last */
};
@@ -1358,6 +1365,8 @@ struct wx {
#define WX_RSS_KEY_SIZE 40 /* size of RSS Hash Key in bytes */
u32 *rss_key;
u32 wol;
+ bool io_err;
+ struct aer_capability_regs aer_info;
u16 bd_number;
bool default_up;
@@ -1415,6 +1424,8 @@ struct wx {
#define WX_INTR_ALL (~0ULL)
#define WX_INTR_Q(i) BIT((i))
+#define WX_FAILED_READ_CFG_WORD 0xffffU
+
/* register operations */
#define wr32(a, reg, value) writel((value), ((a)->hw_addr + (reg)))
#define rd32(a, reg) readl((a)->hw_addr + (reg))
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
index 3171a98d81a6..653a98d9835b 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
@@ -234,6 +234,12 @@ static irqreturn_t __ngbe_msix_misc(struct wx *wx, u32 eicr)
if (eicr & NGBE_PX_MISC_IC_VF_MBOX)
wx_msg_task(wx);
+ if (eicr & NGBE_PX_MISC_PCIE_REQ_ERR) {
+ wx_warn(wx, "PCIe Request Error founded on Lan %d\n",
+ wx->bus.func);
+ wx_pcie_error_handler(wx);
+ }
+
if (unlikely(eicr & NGBE_PX_MISC_IC_TIMESYNC))
wx_ptp_check_pps_event(wx);
@@ -392,6 +398,7 @@ static void ngbe_disable_device(struct wx *wx)
netif_tx_stop_all_queues(netdev);
netif_tx_disable(netdev);
timer_delete_sync(&wx->service_timer);
+ wx_set_pci_lan_up(wx, false);
if (wx->gpio_ctrl)
ngbe_sfp_modules_txrx_powerctl(wx, false);
wx_irq_disable(wx);
@@ -439,6 +446,8 @@ static void ngbe_up_complete(struct wx *wx)
netif_tx_start_all_queues(wx->netdev);
mod_timer(&wx->service_timer, jiffies);
+ wx_set_pci_lan_up(wx, true);
+
/* clear any pending interrupts, may auto mask */
rd32(wx, WX_PX_IC(0));
rd32(wx, WX_PX_MISC_IC);
@@ -819,6 +828,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 +878,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 +891,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/ngbe/ngbe_type.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
index eb5c92edae06..69ee8f1f74f0 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
@@ -70,12 +70,14 @@
#define NGBE_PX_MISC_IEN_INT_ERR BIT(20)
#define NGBE_PX_MISC_IC_VF_MBOX BIT(23)
#define NGBE_PX_MISC_IEN_GPIO BIT(26)
+#define NGBE_PX_MISC_PCIE_REQ_ERR BIT(27)
#define NGBE_PX_MISC_IEN_MASK ( \
NGBE_PX_MISC_IEN_DEV_RST | \
NGBE_PX_MISC_IEN_TIMESYNC | \
NGBE_PX_MISC_IEN_ETH_LK | \
NGBE_PX_MISC_IEN_INT_ERR | \
NGBE_PX_MISC_IC_VF_MBOX | \
+ NGBE_PX_MISC_PCIE_REQ_ERR | \
NGBE_PX_MISC_IEN_GPIO)
/* Extended Interrupt Cause Read */
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c
index aa14958d439a..c832100bcb61 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c
@@ -6,6 +6,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_sriov.h"
@@ -178,6 +179,12 @@ static irqreturn_t txgbe_misc_irq_thread_fn(int irq, void *data)
handle_nested_irq(sub_irq);
nhandled++;
}
+ if (eicr & TXGBE_PX_MISC_PCIE_REQ_ERR) {
+ wx_warn(wx, "PCIe Request Error founded on Lan %d\n",
+ wx->bus.func);
+ wx_pcie_error_handler(wx);
+ nhandled++;
+ }
if (unlikely(eicr & TXGBE_PX_MISC_IC_TIMESYNC)) {
wx_ptp_check_pps_event(wx);
nhandled++;
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
index 8d2302c62ebf..48d418574ee8 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -182,6 +182,8 @@ static void txgbe_up_complete(struct wx *wx)
netif_tx_start_all_queues(netdev);
mod_timer(&wx->service_timer, jiffies);
+ wx_set_pci_lan_up(wx, true);
+
/* Set PF Reset Done bit so PF/VF Mail Ops can work */
wr32m(wx, WX_CFG_PORT_CTL, WX_CFG_PORT_CTL_PFRSTD,
WX_CFG_PORT_CTL_PFRSTD);
@@ -231,11 +233,7 @@ static void txgbe_disable_device(struct wx *wx)
timer_delete_sync(&wx->service_timer);
- if (wx->bus.func < 2)
- wr32m(wx, TXGBE_MIS_PRB_CTL, TXGBE_MIS_PRB_CTL_LAN_UP(wx->bus.func), 0);
- else
- wx_err(wx, "%s: invalid bus lan id %d\n",
- __func__, wx->bus.func);
+ wx_set_pci_lan_up(wx, false);
if (wx->num_vfs) {
/* Clear EITR Select mapping */
@@ -886,6 +884,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 +954,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 +967,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);
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
index e9360e935682..74ac762a8c34 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
@@ -54,8 +54,7 @@
/* chip control Registers */
#define TXGBE_MIS_RST 0x1000C
#define TXGBE_MIS_RST_MAC_RST(_i) BIT(20 - (_i) * 3)
-#define TXGBE_MIS_PRB_CTL 0x10010
-#define TXGBE_MIS_PRB_CTL_LAN_UP(_i) BIT(1 - (_i))
+
/* FMGR Registers */
#define TXGBE_SPI_ILDR_STATUS 0x10120
#define TXGBE_SPI_ILDR_STATUS_PERST BIT(0) /* PCIE_PERST is done */
@@ -89,10 +88,12 @@
#define TXGBE_PX_MISC_INT_ERR BIT(20)
#define TXGBE_PX_MISC_IC_VF_MBOX BIT(23)
#define TXGBE_PX_MISC_GPIO BIT(26)
+#define TXGBE_PX_MISC_PCIE_REQ_ERR BIT(27)
#define TXGBE_PX_MISC_IEN_MASK \
(TXGBE_PX_MISC_ETH_LKDN | TXGBE_PX_MISC_DEV_RST | \
TXGBE_PX_MISC_ETH_EVENT | TXGBE_PX_MISC_ETH_LK | \
TXGBE_PX_MISC_ETH_AN | TXGBE_PX_MISC_INT_ERR | \
+ TXGBE_PX_MISC_PCIE_REQ_ERR | \
TXGBE_PX_MISC_IC_VF_MBOX | TXGBE_PX_MISC_IC_TIMESYNC)
/* Port cfg registers */
--
2.48.1
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH net-next v1 7/7] net: wangxun: add pcie error handler
2026-02-03 7:57 ` [PATCH net-next v1 7/7] net: wangxun: add pcie error handler Jiawen Wu
@ 2026-02-04 1:34 ` kernel test robot
2026-02-04 2:22 ` [net-next,v1,7/7] " Jakub Kicinski
2026-02-04 11:18 ` [PATCH net-next v1 7/7] " kernel test robot
2 siblings, 0 replies; 18+ messages in thread
From: kernel test robot @ 2026-02-04 1:34 UTC (permalink / raw)
To: Jiawen Wu, netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman
Cc: oe-kbuild-all, Mengyuan Lou, Jiawen Wu
Hi Jiawen,
kernel test robot noticed the following build errors:
[auto build test ERROR on net-next/main]
url: https://github.com/intel-lab-lkp/linux/commits/Jiawen-Wu/net-ngbe-move-the-WOL-functions-to-libwx/20260203-160343
base: net-next/main
patch link: https://lore.kernel.org/r/20260203075759.5852-8-jiawenwu%40trustnetic.com
patch subject: [PATCH net-next v1 7/7] net: wangxun: add pcie error handler
config: sparc64-randconfig-r051-20260204 (https://download.01.org/0day-ci/archive/20260204/202602040920.UKsVMKFG-lkp@intel.com/config)
compiler: sparc64-linux-gcc (GCC) 8.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260204/202602040920.UKsVMKFG-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602040920.UKsVMKFG-lkp@intel.com/
All errors (new ones prefixed by >>):
sparc64-linux-ld: drivers/net/ethernet/wangxun/libwx/wx_err.o: in function `wx_handle_errors_subtask':
>> wx_err.c:(.text+0x4a0): undefined reference to `aer_recover_queue'
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [net-next,v1,7/7] net: wangxun: add pcie error handler
2026-02-03 7:57 ` [PATCH net-next v1 7/7] net: wangxun: add pcie error handler Jiawen Wu
2026-02-04 1:34 ` kernel test robot
@ 2026-02-04 2:22 ` Jakub Kicinski
2026-02-04 6:10 ` Jiawen Wu
2026-02-04 11:18 ` [PATCH net-next v1 7/7] " kernel test robot
2 siblings, 1 reply; 18+ messages in thread
From: Jakub Kicinski @ 2026-02-04 2:22 UTC (permalink / raw)
To: jiawenwu
Cc: Jakub Kicinski, mengyuanlou, horms, netdev, edumazet, davem,
andrew+netdev, pabeni
This is an AI-generated review of your patch. The human sending this
email says "Sending this out without checking since you have to
repost. Please note that the AI code review may be wrong especially
around defensive programming (which we recommend against)."
For local reproduction steps see https://netdev-ai.bots.linux.dev/ai-local.html
---
net: wangxun: add pcie error handler
Support to check pcie error and invoke aer driver to recover pcie.
> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.c b/drivers/net/ethernet/wangxun/libwx/wx_err.c
> index bb77bdce69d2..36eb06bd1798 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_err.c
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_err.c
[ ... ]
> @@ -3,10 +3,228 @@
>
> #include <linux/netdevice.h>
> #include <linux/pci.h>
> +#include <linux/aer.h>
>
> #include "wx_type.h"
> #include "wx_lib.h"
> #include "wx_err.h"
> +#include "wx_hw.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);
> +
> + wx->io_err = true;
> + 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);
> +
> + wx->io_err = false;
> + 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 bool wx_check_pcie_error(struct wx *wx)
> +{
> + u16 vid, pci_cmd, devctl2;
> + u32 value;
> +
> + pci_read_config_word(wx->pdev, PCI_VENDOR_ID, &vid);
> + wx_warn(wx, "PCI vendor id is 0x%x\n", vid);
> + pci_read_config_word(wx->pdev, PCI_COMMAND, &pci_cmd);
> + wx_warn(wx, "PCI command reg is 0x%x\n", pci_cmd);
> + pcie_capability_read_word(wx->pdev, PCI_EXP_DEVCTL2, &devctl2);
> + wx_warn(wx, "Device Control2 Register: 0x%04x\n", devctl2);
> +
> + value = rd32(wx, WX_MIS_PWR);
> + wx_warn(wx, "MIS_PWR value is 0x%08x\n", value);
> + value = rd32(wx, WX_PX_IMS(0));
> + wx_warn(wx, "PX_IMS0 value is 0x%08x\n", value);
> + if (test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags)) {
> + value = rd32(wx, WX_PX_IMS(1));
> + wx_warn(wx, "PX_IMS1 value is 0x%08x\n", value);
> + }
> + value = rd32(wx, WX_TDB_TFCS);
> + wx_warn(wx, "Tx flow control Status[TDB_TFCS 0xCE00]: 0x%x\n", value);
> +
> + /* PCIe link loss or memory space can't access */
> + if (vid == WX_FAILED_READ_CFG_WORD || !(pci_cmd & 0x2))
> + return true;
> +
> + return false;
> +}
> +
> +static void wx_check_error_subtask(struct wx *wx)
> +{
> + u32 sm;
> +
> + if (test_bit(WX_FLAG_ERROR_CHECK, wx->flags)) {
> + /* get PF semaphore */
> + wr32(wx, WX_MIS_PF_SM, 1);
> + clear_bit(WX_FLAG_ERROR_CHECK, wx->flags);
> + }
> +
> + sm = rd32(wx, WX_MIS_PF_SM);
> + /* PCIe memory space access error */
> + if (sm == U32_MAX)
> + goto out;
> +
> + /* PCIe error may be occurred in another port */
> + if ((sm == 1 && wx_check_first_lan_up(wx)))
> + goto out;
> +
> + return;
> +out:
> + set_bit(WX_FLAG_NEED_PCIE_RECOVER, wx->flags);
> + wx_warn(wx, "Set PCIe recover on LAN %d\n", wx->bus.func);
> +}
> +
> +static bool wx_check_recovery_capability(struct pci_dev *dev)
> +{
> +#if defined(__i386__) || defined(__x86_64__)
> + return true;
> +#else
> + /* check upstream bridge is root or PLX bridge,
> + * or if CPU is kunpeng 920
> + */
> + if (dev->bus->self->vendor == PCI_VENDOR_ID_PLX ||
> + dev->bus->self->vendor == PCI_VENDOR_ID_HUAWEI)
Can this dereference dev->bus->self without checking if it's NULL first?
When the PCI device is directly attached to the root bus, dev->bus->self
is NULL. On non-x86 platforms, this code checks the upstream bridge
vendor without verifying that an upstream bridge exists.
The function comment states "check upstream bridge is root or PLX bridge"
but the code doesn't actually check for the root bus case. The x86 path
returns true unconditionally, but the non-x86 path assumes dev->bus->self
is non-NULL.
> + return true;
> + else
> + return false;
> +#endif
> +}
[ ... ]
> diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> index 3171a98d81a6..653a98d9835b 100644
> --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> @@ -234,6 +234,12 @@ static irqreturn_t __ngbe_msix_misc(struct wx *wx, u32 eicr)
> if (eicr & NGBE_PX_MISC_IC_VF_MBOX)
> wx_msg_task(wx);
>
> + if (eicr & NGBE_PX_MISC_PCIE_REQ_ERR) {
> + wx_warn(wx, "PCIe Request Error founded on Lan %d\n",
> + wx->bus.func);
This isn't a bug, but there's a typo: 'founded' should be 'found'.
> + wx_pcie_error_handler(wx);
> + }
> +
> if (unlikely(eicr & NGBE_PX_MISC_IC_TIMESYNC))
> wx_ptp_check_pps_event(wx);
>
[ ... ]
> diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c
> index aa14958d439a..c832100bcb61 100644
> --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c
> +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c
> @@ -6,6 +6,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_sriov.h"
> @@ -178,6 +179,12 @@ static irqreturn_t txgbe_misc_irq_thread_fn(int irq, void *data)
> handle_nested_irq(sub_irq);
> nhandled++;
> }
> + if (eicr & TXGBE_PX_MISC_PCIE_REQ_ERR) {
> + wx_warn(wx, "PCIe Request Error founded on Lan %d\n",
> + wx->bus.func);
This isn't a bug, but there's a typo: 'founded' should be 'found'.
> + wx_pcie_error_handler(wx);
> + nhandled++;
> + }
> if (unlikely(eicr & TXGBE_PX_MISC_IC_TIMESYNC)) {
> wx_ptp_check_pps_event(wx);
> nhandled++;
^ permalink raw reply [flat|nested] 18+ messages in thread* RE: [net-next,v1,7/7] net: wangxun: add pcie error handler
2026-02-04 2:22 ` [net-next,v1,7/7] " Jakub Kicinski
@ 2026-02-04 6:10 ` Jiawen Wu
0 siblings, 0 replies; 18+ messages in thread
From: Jiawen Wu @ 2026-02-04 6:10 UTC (permalink / raw)
To: 'Jakub Kicinski'
Cc: mengyuanlou, horms, netdev, edumazet, davem, andrew+netdev,
pabeni, mengyuanlou, horms, netdev, edumazet, davem,
andrew+netdev, pabeni
On Wed, Feb 4, 2026 10:23 AM, Jakub Kicinski wrote:
> This is an AI-generated review of your patch. The human sending this
> email says "Sending this out without checking since you have to
> repost. Please note that the AI code review may be wrong especially
> around defensive programming (which we recommend against)."
>
> For local reproduction steps see https://netdev-ai.bots.linux.dev/ai-local.html
> ---
> net: wangxun: add pcie error handler
>
> Support to check pcie error and invoke aer driver to recover pcie.
>
> > diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.c b/drivers/net/ethernet/wangxun/libwx/wx_err.c
> > index bb77bdce69d2..36eb06bd1798 100644
> > --- a/drivers/net/ethernet/wangxun/libwx/wx_err.c
> > +++ b/drivers/net/ethernet/wangxun/libwx/wx_err.c
>
> [ ... ]
>
> > @@ -3,10 +3,228 @@
> >
> > #include <linux/netdevice.h>
> > #include <linux/pci.h>
> > +#include <linux/aer.h>
> >
> > #include "wx_type.h"
> > #include "wx_lib.h"
> > #include "wx_err.h"
> > +#include "wx_hw.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);
> > +
> > + wx->io_err = true;
> > + 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);
> > +
> > + wx->io_err = false;
> > + 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 bool wx_check_pcie_error(struct wx *wx)
> > +{
> > + u16 vid, pci_cmd, devctl2;
> > + u32 value;
> > +
> > + pci_read_config_word(wx->pdev, PCI_VENDOR_ID, &vid);
> > + wx_warn(wx, "PCI vendor id is 0x%x\n", vid);
> > + pci_read_config_word(wx->pdev, PCI_COMMAND, &pci_cmd);
> > + wx_warn(wx, "PCI command reg is 0x%x\n", pci_cmd);
> > + pcie_capability_read_word(wx->pdev, PCI_EXP_DEVCTL2, &devctl2);
> > + wx_warn(wx, "Device Control2 Register: 0x%04x\n", devctl2);
> > +
> > + value = rd32(wx, WX_MIS_PWR);
> > + wx_warn(wx, "MIS_PWR value is 0x%08x\n", value);
> > + value = rd32(wx, WX_PX_IMS(0));
> > + wx_warn(wx, "PX_IMS0 value is 0x%08x\n", value);
> > + if (test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags)) {
> > + value = rd32(wx, WX_PX_IMS(1));
> > + wx_warn(wx, "PX_IMS1 value is 0x%08x\n", value);
> > + }
> > + value = rd32(wx, WX_TDB_TFCS);
> > + wx_warn(wx, "Tx flow control Status[TDB_TFCS 0xCE00]: 0x%x\n", value);
> > +
> > + /* PCIe link loss or memory space can't access */
> > + if (vid == WX_FAILED_READ_CFG_WORD || !(pci_cmd & 0x2))
> > + return true;
> > +
> > + return false;
> > +}
> > +
> > +static void wx_check_error_subtask(struct wx *wx)
> > +{
> > + u32 sm;
> > +
> > + if (test_bit(WX_FLAG_ERROR_CHECK, wx->flags)) {
> > + /* get PF semaphore */
> > + wr32(wx, WX_MIS_PF_SM, 1);
> > + clear_bit(WX_FLAG_ERROR_CHECK, wx->flags);
> > + }
> > +
> > + sm = rd32(wx, WX_MIS_PF_SM);
> > + /* PCIe memory space access error */
> > + if (sm == U32_MAX)
> > + goto out;
> > +
> > + /* PCIe error may be occurred in another port */
> > + if ((sm == 1 && wx_check_first_lan_up(wx)))
> > + goto out;
> > +
> > + return;
> > +out:
> > + set_bit(WX_FLAG_NEED_PCIE_RECOVER, wx->flags);
> > + wx_warn(wx, "Set PCIe recover on LAN %d\n", wx->bus.func);
> > +}
> > +
> > +static bool wx_check_recovery_capability(struct pci_dev *dev)
> > +{
> > +#if defined(__i386__) || defined(__x86_64__)
> > + return true;
> > +#else
> > + /* check upstream bridge is root or PLX bridge,
> > + * or if CPU is kunpeng 920
> > + */
> > + if (dev->bus->self->vendor == PCI_VENDOR_ID_PLX ||
> > + dev->bus->self->vendor == PCI_VENDOR_ID_HUAWEI)
>
> Can this dereference dev->bus->self without checking if it's NULL first?
>
> When the PCI device is directly attached to the root bus, dev->bus->self
> is NULL. On non-x86 platforms, this code checks the upstream bridge
> vendor without verifying that an upstream bridge exists.
>
> The function comment states "check upstream bridge is root or PLX bridge"
> but the code doesn't actually check for the root bus case. The x86 path
> returns true unconditionally, but the non-x86 path assumes dev->bus->self
> is non-NULL.
I think the comment here should be revised. On the non-x86 path, it should
returns true only when the upstream bridge is PLX or CPU is Kunpeng 920.
When dev->bus->self is null, it is necessary to return false. Although in reality,
it is not null either when device is directly attached to the root bus.
>
> > + return true;
> > + else
> > + return false;
> > +#endif
> > +}
>
> [ ... ]
>
> > diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> > index 3171a98d81a6..653a98d9835b 100644
> > --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> > +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> > @@ -234,6 +234,12 @@ static irqreturn_t __ngbe_msix_misc(struct wx *wx, u32 eicr)
> > if (eicr & NGBE_PX_MISC_IC_VF_MBOX)
> > wx_msg_task(wx);
> >
> > + if (eicr & NGBE_PX_MISC_PCIE_REQ_ERR) {
> > + wx_warn(wx, "PCIe Request Error founded on Lan %d\n",
> > + wx->bus.func);
>
> This isn't a bug, but there's a typo: 'founded' should be 'found'.
>
> > + wx_pcie_error_handler(wx);
> > + }
> > +
> > if (unlikely(eicr & NGBE_PX_MISC_IC_TIMESYNC))
> > wx_ptp_check_pps_event(wx);
> >
>
> [ ... ]
>
> > diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c
> > index aa14958d439a..c832100bcb61 100644
> > --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c
> > +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c
> > @@ -6,6 +6,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_sriov.h"
> > @@ -178,6 +179,12 @@ static irqreturn_t txgbe_misc_irq_thread_fn(int irq, void *data)
> > handle_nested_irq(sub_irq);
> > nhandled++;
> > }
> > + if (eicr & TXGBE_PX_MISC_PCIE_REQ_ERR) {
> > + wx_warn(wx, "PCIe Request Error founded on Lan %d\n",
> > + wx->bus.func);
>
> This isn't a bug, but there's a typo: 'founded' should be 'found'.
>
> > + wx_pcie_error_handler(wx);
> > + nhandled++;
> > + }
> > if (unlikely(eicr & TXGBE_PX_MISC_IC_TIMESYNC)) {
> > wx_ptp_check_pps_event(wx);
> > nhandled++;
>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH net-next v1 7/7] net: wangxun: add pcie error handler
2026-02-03 7:57 ` [PATCH net-next v1 7/7] net: wangxun: add pcie error handler Jiawen Wu
2026-02-04 1:34 ` kernel test robot
2026-02-04 2:22 ` [net-next,v1,7/7] " Jakub Kicinski
@ 2026-02-04 11:18 ` kernel test robot
2 siblings, 0 replies; 18+ messages in thread
From: kernel test robot @ 2026-02-04 11:18 UTC (permalink / raw)
To: Jiawen Wu, netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman
Cc: oe-kbuild-all, Mengyuan Lou, Jiawen Wu
Hi Jiawen,
kernel test robot noticed the following build errors:
[auto build test ERROR on net-next/main]
url: https://github.com/intel-lab-lkp/linux/commits/Jiawen-Wu/net-ngbe-move-the-WOL-functions-to-libwx/20260203-160343
base: net-next/main
patch link: https://lore.kernel.org/r/20260203075759.5852-8-jiawenwu%40trustnetic.com
patch subject: [PATCH net-next v1 7/7] net: wangxun: add pcie error handler
config: s390-allyesconfig (https://download.01.org/0day-ci/archive/20260204/202602041942.kNt5G253-lkp@intel.com/config)
compiler: s390-linux-gcc (GCC) 15.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260204/202602041942.kNt5G253-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602041942.kNt5G253-lkp@intel.com/
All errors (new ones prefixed by >>):
s390-linux-ld: drivers/net/ethernet/wangxun/libwx/wx_err.o: in function `wx_pcie_do_recovery':
>> wx_err.c:(.text.unlikely+0x214): undefined reference to `aer_recover_queue'
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 18+ messages in thread