#include <linux/if_vlan.h>
 #include <linux/dma-mapping.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
 #include <linux/prefetch.h>
 #include <linux/pinctrl/consumer.h>
 #ifdef CONFIG_DEBUG_FS
 
 #define STMMAC_COAL_TIMER(x) (ns_to_ktime((x) * NSEC_PER_USEC))
 
+int stmmac_bus_clks_config(struct stmmac_priv *priv, bool enabled)
+{
+       int ret = 0;
+
+       if (enabled) {
+               ret = clk_prepare_enable(priv->plat->stmmac_clk);
+               if (ret)
+                       return ret;
+               ret = clk_prepare_enable(priv->plat->pclk);
+               if (ret) {
+                       clk_disable_unprepare(priv->plat->stmmac_clk);
+                       return ret;
+               }
+       } else {
+               clk_disable_unprepare(priv->plat->stmmac_clk);
+               clk_disable_unprepare(priv->plat->pclk);
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(stmmac_bus_clks_config);
+
 /**
  * stmmac_verify_args - verify the driver parameters.
  * Description: it checks the driver parameters and set a default in case of
        u32 chan;
        int ret;
 
+       ret = pm_runtime_get_sync(priv->device);
+       if (ret < 0) {
+               pm_runtime_put_noidle(priv->device);
+               return ret;
+       }
+
        if (priv->hw->pcs != STMMAC_PCS_TBI &&
            priv->hw->pcs != STMMAC_PCS_RTBI &&
            priv->hw->xpcs_args.an_mode != DW_AN_C73) {
                        netdev_err(priv->dev,
                                   "%s: Cannot attach to PHY (error: %d)\n",
                                   __func__, ret);
-                       return ret;
+                       goto init_phy_error;
                }
        }
 
        free_dma_desc_resources(priv);
 dma_desc_error:
        phylink_disconnect_phy(priv->phylink);
+init_phy_error:
+       pm_runtime_put(priv->device);
        return ret;
 }
 
 
        stmmac_release_ptp(priv);
 
+       pm_runtime_put(priv->device);
+
        return 0;
 }
 
        bool is_double = false;
        int ret;
 
+       ret = pm_runtime_get_sync(priv->device);
+       if (ret < 0) {
+               pm_runtime_put_noidle(priv->device);
+               return ret;
+       }
+
        if (be16_to_cpu(proto) == ETH_P_8021AD)
                is_double = true;
 
        if (priv->hw->num_vlan) {
                ret = stmmac_del_hw_vlan_rx_fltr(priv, ndev, priv->hw, proto, vid);
                if (ret)
-                       return ret;
+                       goto del_vlan_error;
        }
 
-       return stmmac_vlan_update(priv, is_double);
+       ret = stmmac_vlan_update(priv, is_double);
+
+del_vlan_error:
+       pm_runtime_put(priv->device);
+
+       return ret;
 }
 
 static const struct net_device_ops stmmac_netdev_ops = {
 
        stmmac_check_pcs_mode(priv);
 
+       pm_runtime_get_noresume(device);
+       pm_runtime_set_active(device);
+       pm_runtime_enable(device);
+
        if (priv->hw->pcs != STMMAC_PCS_TBI &&
            priv->hw->pcs != STMMAC_PCS_RTBI) {
                /* MDIO bus Registration */
        stmmac_init_fs(ndev);
 #endif
 
+       /* Let pm_runtime_put() disable the clocks.
+        * If CONFIG_PM is not enabled, the clocks will stay powered.
+        */
+       pm_runtime_put(device);
+
        return ret;
 
 error_serdes_powerup:
        stmmac_napi_del(ndev);
 error_hw_init:
        destroy_workqueue(priv->wq);
+       stmmac_bus_clks_config(priv, false);
 
        return ret;
 }
        phylink_destroy(priv->phylink);
        if (priv->plat->stmmac_rst)
                reset_control_assert(priv->plat->stmmac_rst);
-       clk_disable_unprepare(priv->plat->pclk);
-       clk_disable_unprepare(priv->plat->stmmac_clk);
+       pm_runtime_put(dev);
+       pm_runtime_disable(dev);
        if (priv->hw->pcs != STMMAC_PCS_TBI &&
            priv->hw->pcs != STMMAC_PCS_RTBI)
                stmmac_mdio_unregister(ndev);
        struct net_device *ndev = dev_get_drvdata(dev);
        struct stmmac_priv *priv = netdev_priv(ndev);
        u32 chan;
+       int ret;
 
        if (!ndev || !netif_running(ndev))
                return 0;
                pinctrl_pm_select_sleep_state(priv->device);
                /* Disable clock in case of PWM is off */
                clk_disable_unprepare(priv->plat->clk_ptp_ref);
-               clk_disable_unprepare(priv->plat->pclk);
-               clk_disable_unprepare(priv->plat->stmmac_clk);
+               ret = pm_runtime_force_suspend(dev);
+               if (ret)
+                       return ret;
        }
        mutex_unlock(&priv->lock);
 
        } else {
                pinctrl_pm_select_default_state(priv->device);
                /* enable the clk previously disabled */
-               clk_prepare_enable(priv->plat->stmmac_clk);
-               clk_prepare_enable(priv->plat->pclk);
+               ret = pm_runtime_force_resume(dev);
+               if (ret)
+                       return ret;
                if (priv->plat->clk_ptp_ref)
                        clk_prepare_enable(priv->plat->clk_ptp_ref);
                /* reset the phy so that it's ready */
 
 #include <linux/iopoll.h>
 #include <linux/mii.h>
 #include <linux/of_mdio.h>
+#include <linux/pm_runtime.h>
 #include <linux/phy.h>
 #include <linux/property.h>
 #include <linux/slab.h>
        u32 tmp, addr, value = MII_XGMAC_BUSY;
        int ret;
 
+       ret = pm_runtime_get_sync(priv->device);
+       if (ret < 0) {
+               pm_runtime_put_noidle(priv->device);
+               return ret;
+       }
+
        /* Wait until any existing MII operation is complete */
        if (readl_poll_timeout(priv->ioaddr + mii_data, tmp,
-                              !(tmp & MII_XGMAC_BUSY), 100, 10000))
-               return -EBUSY;
+                              !(tmp & MII_XGMAC_BUSY), 100, 10000)) {
+               ret = -EBUSY;
+               goto err_disable_clks;
+       }
 
        if (phyreg & MII_ADDR_C45) {
                phyreg &= ~MII_ADDR_C45;
 
                ret = stmmac_xgmac2_c45_format(priv, phyaddr, phyreg, &addr);
                if (ret)
-                       return ret;
+                       goto err_disable_clks;
        } else {
                ret = stmmac_xgmac2_c22_format(priv, phyaddr, phyreg, &addr);
                if (ret)
-                       return ret;
+                       goto err_disable_clks;
 
                value |= MII_XGMAC_SADDR;
        }
 
        /* Wait until any existing MII operation is complete */
        if (readl_poll_timeout(priv->ioaddr + mii_data, tmp,
-                              !(tmp & MII_XGMAC_BUSY), 100, 10000))
-               return -EBUSY;
+                              !(tmp & MII_XGMAC_BUSY), 100, 10000)) {
+               ret = -EBUSY;
+               goto err_disable_clks;
+       }
 
        /* Set the MII address register to read */
        writel(addr, priv->ioaddr + mii_address);
 
        /* Wait until any existing MII operation is complete */
        if (readl_poll_timeout(priv->ioaddr + mii_data, tmp,
-                              !(tmp & MII_XGMAC_BUSY), 100, 10000))
-               return -EBUSY;
+                              !(tmp & MII_XGMAC_BUSY), 100, 10000)) {
+               ret = -EBUSY;
+               goto err_disable_clks;
+       }
 
        /* Read the data from the MII data register */
-       return readl(priv->ioaddr + mii_data) & GENMASK(15, 0);
+       ret = (int)readl(priv->ioaddr + mii_data) & GENMASK(15, 0);
+
+err_disable_clks:
+       pm_runtime_put(priv->device);
+
+       return ret;
 }
 
 static int stmmac_xgmac2_mdio_write(struct mii_bus *bus, int phyaddr,
        u32 addr, tmp, value = MII_XGMAC_BUSY;
        int ret;
 
+       ret = pm_runtime_get_sync(priv->device);
+       if (ret < 0) {
+               pm_runtime_put_noidle(priv->device);
+               return ret;
+       }
+
        /* Wait until any existing MII operation is complete */
        if (readl_poll_timeout(priv->ioaddr + mii_data, tmp,
-                              !(tmp & MII_XGMAC_BUSY), 100, 10000))
-               return -EBUSY;
+                              !(tmp & MII_XGMAC_BUSY), 100, 10000)) {
+               ret = -EBUSY;
+               goto err_disable_clks;
+       }
 
        if (phyreg & MII_ADDR_C45) {
                phyreg &= ~MII_ADDR_C45;
 
                ret = stmmac_xgmac2_c45_format(priv, phyaddr, phyreg, &addr);
                if (ret)
-                       return ret;
+                       goto err_disable_clks;
        } else {
                ret = stmmac_xgmac2_c22_format(priv, phyaddr, phyreg, &addr);
                if (ret)
-                       return ret;
+                       goto err_disable_clks;
 
                value |= MII_XGMAC_SADDR;
        }
 
        /* Wait until any existing MII operation is complete */
        if (readl_poll_timeout(priv->ioaddr + mii_data, tmp,
-                              !(tmp & MII_XGMAC_BUSY), 100, 10000))
-               return -EBUSY;
+                              !(tmp & MII_XGMAC_BUSY), 100, 10000)) {
+               ret = -EBUSY;
+               goto err_disable_clks;
+       }
 
        /* Set the MII address register to write */
        writel(addr, priv->ioaddr + mii_address);
        writel(value, priv->ioaddr + mii_data);
 
        /* Wait until any existing MII operation is complete */
-       return readl_poll_timeout(priv->ioaddr + mii_data, tmp,
-                                 !(tmp & MII_XGMAC_BUSY), 100, 10000);
+       ret = readl_poll_timeout(priv->ioaddr + mii_data, tmp,
+                                !(tmp & MII_XGMAC_BUSY), 100, 10000);
+
+err_disable_clks:
+       pm_runtime_put(priv->device);
+
+       return ret;
 }
 
 /**
        int data = 0;
        u32 v;
 
+       data = pm_runtime_get_sync(priv->device);
+       if (data < 0) {
+               pm_runtime_put_noidle(priv->device);
+               return data;
+       }
+
        value |= (phyaddr << priv->hw->mii.addr_shift)
                & priv->hw->mii.addr_mask;
        value |= (phyreg << priv->hw->mii.reg_shift) & priv->hw->mii.reg_mask;
        }
 
        if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY),
-                              100, 10000))
-               return -EBUSY;
+                              100, 10000)) {
+               data = -EBUSY;
+               goto err_disable_clks;
+       }
 
        writel(data, priv->ioaddr + mii_data);
        writel(value, priv->ioaddr + mii_address);
 
        if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY),
-                              100, 10000))
-               return -EBUSY;
+                              100, 10000)) {
+               data = -EBUSY;
+               goto err_disable_clks;
+       }
 
        /* Read the data from the MII data register */
        data = (int)readl(priv->ioaddr + mii_data) & MII_DATA_MASK;
 
+err_disable_clks:
+       pm_runtime_put(priv->device);
+
        return data;
 }
 
        struct stmmac_priv *priv = netdev_priv(ndev);
        unsigned int mii_address = priv->hw->mii.addr;
        unsigned int mii_data = priv->hw->mii.data;
+       int ret, data = phydata;
        u32 value = MII_BUSY;
-       int data = phydata;
        u32 v;
 
+       ret = pm_runtime_get_sync(priv->device);
+       if (ret < 0) {
+               pm_runtime_put_noidle(priv->device);
+               return ret;
+       }
+
        value |= (phyaddr << priv->hw->mii.addr_shift)
                & priv->hw->mii.addr_mask;
        value |= (phyreg << priv->hw->mii.reg_shift) & priv->hw->mii.reg_mask;
 
        /* Wait until any existing MII operation is complete */
        if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY),
-                              100, 10000))
-               return -EBUSY;
+                              100, 10000)) {
+               ret = -EBUSY;
+               goto err_disable_clks;
+       }
 
        /* Set the MII address register to write */
        writel(data, priv->ioaddr + mii_data);
        writel(value, priv->ioaddr + mii_address);
 
        /* Wait until any existing MII operation is complete */
-       return readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY),
-                                 100, 10000);
+       ret = readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY),
+                                100, 10000);
+
+err_disable_clks:
+       pm_runtime_put(priv->device);
+
+       return ret;
 }
 
 /**