Merge branch 'net-stmmac-improbe-suspend-resume-architecture'

Russell King says:

====================
net: stmmac: improbe suspend/resume architecture

This series improves the stmmac suspend/resume architecture by
providing a couple of method hooks in struct plat_stmmacenet_data which
are called by core code, and thus are available for any of the
platform glue drivers, whether using a platform or PCI device.

As these methods are called by core code, we can also provide a simple
PM ops structure also in the core code for converted glue drivers to
use.

The remainder of the patches convert the various drivers.
====================

Link: https://patch.msgid.link/aJo7kvoub5voHOUQ@shell.armlinux.org.uk
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2025-08-12 18:04:56 -07:00
commit 0a529da8cf
10 changed files with 195 additions and 241 deletions

View File

@ -1231,6 +1231,37 @@ static int stmmac_config_multi_msi(struct pci_dev *pdev,
return 0;
}
static int intel_eth_pci_suspend(struct device *dev, void *bsp_priv)
{
struct pci_dev *pdev = to_pci_dev(dev);
int ret;
ret = pci_save_state(pdev);
if (ret)
return ret;
pci_wake_from_d3(pdev, true);
pci_set_power_state(pdev, PCI_D3hot);
return 0;
}
static int intel_eth_pci_resume(struct device *dev, void *bsp_priv)
{
struct pci_dev *pdev = to_pci_dev(dev);
int ret;
pci_restore_state(pdev);
pci_set_power_state(pdev, PCI_D0);
ret = pcim_enable_device(pdev);
if (ret)
return ret;
pci_set_master(pdev);
return 0;
}
/**
* intel_eth_pci_probe
*
@ -1292,6 +1323,9 @@ static int intel_eth_pci_probe(struct pci_dev *pdev,
pci_set_master(pdev);
plat->bsp_priv = intel_priv;
plat->suspend = intel_eth_pci_suspend;
plat->resume = intel_eth_pci_resume;
intel_priv->mdio_adhoc_addr = INTEL_MGBE_ADHOC_ADDR;
intel_priv->crossts_adj = 1;
@ -1355,44 +1389,6 @@ static void intel_eth_pci_remove(struct pci_dev *pdev)
clk_unregister_fixed_rate(priv->plat->stmmac_clk);
}
static int __maybe_unused intel_eth_pci_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
int ret;
ret = stmmac_suspend(dev);
if (ret)
return ret;
ret = pci_save_state(pdev);
if (ret)
return ret;
pci_wake_from_d3(pdev, true);
pci_set_power_state(pdev, PCI_D3hot);
return 0;
}
static int __maybe_unused intel_eth_pci_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
int ret;
pci_restore_state(pdev);
pci_set_power_state(pdev, PCI_D0);
ret = pcim_enable_device(pdev);
if (ret)
return ret;
pci_set_master(pdev);
return stmmac_resume(dev);
}
static SIMPLE_DEV_PM_OPS(intel_eth_pm_ops, intel_eth_pci_suspend,
intel_eth_pci_resume);
#define PCI_DEVICE_ID_INTEL_QUARK 0x0937
#define PCI_DEVICE_ID_INTEL_EHL_RGMII1G 0x4b30
#define PCI_DEVICE_ID_INTEL_EHL_SGMII1G 0x4b31
@ -1442,7 +1438,7 @@ static struct pci_driver intel_eth_pci_driver = {
.probe = intel_eth_pci_probe,
.remove = intel_eth_pci_remove,
.driver = {
.pm = &intel_eth_pm_ops,
.pm = &stmmac_simple_pm_ops,
},
};

View File

@ -521,6 +521,37 @@ static int loongson_dwmac_fix_reset(void *priv, void __iomem *ioaddr)
10000, 2000000);
}
static int loongson_dwmac_suspend(struct device *dev, void *bsp_priv)
{
struct pci_dev *pdev = to_pci_dev(dev);
int ret;
ret = pci_save_state(pdev);
if (ret)
return ret;
pci_disable_device(pdev);
pci_wake_from_d3(pdev, true);
return 0;
}
static int loongson_dwmac_resume(struct device *dev, void *bsp_priv)
{
struct pci_dev *pdev = to_pci_dev(dev);
int ret;
pci_restore_state(pdev);
pci_set_power_state(pdev, PCI_D0);
ret = pci_enable_device(pdev);
if (ret)
return ret;
pci_set_master(pdev);
return 0;
}
static int loongson_dwmac_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct plat_stmmacenet_data *plat;
@ -565,6 +596,8 @@ static int loongson_dwmac_probe(struct pci_dev *pdev, const struct pci_device_id
plat->bsp_priv = ld;
plat->setup = loongson_dwmac_setup;
plat->fix_soc_reset = loongson_dwmac_fix_reset;
plat->suspend = loongson_dwmac_suspend;
plat->resume = loongson_dwmac_resume;
ld->dev = &pdev->dev;
ld->loongson_id = readl(res.addr + GMAC_VERSION) & 0xff;
@ -621,44 +654,6 @@ static void loongson_dwmac_remove(struct pci_dev *pdev)
pci_disable_device(pdev);
}
static int __maybe_unused loongson_dwmac_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
int ret;
ret = stmmac_suspend(dev);
if (ret)
return ret;
ret = pci_save_state(pdev);
if (ret)
return ret;
pci_disable_device(pdev);
pci_wake_from_d3(pdev, true);
return 0;
}
static int __maybe_unused loongson_dwmac_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
int ret;
pci_restore_state(pdev);
pci_set_power_state(pdev, PCI_D0);
ret = pci_enable_device(pdev);
if (ret)
return ret;
pci_set_master(pdev);
return stmmac_resume(dev);
}
static SIMPLE_DEV_PM_OPS(loongson_dwmac_pm_ops, loongson_dwmac_suspend,
loongson_dwmac_resume);
static const struct pci_device_id loongson_dwmac_id_table[] = {
{ PCI_DEVICE_DATA(LOONGSON, GMAC1, &loongson_gmac_pci_info) },
{ PCI_DEVICE_DATA(LOONGSON, GMAC2, &loongson_gmac_pci_info) },
@ -673,7 +668,7 @@ static struct pci_driver loongson_dwmac_driver = {
.probe = loongson_dwmac_probe,
.remove = loongson_dwmac_remove,
.driver = {
.pm = &loongson_dwmac_pm_ops,
.pm = &stmmac_simple_pm_ops,
},
};

View File

@ -523,7 +523,7 @@ static int mediatek_dwmac_clk_init(struct mediatek_dwmac_plat_data *plat)
return ret;
}
static int mediatek_dwmac_init(struct platform_device *pdev, void *priv)
static int mediatek_dwmac_init(struct device *dev, void *priv)
{
struct mediatek_dwmac_plat_data *plat = priv;
const struct mediatek_dwmac_variant *variant = plat->variant;
@ -532,7 +532,7 @@ static int mediatek_dwmac_init(struct platform_device *pdev, void *priv)
if (variant->dwmac_set_phy_interface) {
ret = variant->dwmac_set_phy_interface(plat);
if (ret) {
dev_err(plat->dev, "failed to set phy interface, err = %d\n", ret);
dev_err(dev, "failed to set phy interface, err = %d\n", ret);
return ret;
}
}
@ -540,7 +540,7 @@ static int mediatek_dwmac_init(struct platform_device *pdev, void *priv)
if (variant->dwmac_set_delay) {
ret = variant->dwmac_set_delay(plat);
if (ret) {
dev_err(plat->dev, "failed to set delay value, err = %d\n", ret);
dev_err(dev, "failed to set delay value, err = %d\n", ret);
return ret;
}
}
@ -589,7 +589,7 @@ static int mediatek_dwmac_common_data(struct platform_device *pdev,
plat->maxmtu = ETH_DATA_LEN;
plat->host_dma_width = priv_plat->variant->dma_bit_mask;
plat->bsp_priv = priv_plat;
plat->init = mediatek_dwmac_init;
plat->resume = mediatek_dwmac_init;
plat->clks_config = mediatek_dwmac_clks_config;
plat->safety_feat_cfg = devm_kzalloc(&pdev->dev,
@ -654,7 +654,7 @@ static int mediatek_dwmac_probe(struct platform_device *pdev)
return PTR_ERR(plat_dat);
mediatek_dwmac_common_data(pdev, plat_dat, priv_plat);
mediatek_dwmac_init(pdev, priv_plat);
mediatek_dwmac_init(&pdev->dev, priv_plat);
ret = mediatek_dwmac_clks_config(priv_plat, true);
if (ret)

View File

@ -71,7 +71,6 @@ struct rk_priv_data {
phy_interface_t phy_iface;
int id;
struct regulator *regulator;
bool suspended;
const struct rk_gmac_ops *ops;
bool clk_enabled;
@ -1706,6 +1705,28 @@ static int rk_set_clk_tx_rate(void *bsp_priv_, struct clk *clk_tx_i,
return -EINVAL;
}
static int rk_gmac_suspend(struct device *dev, void *bsp_priv_)
{
struct rk_priv_data *bsp_priv = bsp_priv_;
/* Keep the PHY up if we use Wake-on-Lan. */
if (!device_may_wakeup(dev))
rk_gmac_powerdown(bsp_priv);
return 0;
}
static int rk_gmac_resume(struct device *dev, void *bsp_priv_)
{
struct rk_priv_data *bsp_priv = bsp_priv_;
/* The PHY was up for Wake-on-Lan. */
if (!device_may_wakeup(dev))
rk_gmac_powerup(bsp_priv);
return 0;
}
static int rk_gmac_probe(struct platform_device *pdev)
{
struct plat_stmmacenet_data *plat_dat;
@ -1738,6 +1759,8 @@ static int rk_gmac_probe(struct platform_device *pdev)
plat_dat->get_interfaces = rk_get_interfaces;
plat_dat->set_clk_tx_rate = rk_set_clk_tx_rate;
plat_dat->suspend = rk_gmac_suspend;
plat_dat->resume = rk_gmac_resume;
plat_dat->bsp_priv = rk_gmac_setup(pdev, plat_dat, data);
if (IS_ERR(plat_dat->bsp_priv))
@ -1772,37 +1795,6 @@ static void rk_gmac_remove(struct platform_device *pdev)
rk_gmac_powerdown(bsp_priv);
}
#ifdef CONFIG_PM_SLEEP
static int rk_gmac_suspend(struct device *dev)
{
struct rk_priv_data *bsp_priv = get_stmmac_bsp_priv(dev);
int ret = stmmac_suspend(dev);
/* Keep the PHY up if we use Wake-on-Lan. */
if (!device_may_wakeup(dev)) {
rk_gmac_powerdown(bsp_priv);
bsp_priv->suspended = true;
}
return ret;
}
static int rk_gmac_resume(struct device *dev)
{
struct rk_priv_data *bsp_priv = get_stmmac_bsp_priv(dev);
/* The PHY was up for Wake-on-Lan. */
if (bsp_priv->suspended) {
rk_gmac_powerup(bsp_priv);
bsp_priv->suspended = false;
}
return stmmac_resume(dev);
}
#endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(rk_gmac_pm_ops, rk_gmac_suspend, rk_gmac_resume);
static const struct of_device_id rk_gmac_dwmac_match[] = {
{ .compatible = "rockchip,px30-gmac", .data = &px30_ops },
{ .compatible = "rockchip,rk3128-gmac", .data = &rk3128_ops },
@ -1828,7 +1820,7 @@ static struct platform_driver rk_gmac_dwmac_driver = {
.remove = rk_gmac_remove,
.driver = {
.name = "rk_gmac-dwmac",
.pm = &rk_gmac_pm_ops,
.pm = &stmmac_simple_pm_ops,
.of_match_table = rk_gmac_dwmac_match,
},
};

View File

@ -498,6 +498,26 @@ static int stm32mp1_parse_data(struct stm32_dwmac *dwmac,
return err;
}
static int stm32_dwmac_suspend(struct device *dev, void *bsp_priv)
{
struct stm32_dwmac *dwmac = bsp_priv;
stm32_dwmac_clk_disable(dwmac);
return dwmac->ops->suspend ? dwmac->ops->suspend(dwmac) : 0;
}
static int stm32_dwmac_resume(struct device *dev, void *bsp_priv)
{
struct stmmac_priv *priv = netdev_priv(dev_get_drvdata(dev));
struct stm32_dwmac *dwmac = bsp_priv;
if (dwmac->ops->resume)
dwmac->ops->resume(dwmac);
return stm32_dwmac_init(priv->plat);
}
static int stm32_dwmac_probe(struct platform_device *pdev)
{
struct plat_stmmacenet_data *plat_dat;
@ -535,6 +555,8 @@ static int stm32_dwmac_probe(struct platform_device *pdev)
plat_dat->flags |= STMMAC_FLAG_EN_TX_LPI_CLK_PHY_CAP;
plat_dat->bsp_priv = dwmac;
plat_dat->suspend = stm32_dwmac_suspend;
plat_dat->resume = stm32_dwmac_resume;
ret = stm32_dwmac_init(plat_dat);
if (ret)
@ -600,50 +622,6 @@ static void stm32mp1_resume(struct stm32_dwmac *dwmac)
clk_disable_unprepare(dwmac->clk_ethstp);
}
#ifdef CONFIG_PM_SLEEP
static int stm32_dwmac_suspend(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
struct stmmac_priv *priv = netdev_priv(ndev);
struct stm32_dwmac *dwmac = priv->plat->bsp_priv;
int ret;
ret = stmmac_suspend(dev);
if (ret)
return ret;
stm32_dwmac_clk_disable(dwmac);
if (dwmac->ops->suspend)
ret = dwmac->ops->suspend(dwmac);
return ret;
}
static int stm32_dwmac_resume(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
struct stmmac_priv *priv = netdev_priv(ndev);
struct stm32_dwmac *dwmac = priv->plat->bsp_priv;
int ret;
if (dwmac->ops->resume)
dwmac->ops->resume(dwmac);
ret = stm32_dwmac_init(priv->plat);
if (ret)
return ret;
ret = stmmac_resume(dev);
return ret;
}
#endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(stm32_dwmac_pm_ops,
stm32_dwmac_suspend, stm32_dwmac_resume);
static struct stm32_ops stm32mcu_dwmac_data = {
.set_mode = stm32mcu_set_mode
};
@ -691,7 +669,7 @@ static struct platform_driver stm32_dwmac_driver = {
.remove = stm32_dwmac_remove,
.driver = {
.name = "stm32-dwmac",
.pm = &stm32_dwmac_pm_ops,
.pm = &stmmac_simple_pm_ops,
.of_match_table = stm32_dwmac_match,
},
};

View File

@ -374,6 +374,8 @@ enum stmmac_state {
STMMAC_SERVICE_SCHED,
};
extern const struct dev_pm_ops stmmac_simple_pm_ops;
int stmmac_mdio_unregister(struct net_device *ndev);
int stmmac_mdio_register(struct net_device *ndev);
int stmmac_mdio_reset(struct mii_bus *mii);

View File

@ -7879,6 +7879,9 @@ int stmmac_suspend(struct device *dev)
if (stmmac_fpe_supported(priv))
ethtool_mmsv_stop(&priv->fpe_cfg.mmsv);
if (priv->plat->suspend)
return priv->plat->suspend(dev, priv->plat->bsp_priv);
return 0;
}
EXPORT_SYMBOL_GPL(stmmac_suspend);
@ -7931,6 +7934,12 @@ int stmmac_resume(struct device *dev)
struct stmmac_priv *priv = netdev_priv(ndev);
int ret;
if (priv->plat->resume) {
ret = priv->plat->resume(dev, priv->plat->bsp_priv);
if (ret)
return ret;
}
if (!netif_running(ndev))
return 0;
@ -8004,6 +8013,9 @@ int stmmac_resume(struct device *dev)
}
EXPORT_SYMBOL_GPL(stmmac_resume);
EXPORT_GPL_SIMPLE_DEV_PM_OPS(stmmac_simple_pm_ops, stmmac_suspend,
stmmac_resume);
#ifndef MODULE
static int __init stmmac_cmdline_opt(char *str)
{

View File

@ -138,6 +138,37 @@ static const struct stmmac_pci_info snps_gmac5_pci_info = {
.setup = snps_gmac5_default_data,
};
static int stmmac_pci_suspend(struct device *dev, void *bsp_priv)
{
struct pci_dev *pdev = to_pci_dev(dev);
int ret;
ret = pci_save_state(pdev);
if (ret)
return ret;
pci_disable_device(pdev);
pci_wake_from_d3(pdev, true);
return 0;
}
static int stmmac_pci_resume(struct device *dev, void *bsp_priv)
{
struct pci_dev *pdev = to_pci_dev(dev);
int ret;
pci_restore_state(pdev);
pci_set_power_state(pdev, PCI_D0);
ret = pci_enable_device(pdev);
if (ret)
return ret;
pci_set_master(pdev);
return 0;
}
/**
* stmmac_pci_probe
*
@ -217,6 +248,9 @@ static int stmmac_pci_probe(struct pci_dev *pdev,
plat->safety_feat_cfg->prtyen = 1;
plat->safety_feat_cfg->tmouten = 1;
plat->suspend = stmmac_pci_suspend;
plat->resume = stmmac_pci_resume;
return stmmac_dvr_probe(&pdev->dev, plat, &res);
}
@ -231,43 +265,6 @@ static void stmmac_pci_remove(struct pci_dev *pdev)
stmmac_dvr_remove(&pdev->dev);
}
static int __maybe_unused stmmac_pci_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
int ret;
ret = stmmac_suspend(dev);
if (ret)
return ret;
ret = pci_save_state(pdev);
if (ret)
return ret;
pci_disable_device(pdev);
pci_wake_from_d3(pdev, true);
return 0;
}
static int __maybe_unused stmmac_pci_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
int ret;
pci_restore_state(pdev);
pci_set_power_state(pdev, PCI_D0);
ret = pci_enable_device(pdev);
if (ret)
return ret;
pci_set_master(pdev);
return stmmac_resume(dev);
}
static SIMPLE_DEV_PM_OPS(stmmac_pm_ops, stmmac_pci_suspend, stmmac_pci_resume);
/* synthetic ID, no official vendor */
#define PCI_VENDOR_ID_STMMAC 0x0700
@ -289,7 +286,7 @@ static struct pci_driver stmmac_pci_driver = {
.probe = stmmac_pci_probe,
.remove = stmmac_pci_remove,
.driver = {
.pm = &stmmac_pm_ops,
.pm = &stmmac_simple_pm_ops,
},
};

View File

@ -811,6 +811,22 @@ static void stmmac_pltfr_exit(struct platform_device *pdev,
plat->exit(pdev, plat->bsp_priv);
}
static int stmmac_plat_suspend(struct device *dev, void *bsp_priv)
{
struct stmmac_priv *priv = netdev_priv(dev_get_drvdata(dev));
stmmac_pltfr_exit(to_platform_device(dev), priv->plat);
return 0;
}
static int stmmac_plat_resume(struct device *dev, void *bsp_priv)
{
struct stmmac_priv *priv = netdev_priv(dev_get_drvdata(dev));
return stmmac_pltfr_init(to_platform_device(dev), priv->plat);
}
/**
* stmmac_pltfr_probe
* @pdev: platform device pointer
@ -825,6 +841,11 @@ int stmmac_pltfr_probe(struct platform_device *pdev,
{
int ret;
if (!plat->suspend && plat->exit)
plat->suspend = stmmac_plat_suspend;
if (!plat->resume && plat->init)
plat->resume = stmmac_plat_resume;
ret = stmmac_pltfr_init(pdev, plat);
if (ret)
return ret;
@ -886,47 +907,6 @@ void stmmac_pltfr_remove(struct platform_device *pdev)
}
EXPORT_SYMBOL_GPL(stmmac_pltfr_remove);
/**
* stmmac_pltfr_suspend
* @dev: device pointer
* Description: this function is invoked when suspend the driver and it direcly
* call the main suspend function and then, if required, on some platform, it
* can call an exit helper.
*/
static int __maybe_unused stmmac_pltfr_suspend(struct device *dev)
{
int ret;
struct net_device *ndev = dev_get_drvdata(dev);
struct stmmac_priv *priv = netdev_priv(ndev);
struct platform_device *pdev = to_platform_device(dev);
ret = stmmac_suspend(dev);
stmmac_pltfr_exit(pdev, priv->plat);
return ret;
}
/**
* stmmac_pltfr_resume
* @dev: device pointer
* Description: this function is invoked when resume the driver before calling
* the main resume function, on some platforms, it can call own init helper
* if required.
*/
static int __maybe_unused stmmac_pltfr_resume(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
struct stmmac_priv *priv = netdev_priv(ndev);
struct platform_device *pdev = to_platform_device(dev);
int ret;
ret = stmmac_pltfr_init(pdev, priv->plat);
if (ret)
return ret;
return stmmac_resume(dev);
}
static int __maybe_unused stmmac_runtime_suspend(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
@ -994,7 +974,7 @@ static int __maybe_unused stmmac_pltfr_noirq_resume(struct device *dev)
}
const struct dev_pm_ops stmmac_pltfr_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(stmmac_pltfr_suspend, stmmac_pltfr_resume)
SET_SYSTEM_SLEEP_PM_OPS(stmmac_suspend, stmmac_resume)
SET_RUNTIME_PM_OPS(stmmac_runtime_suspend, stmmac_runtime_resume, NULL)
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(stmmac_pltfr_noirq_suspend, stmmac_pltfr_noirq_resume)
};

View File

@ -248,6 +248,8 @@ struct plat_stmmacenet_data {
void (*ptp_clk_freq_config)(struct stmmac_priv *priv);
int (*init)(struct platform_device *pdev, void *priv);
void (*exit)(struct platform_device *pdev, void *priv);
int (*suspend)(struct device *dev, void *priv);
int (*resume)(struct device *dev, void *priv);
struct mac_device_info *(*setup)(void *priv);
int (*clks_config)(void *priv, bool enabled);
int (*crosststamp)(ktime_t *device, struct system_counterval_t *system,