* [PATCH net-next v2 0/2] amd-xgbe: Improve power management for S0i3
@ 2026-03-08 9:28 Raju Rangoju
2026-03-08 9:28 ` [PATCH net-next v2 1/2] amd-xgbe: Simplify powerdown/powerup paths Raju Rangoju
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Raju Rangoju @ 2026-03-08 9:28 UTC (permalink / raw)
To: netdev
Cc: linux-kernel, pabeni, kuba, edumazet, davem, andrew+netdev,
Raju Rangoju
Improve the amd-xgbe power management handling to allow AMD platforms to
reach the deepest suspend state (S0i3) when modern standby is used.
The first patch cleans up the xgbe_powerdown() and xgbe_powerup()
helpers by removing an unused caller distinction and aligning the
ordering of operations with xgbe_stop().
The second patch adds proper PCI power management operations, following
the standard PCI PM model, so that the device can be cleanly put into
D3 and resumed back to D0. Without this, the amd_pmc driver reports:
"Last suspend didn't reach deepest state"
when the amd-xgbe driver is enabled.
These changes have been tested on AMD platforms using S0i3 modern
standby.
---
Changes since v1:
- Fix compiler warnings when CONFIG_PM_SLEEP is not enabled.
- Added a new helper function xgbe_pci_synchronize_irqs() that
synchronizes all registered IRQs to ensure that no pending IRQs are
left when the device is suspended.
Raju Rangoju (2):
amd-xgbe: Simplify powerdown/powerup paths
amd-xgbe: add PCI power management for S0i3 support
drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 47 +++++------
drivers/net/ethernet/amd/xgbe/xgbe-pci.c | 83 +++++++++++++++++--
drivers/net/ethernet/amd/xgbe/xgbe-platform.c | 4 +-
drivers/net/ethernet/amd/xgbe/xgbe.h | 8 +-
4 files changed, 99 insertions(+), 43 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH net-next v2 1/2] amd-xgbe: Simplify powerdown/powerup paths
2026-03-08 9:28 [PATCH net-next v2 0/2] amd-xgbe: Improve power management for S0i3 Raju Rangoju
@ 2026-03-08 9:28 ` Raju Rangoju
2026-03-08 9:28 ` [PATCH net-next v2 2/2] amd-xgbe: add PCI power management for S0i3 support Raju Rangoju
2026-03-11 3:00 ` [PATCH net-next v2 0/2] amd-xgbe: Improve power management for S0i3 patchwork-bot+netdevbpf
2 siblings, 0 replies; 4+ messages in thread
From: Raju Rangoju @ 2026-03-08 9:28 UTC (permalink / raw)
To: netdev
Cc: linux-kernel, pabeni, kuba, edumazet, davem, andrew+netdev,
Raju Rangoju
The caller parameter in xgbe_powerdown() and xgbe_powerup() was intended
to differentiate between driver and ioctl contexts, but the only
remaining usage is from the driver suspend/resume path.
Simplify this by:
- Removing the unused XGMAC_DRIVER_CONTEXT and XGMAC_IOCTL_CONTEXT
macros
- Dropping the now-unused caller parameter
- Reordering operations in xgbe_powerdown() to disable NAPI before
stopping TX/RX, matching the order used in xgbe_stop()
This makes the powerdown/powerup paths easier to follow and keeps the
ordering consistent with the rest of the driver.
Signed-off-by: Raju Rangoju <Raju.Rangoju@amd.com>
---
Changes since v1:
- Fix compiler warnings when CONFIG_PM_SLEEP is not enabled.
drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 47 ++++++++-----------
drivers/net/ethernet/amd/xgbe/xgbe-pci.c | 16 +++----
drivers/net/ethernet/amd/xgbe/xgbe-platform.c | 4 +-
drivers/net/ethernet/amd/xgbe/xgbe.h | 8 +---
4 files changed, 32 insertions(+), 43 deletions(-)
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
index 8b79d88480db..d57daf6306e1 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
@@ -1116,69 +1116,62 @@ static int xgbe_phy_reset(struct xgbe_prv_data *pdata)
return pdata->phy_if.phy_reset(pdata);
}
-int xgbe_powerdown(struct net_device *netdev, unsigned int caller)
+int xgbe_powerdown(struct net_device *netdev)
{
struct xgbe_prv_data *pdata = netdev_priv(netdev);
struct xgbe_hw_if *hw_if = &pdata->hw_if;
- DBGPR("-->xgbe_powerdown\n");
-
- if (!netif_running(netdev) ||
- (caller == XGMAC_IOCTL_CONTEXT && pdata->power_down)) {
- netdev_alert(netdev, "Device is already powered down\n");
- DBGPR("<--xgbe_powerdown\n");
+ if (!netif_running(netdev)) {
+ netdev_dbg(netdev, "Device is not running, skipping powerdown\n");
return -EINVAL;
}
- if (caller == XGMAC_DRIVER_CONTEXT)
- netif_device_detach(netdev);
+ if (pdata->power_down) {
+ netdev_dbg(netdev, "Device is already powered down\n");
+ return -EINVAL;
+ }
+ netif_device_detach(netdev);
netif_tx_stop_all_queues(netdev);
xgbe_stop_timers(pdata);
flush_workqueue(pdata->dev_workqueue);
+ xgbe_napi_disable(pdata, 0);
+
hw_if->powerdown_tx(pdata);
hw_if->powerdown_rx(pdata);
- xgbe_napi_disable(pdata, 0);
-
pdata->power_down = 1;
- DBGPR("<--xgbe_powerdown\n");
-
return 0;
}
-int xgbe_powerup(struct net_device *netdev, unsigned int caller)
+int xgbe_powerup(struct net_device *netdev)
{
struct xgbe_prv_data *pdata = netdev_priv(netdev);
struct xgbe_hw_if *hw_if = &pdata->hw_if;
- DBGPR("-->xgbe_powerup\n");
-
- if (!netif_running(netdev) ||
- (caller == XGMAC_IOCTL_CONTEXT && !pdata->power_down)) {
- netdev_alert(netdev, "Device is already powered up\n");
- DBGPR("<--xgbe_powerup\n");
+ if (!netif_running(netdev)) {
+ netdev_dbg(netdev, "Device is not running, skipping powerup\n");
return -EINVAL;
}
- pdata->power_down = 0;
-
- xgbe_napi_enable(pdata, 0);
+ if (!pdata->power_down) {
+ netdev_dbg(netdev, "Device is already powered up\n");
+ return -EINVAL;
+ }
hw_if->powerup_tx(pdata);
hw_if->powerup_rx(pdata);
- if (caller == XGMAC_DRIVER_CONTEXT)
- netif_device_attach(netdev);
+ xgbe_napi_enable(pdata, 0);
netif_tx_start_all_queues(netdev);
-
xgbe_start_timers(pdata);
+ netif_device_attach(netdev);
- DBGPR("<--xgbe_powerup\n");
+ pdata->power_down = 0;
return 0;
}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c
index f54a5040a493..9e09b269cb1d 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c
@@ -360,14 +360,14 @@ static void xgbe_pci_remove(struct pci_dev *pdev)
xgbe_free_pdata(pdata);
}
-static int __maybe_unused xgbe_pci_suspend(struct device *dev)
+static int xgbe_pci_suspend(struct device *dev)
{
struct xgbe_prv_data *pdata = dev_get_drvdata(dev);
struct net_device *netdev = pdata->netdev;
int ret = 0;
if (netif_running(netdev))
- ret = xgbe_powerdown(netdev, XGMAC_DRIVER_CONTEXT);
+ ret = xgbe_powerdown(netdev);
pdata->lpm_ctrl = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1);
pdata->lpm_ctrl |= MDIO_CTRL1_LPOWER;
@@ -376,7 +376,7 @@ static int __maybe_unused xgbe_pci_suspend(struct device *dev)
return ret;
}
-static int __maybe_unused xgbe_pci_resume(struct device *dev)
+static int xgbe_pci_resume(struct device *dev)
{
struct xgbe_prv_data *pdata = dev_get_drvdata(dev);
struct net_device *netdev = pdata->netdev;
@@ -388,7 +388,7 @@ static int __maybe_unused xgbe_pci_resume(struct device *dev)
XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, pdata->lpm_ctrl);
if (netif_running(netdev)) {
- ret = xgbe_powerup(netdev, XGMAC_DRIVER_CONTEXT);
+ ret = xgbe_powerup(netdev);
/* Schedule a restart in case the link or phy state changed
* while we were powered down.
@@ -461,16 +461,16 @@ static const struct pci_device_id xgbe_pci_table[] = {
};
MODULE_DEVICE_TABLE(pci, xgbe_pci_table);
-static SIMPLE_DEV_PM_OPS(xgbe_pci_pm_ops, xgbe_pci_suspend, xgbe_pci_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(xgbe_pci_pm_ops,
+ xgbe_pci_suspend,
+ xgbe_pci_resume);
static struct pci_driver xgbe_driver = {
.name = XGBE_DRV_NAME,
.id_table = xgbe_pci_table,
.probe = xgbe_pci_probe,
.remove = xgbe_pci_remove,
- .driver = {
- .pm = &xgbe_pci_pm_ops,
- }
+ .driver.pm = pm_sleep_ptr(&xgbe_pci_pm_ops),
};
int xgbe_pci_init(void)
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-platform.c b/drivers/net/ethernet/amd/xgbe/xgbe-platform.c
index 47d53e59ccf6..98b03a3f3a95 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-platform.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-platform.c
@@ -384,7 +384,7 @@ static int xgbe_platform_suspend(struct device *dev)
DBGPR("-->xgbe_suspend\n");
if (netif_running(netdev))
- ret = xgbe_powerdown(netdev, XGMAC_DRIVER_CONTEXT);
+ ret = xgbe_powerdown(netdev);
pdata->lpm_ctrl = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1);
pdata->lpm_ctrl |= MDIO_CTRL1_LPOWER;
@@ -407,7 +407,7 @@ static int xgbe_platform_resume(struct device *dev)
XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, pdata->lpm_ctrl);
if (netif_running(netdev)) {
- ret = xgbe_powerup(netdev, XGMAC_DRIVER_CONTEXT);
+ ret = xgbe_powerup(netdev);
/* Schedule a restart in case the link or phy state changed
* while we were powered down.
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h
index 4333d269ee84..3b9345506c2b 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe.h
+++ b/drivers/net/ethernet/amd/xgbe/xgbe.h
@@ -146,10 +146,6 @@
#define XGBE_MAX_PPS_OUT 4
#define XGBE_MAX_AUX_SNAP 4
-/* Driver PMT macros */
-#define XGMAC_DRIVER_CONTEXT 1
-#define XGMAC_IOCTL_CONTEXT 2
-
#define XGMAC_FIFO_MIN_ALLOC 2048
#define XGMAC_FIFO_UNIT 256
#define XGMAC_FIFO_ALIGN(_x) \
@@ -1309,8 +1305,8 @@ void xgbe_dump_rx_desc(struct xgbe_prv_data *, struct xgbe_ring *,
unsigned int);
void xgbe_print_pkt(struct net_device *, struct sk_buff *, bool);
void xgbe_get_all_hw_features(struct xgbe_prv_data *);
-int xgbe_powerup(struct net_device *, unsigned int);
-int xgbe_powerdown(struct net_device *, unsigned int);
+int xgbe_powerup(struct net_device *netdev);
+int xgbe_powerdown(struct net_device *netdev);
void xgbe_init_rx_coalesce(struct xgbe_prv_data *);
void xgbe_init_tx_coalesce(struct xgbe_prv_data *);
void xgbe_restart_dev(struct xgbe_prv_data *pdata);
--
2.34.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH net-next v2 2/2] amd-xgbe: add PCI power management for S0i3 support
2026-03-08 9:28 [PATCH net-next v2 0/2] amd-xgbe: Improve power management for S0i3 Raju Rangoju
2026-03-08 9:28 ` [PATCH net-next v2 1/2] amd-xgbe: Simplify powerdown/powerup paths Raju Rangoju
@ 2026-03-08 9:28 ` Raju Rangoju
2026-03-11 3:00 ` [PATCH net-next v2 0/2] amd-xgbe: Improve power management for S0i3 patchwork-bot+netdevbpf
2 siblings, 0 replies; 4+ messages in thread
From: Raju Rangoju @ 2026-03-08 9:28 UTC (permalink / raw)
To: netdev
Cc: linux-kernel, pabeni, kuba, edumazet, davem, andrew+netdev,
Raju Rangoju
The current suspend/resume implementation does not correctly handle PCI
device power state transitions, which prevents AMD platforms from
reaching the deepest suspend state (S0i3) when the amd-xgbe driver is
enabled.
In particular, the amd_pmc driver reports:
"Last suspend didn't reach deepest state"
when this device is present.
Implement proper PCI power management operations following the standard
PCI PM model so that the device can be cleanly powered down and resumed.
Suspend path:
- Power down the network interface
- Put the PHY into low-power mode
- Disable bus mastering to prevent DMA activity
- Save PCI configuration space
- Disable the PCI device
- Disable wake from D3 (S0i3 does not require Wake-on-LAN)
- Set the device to D3hot
Resume path:
- Restore the PCI power state to D0
- Restore PCI configuration space
- Enable the PCI device
- Re-enable bus mastering
- Re-enable device interrupts
- Clear the PHY low-power mode
- Power up the network interface
This allows systems using amd-xgbe to reach the deepest suspend state
when entering modern standby (S0i3).
Signed-off-by: Raju Rangoju <Raju.Rangoju@amd.com>
---
Changes since v1:
- Added a new helper function xgbe_pci_synchronize_irqs() that
synchronizes all registered IRQs to ensure that no pending IRQs are
left when the device is suspended.
drivers/net/ethernet/amd/xgbe/xgbe-pci.c | 67 ++++++++++++++++++++++++
1 file changed, 67 insertions(+)
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c
index 9e09b269cb1d..dbdd791380a4 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c
@@ -360,19 +360,67 @@ static void xgbe_pci_remove(struct pci_dev *pdev)
xgbe_free_pdata(pdata);
}
+static void xgbe_pci_synchronize_irqs(struct xgbe_prv_data *pdata)
+{
+ unsigned int i;
+
+ /* Synchronize main device interrupt */
+ synchronize_irq(pdata->dev_irq);
+
+ /* Synchronize ECC interrupt if separate from main device interrupt */
+ if (pdata->vdata->ecc_support && pdata->dev_irq != pdata->ecc_irq)
+ synchronize_irq(pdata->ecc_irq);
+
+ /* Synchronize I2C interrupt if separate from main device interrupt */
+ if (pdata->vdata->i2c_support && pdata->dev_irq != pdata->i2c_irq)
+ synchronize_irq(pdata->i2c_irq);
+
+ /* Synchronize AN interrupt if separate from main device interrupt */
+ if (pdata->dev_irq != pdata->an_irq)
+ synchronize_irq(pdata->an_irq);
+
+ /* Synchronize per-channel DMA interrupts */
+ if (pdata->per_channel_irq) {
+ for (i = 0; i < pdata->channel_count; i++)
+ synchronize_irq(pdata->channel[i]->dma_irq);
+ }
+}
+
static int xgbe_pci_suspend(struct device *dev)
{
struct xgbe_prv_data *pdata = dev_get_drvdata(dev);
struct net_device *netdev = pdata->netdev;
+ struct pci_dev *pdev = to_pci_dev(dev);
int ret = 0;
if (netif_running(netdev))
ret = xgbe_powerdown(netdev);
+ /* Disable all device interrupts to prevent spurious wakeups */
+ XP_IOWRITE(pdata, XP_INT_EN, 0x0);
+
+ /* Ensure no IRQ handlers are still executing before powering down.
+ * This prevents race conditions where an IRQ handler could access
+ * invalid register state after the device is disabled.
+ */
+ xgbe_pci_synchronize_irqs(pdata);
+
+ /* Set PHY to low-power mode */
pdata->lpm_ctrl = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1);
pdata->lpm_ctrl |= MDIO_CTRL1_LPOWER;
XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, pdata->lpm_ctrl);
+ /* Disable bus mastering to prevent DMA activity */
+ pci_clear_master(pdev);
+
+ /* Save PCI configuration state and disable device */
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+
+ /* Disable wake from D3 - required for S0i3 deep sleep */
+ pci_wake_from_d3(pdev, false);
+ pci_set_power_state(pdev, PCI_D3hot);
+
return ret;
}
@@ -380,10 +428,29 @@ static int xgbe_pci_resume(struct device *dev)
{
struct xgbe_prv_data *pdata = dev_get_drvdata(dev);
struct net_device *netdev = pdata->netdev;
+ struct pci_dev *pdev = to_pci_dev(dev);
int ret = 0;
+ /* Restore PCI power state */
+ pci_set_power_state(pdev, PCI_D0);
+
+ /* Restore PCI configuration state */
+ pci_restore_state(pdev);
+
+ /* Enable PCI device */
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ dev_err(dev, "pci_enable_device failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Re-enable bus mastering */
+ pci_set_master(pdev);
+
+ /* Re-enable all device interrupts */
XP_IOWRITE(pdata, XP_INT_EN, 0x1fffff);
+ /* Clear PHY low-power mode */
pdata->lpm_ctrl &= ~MDIO_CTRL1_LPOWER;
XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, pdata->lpm_ctrl);
--
2.34.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH net-next v2 0/2] amd-xgbe: Improve power management for S0i3
2026-03-08 9:28 [PATCH net-next v2 0/2] amd-xgbe: Improve power management for S0i3 Raju Rangoju
2026-03-08 9:28 ` [PATCH net-next v2 1/2] amd-xgbe: Simplify powerdown/powerup paths Raju Rangoju
2026-03-08 9:28 ` [PATCH net-next v2 2/2] amd-xgbe: add PCI power management for S0i3 support Raju Rangoju
@ 2026-03-11 3:00 ` patchwork-bot+netdevbpf
2 siblings, 0 replies; 4+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-03-11 3:00 UTC (permalink / raw)
To: Raju Rangoju
Cc: netdev, linux-kernel, pabeni, kuba, edumazet, davem,
andrew+netdev
Hello:
This series was applied to netdev/net-next.git (main)
by Jakub Kicinski <kuba@kernel.org>:
On Sun, 8 Mar 2026 14:58:49 +0530 you wrote:
> Improve the amd-xgbe power management handling to allow AMD platforms to
> reach the deepest suspend state (S0i3) when modern standby is used.
>
> The first patch cleans up the xgbe_powerdown() and xgbe_powerup()
> helpers by removing an unused caller distinction and aligning the
> ordering of operations with xgbe_stop().
>
> [...]
Here is the summary with links:
- [net-next,v2,1/2] amd-xgbe: Simplify powerdown/powerup paths
https://git.kernel.org/netdev/net-next/c/fe81629217e0
- [net-next,v2,2/2] amd-xgbe: add PCI power management for S0i3 support
https://git.kernel.org/netdev/net-next/c/7644e76956ba
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-03-11 3:00 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-08 9:28 [PATCH net-next v2 0/2] amd-xgbe: Improve power management for S0i3 Raju Rangoju
2026-03-08 9:28 ` [PATCH net-next v2 1/2] amd-xgbe: Simplify powerdown/powerup paths Raju Rangoju
2026-03-08 9:28 ` [PATCH net-next v2 2/2] amd-xgbe: add PCI power management for S0i3 support Raju Rangoju
2026-03-11 3:00 ` [PATCH net-next v2 0/2] amd-xgbe: Improve power management for S0i3 patchwork-bot+netdevbpf
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox