Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'amd-xgbe-improve-power-management-for-s0i3'

Raju Rangoju says:

====================
amd-xgbe: Improve power management for S0i3

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.
====================

Link: https://patch.msgid.link/20260308092851.1510214-1-Raju.Rangoju@amd.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+99 -43
+20 -27
drivers/net/ethernet/amd/xgbe/xgbe-drv.c
··· 1116 1116 return pdata->phy_if.phy_reset(pdata); 1117 1117 } 1118 1118 1119 - int xgbe_powerdown(struct net_device *netdev, unsigned int caller) 1119 + int xgbe_powerdown(struct net_device *netdev) 1120 1120 { 1121 1121 struct xgbe_prv_data *pdata = netdev_priv(netdev); 1122 1122 struct xgbe_hw_if *hw_if = &pdata->hw_if; 1123 1123 1124 - DBGPR("-->xgbe_powerdown\n"); 1125 - 1126 - if (!netif_running(netdev) || 1127 - (caller == XGMAC_IOCTL_CONTEXT && pdata->power_down)) { 1128 - netdev_alert(netdev, "Device is already powered down\n"); 1129 - DBGPR("<--xgbe_powerdown\n"); 1124 + if (!netif_running(netdev)) { 1125 + netdev_dbg(netdev, "Device is not running, skipping powerdown\n"); 1130 1126 return -EINVAL; 1131 1127 } 1132 1128 1133 - if (caller == XGMAC_DRIVER_CONTEXT) 1134 - netif_device_detach(netdev); 1129 + if (pdata->power_down) { 1130 + netdev_dbg(netdev, "Device is already powered down\n"); 1131 + return -EINVAL; 1132 + } 1135 1133 1134 + netif_device_detach(netdev); 1136 1135 netif_tx_stop_all_queues(netdev); 1137 1136 1138 1137 xgbe_stop_timers(pdata); 1139 1138 flush_workqueue(pdata->dev_workqueue); 1140 1139 1140 + xgbe_napi_disable(pdata, 0); 1141 + 1141 1142 hw_if->powerdown_tx(pdata); 1142 1143 hw_if->powerdown_rx(pdata); 1143 1144 1144 - xgbe_napi_disable(pdata, 0); 1145 - 1146 1145 pdata->power_down = 1; 1147 - 1148 - DBGPR("<--xgbe_powerdown\n"); 1149 1146 1150 1147 return 0; 1151 1148 } 1152 1149 1153 - int xgbe_powerup(struct net_device *netdev, unsigned int caller) 1150 + int xgbe_powerup(struct net_device *netdev) 1154 1151 { 1155 1152 struct xgbe_prv_data *pdata = netdev_priv(netdev); 1156 1153 struct xgbe_hw_if *hw_if = &pdata->hw_if; 1157 1154 1158 - DBGPR("-->xgbe_powerup\n"); 1159 - 1160 - if (!netif_running(netdev) || 1161 - (caller == XGMAC_IOCTL_CONTEXT && !pdata->power_down)) { 1162 - netdev_alert(netdev, "Device is already powered up\n"); 1163 - DBGPR("<--xgbe_powerup\n"); 1155 + if (!netif_running(netdev)) { 1156 + netdev_dbg(netdev, "Device is not running, skipping powerup\n"); 1164 1157 return -EINVAL; 1165 1158 } 1166 1159 1167 - pdata->power_down = 0; 1168 - 1169 - xgbe_napi_enable(pdata, 0); 1160 + if (!pdata->power_down) { 1161 + netdev_dbg(netdev, "Device is already powered up\n"); 1162 + return -EINVAL; 1163 + } 1170 1164 1171 1165 hw_if->powerup_tx(pdata); 1172 1166 hw_if->powerup_rx(pdata); 1173 1167 1174 - if (caller == XGMAC_DRIVER_CONTEXT) 1175 - netif_device_attach(netdev); 1168 + xgbe_napi_enable(pdata, 0); 1176 1169 1177 1170 netif_tx_start_all_queues(netdev); 1178 - 1179 1171 xgbe_start_timers(pdata); 1172 + netif_device_attach(netdev); 1180 1173 1181 - DBGPR("<--xgbe_powerup\n"); 1174 + pdata->power_down = 0; 1182 1175 1183 1176 return 0; 1184 1177 }
+75 -8
drivers/net/ethernet/amd/xgbe/xgbe-pci.c
··· 360 360 xgbe_free_pdata(pdata); 361 361 } 362 362 363 - static int __maybe_unused xgbe_pci_suspend(struct device *dev) 363 + static void xgbe_pci_synchronize_irqs(struct xgbe_prv_data *pdata) 364 + { 365 + unsigned int i; 366 + 367 + /* Synchronize main device interrupt */ 368 + synchronize_irq(pdata->dev_irq); 369 + 370 + /* Synchronize ECC interrupt if separate from main device interrupt */ 371 + if (pdata->vdata->ecc_support && pdata->dev_irq != pdata->ecc_irq) 372 + synchronize_irq(pdata->ecc_irq); 373 + 374 + /* Synchronize I2C interrupt if separate from main device interrupt */ 375 + if (pdata->vdata->i2c_support && pdata->dev_irq != pdata->i2c_irq) 376 + synchronize_irq(pdata->i2c_irq); 377 + 378 + /* Synchronize AN interrupt if separate from main device interrupt */ 379 + if (pdata->dev_irq != pdata->an_irq) 380 + synchronize_irq(pdata->an_irq); 381 + 382 + /* Synchronize per-channel DMA interrupts */ 383 + if (pdata->per_channel_irq) { 384 + for (i = 0; i < pdata->channel_count; i++) 385 + synchronize_irq(pdata->channel[i]->dma_irq); 386 + } 387 + } 388 + 389 + static int xgbe_pci_suspend(struct device *dev) 364 390 { 365 391 struct xgbe_prv_data *pdata = dev_get_drvdata(dev); 366 392 struct net_device *netdev = pdata->netdev; 393 + struct pci_dev *pdev = to_pci_dev(dev); 367 394 int ret = 0; 368 395 369 396 if (netif_running(netdev)) 370 - ret = xgbe_powerdown(netdev, XGMAC_DRIVER_CONTEXT); 397 + ret = xgbe_powerdown(netdev); 371 398 399 + /* Disable all device interrupts to prevent spurious wakeups */ 400 + XP_IOWRITE(pdata, XP_INT_EN, 0x0); 401 + 402 + /* Ensure no IRQ handlers are still executing before powering down. 403 + * This prevents race conditions where an IRQ handler could access 404 + * invalid register state after the device is disabled. 405 + */ 406 + xgbe_pci_synchronize_irqs(pdata); 407 + 408 + /* Set PHY to low-power mode */ 372 409 pdata->lpm_ctrl = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); 373 410 pdata->lpm_ctrl |= MDIO_CTRL1_LPOWER; 374 411 XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, pdata->lpm_ctrl); 375 412 413 + /* Disable bus mastering to prevent DMA activity */ 414 + pci_clear_master(pdev); 415 + 416 + /* Save PCI configuration state and disable device */ 417 + pci_save_state(pdev); 418 + pci_disable_device(pdev); 419 + 420 + /* Disable wake from D3 - required for S0i3 deep sleep */ 421 + pci_wake_from_d3(pdev, false); 422 + pci_set_power_state(pdev, PCI_D3hot); 423 + 376 424 return ret; 377 425 } 378 426 379 - static int __maybe_unused xgbe_pci_resume(struct device *dev) 427 + static int xgbe_pci_resume(struct device *dev) 380 428 { 381 429 struct xgbe_prv_data *pdata = dev_get_drvdata(dev); 382 430 struct net_device *netdev = pdata->netdev; 431 + struct pci_dev *pdev = to_pci_dev(dev); 383 432 int ret = 0; 384 433 434 + /* Restore PCI power state */ 435 + pci_set_power_state(pdev, PCI_D0); 436 + 437 + /* Restore PCI configuration state */ 438 + pci_restore_state(pdev); 439 + 440 + /* Enable PCI device */ 441 + ret = pci_enable_device(pdev); 442 + if (ret) { 443 + dev_err(dev, "pci_enable_device failed: %d\n", ret); 444 + return ret; 445 + } 446 + 447 + /* Re-enable bus mastering */ 448 + pci_set_master(pdev); 449 + 450 + /* Re-enable all device interrupts */ 385 451 XP_IOWRITE(pdata, XP_INT_EN, 0x1fffff); 386 452 453 + /* Clear PHY low-power mode */ 387 454 pdata->lpm_ctrl &= ~MDIO_CTRL1_LPOWER; 388 455 XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, pdata->lpm_ctrl); 389 456 390 457 if (netif_running(netdev)) { 391 - ret = xgbe_powerup(netdev, XGMAC_DRIVER_CONTEXT); 458 + ret = xgbe_powerup(netdev); 392 459 393 460 /* Schedule a restart in case the link or phy state changed 394 461 * while we were powered down. ··· 528 461 }; 529 462 MODULE_DEVICE_TABLE(pci, xgbe_pci_table); 530 463 531 - static SIMPLE_DEV_PM_OPS(xgbe_pci_pm_ops, xgbe_pci_suspend, xgbe_pci_resume); 464 + static DEFINE_SIMPLE_DEV_PM_OPS(xgbe_pci_pm_ops, 465 + xgbe_pci_suspend, 466 + xgbe_pci_resume); 532 467 533 468 static struct pci_driver xgbe_driver = { 534 469 .name = XGBE_DRV_NAME, 535 470 .id_table = xgbe_pci_table, 536 471 .probe = xgbe_pci_probe, 537 472 .remove = xgbe_pci_remove, 538 - .driver = { 539 - .pm = &xgbe_pci_pm_ops, 540 - } 473 + .driver.pm = pm_sleep_ptr(&xgbe_pci_pm_ops), 541 474 }; 542 475 543 476 int xgbe_pci_init(void)
+2 -2
drivers/net/ethernet/amd/xgbe/xgbe-platform.c
··· 384 384 DBGPR("-->xgbe_suspend\n"); 385 385 386 386 if (netif_running(netdev)) 387 - ret = xgbe_powerdown(netdev, XGMAC_DRIVER_CONTEXT); 387 + ret = xgbe_powerdown(netdev); 388 388 389 389 pdata->lpm_ctrl = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); 390 390 pdata->lpm_ctrl |= MDIO_CTRL1_LPOWER; ··· 407 407 XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, pdata->lpm_ctrl); 408 408 409 409 if (netif_running(netdev)) { 410 - ret = xgbe_powerup(netdev, XGMAC_DRIVER_CONTEXT); 410 + ret = xgbe_powerup(netdev); 411 411 412 412 /* Schedule a restart in case the link or phy state changed 413 413 * while we were powered down.
+2 -6
drivers/net/ethernet/amd/xgbe/xgbe.h
··· 146 146 #define XGBE_MAX_PPS_OUT 4 147 147 #define XGBE_MAX_AUX_SNAP 4 148 148 149 - /* Driver PMT macros */ 150 - #define XGMAC_DRIVER_CONTEXT 1 151 - #define XGMAC_IOCTL_CONTEXT 2 152 - 153 149 #define XGMAC_FIFO_MIN_ALLOC 2048 154 150 #define XGMAC_FIFO_UNIT 256 155 151 #define XGMAC_FIFO_ALIGN(_x) \ ··· 1305 1309 unsigned int); 1306 1310 void xgbe_print_pkt(struct net_device *, struct sk_buff *, bool); 1307 1311 void xgbe_get_all_hw_features(struct xgbe_prv_data *); 1308 - int xgbe_powerup(struct net_device *, unsigned int); 1309 - int xgbe_powerdown(struct net_device *, unsigned int); 1312 + int xgbe_powerup(struct net_device *netdev); 1313 + int xgbe_powerdown(struct net_device *netdev); 1310 1314 void xgbe_init_rx_coalesce(struct xgbe_prv_data *); 1311 1315 void xgbe_init_tx_coalesce(struct xgbe_prv_data *); 1312 1316 void xgbe_restart_dev(struct xgbe_prv_data *pdata);