]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
Merge branch 'clk-pm-runtime' into clk-next
authorStephen Boyd <sboyd@codeaurora.org>
Fri, 29 Sep 2017 23:07:28 +0000 (16:07 -0700)
committerStephen Boyd <sboyd@codeaurora.org>
Fri, 29 Sep 2017 23:07:28 +0000 (16:07 -0700)
* clk-pm-runtime:
  clk: samsung: exynos-audss: Add support for runtime PM
  clk: samsung: exynos-audss: Use local variable for controller's device
  clk: samsung: exynos5433: Add support for runtime PM
  clk: samsung: Add support for runtime PM
  clk: Add support for runtime PM

1  2 
Documentation/devicetree/bindings/clock/exynos5433-clock.txt
drivers/clk/clk.c
drivers/clk/samsung/clk-exynos-audss.c

index fe885abc9cb44874b2e7b7d42d6118a7a0828533,5c7dd12e667a5e089fd72ccf0471a3575f9e2cce..c473dd38dd550ccc03d9fc219095dc9257007376
@@@ -168,6 -168,11 +168,11 @@@ Required Properties
                - aclk_cam1_400
                - aclk_cam1_552
  
+ Optional properties:
+   - power-domains: a phandle to respective power domain node as described by
+       generic PM domain bindings (see power/power_domain.txt for more
+       information).
  Each clock is assigned an identifier and client nodes can use this identifier
  to specify the clock which they consume.
  
@@@ -270,6 -275,7 +275,7 @@@ Example 2: Examples of clock controlle
                clocks = <&xxti>,
                       <&cmu_top CLK_ACLK_G2D_266>,
                       <&cmu_top CLK_ACLK_G2D_400>;
+               power-domains = <&pd_g2d>;
        };
  
        cmu_disp: clock-controller@13b90000 {
                       <&cmu_mif CLK_SCLK_DECON_ECLK_DISP>,
                       <&cmu_mif CLK_SCLK_DECON_TV_VCLK_DISP>,
                       <&cmu_mif CLK_ACLK_DISP_333>;
+               power-domains = <&pd_disp>;
        };
  
        cmu_aud: clock-controller@114c0000 {
  
                clock-names = "oscclk", "fout_aud_pll";
                clocks = <&xxti>, <&cmu_top CLK_FOUT_AUD_PLL>;
+               power-domains = <&pd_aud>;
        };
  
        cmu_bus0: clock-controller@13600000 {
  
                clock-names = "oscclk", "aclk_g3d_400";
                clocks = <&xxti>, <&cmu_top CLK_ACLK_G3D_400>;
+               power-domains = <&pd_g3d>;
        };
  
        cmu_gscl: clock-controller@13cf0000 {
                clocks = <&xxti>,
                        <&cmu_top CLK_ACLK_GSCL_111>,
                        <&cmu_top CLK_ACLK_GSCL_333>;
+               power-domains = <&pd_gscl>;
        };
  
        cmu_apollo: clock-controller@11900000 {
                clocks = <&xxti>,
                       <&cmu_top CLK_SCLK_JPEG_MSCL>,
                       <&cmu_top CLK_ACLK_MSCL_400>;
+               power-domains = <&pd_mscl>;
        };
  
        cmu_mfc: clock-controller@15280000 {
  
                clock-names = "oscclk", "aclk_mfc_400";
                clocks = <&xxti>, <&cmu_top CLK_ACLK_MFC_400>;
+               power-domains = <&pd_mfc>;
        };
  
        cmu_hevc: clock-controller@14f80000 {
  
                clock-names = "oscclk", "aclk_hevc_400";
                clocks = <&xxti>, <&cmu_top CLK_ACLK_HEVC_400>;
+               power-domains = <&pd_hevc>;
        };
  
        cmu_isp: clock-controller@146d0000 {
                clocks = <&xxti>,
                       <&cmu_top CLK_ACLK_ISP_DIS_400>,
                       <&cmu_top CLK_ACLK_ISP_400>;
+               power-domains = <&pd_isp>;
        };
  
        cmu_cam0: clock-controller@120d0000 {
                       <&cmu_top CLK_ACLK_CAM0_333>,
                       <&cmu_top CLK_ACLK_CAM0_400>,
                       <&cmu_top CLK_ACLK_CAM0_552>;
+               power-domains = <&pd_cam0>;
        };
  
        cmu_cam1: clock-controller@145d0000 {
                       <&cmu_top CLK_ACLK_CAM1_333>,
                       <&cmu_top CLK_ACLK_CAM1_400>,
                       <&cmu_top CLK_ACLK_CAM1_552>;
+               power-domains = <&pd_cam1>;
        };
  
  Example 3: UART controller node that consumes the clock generated by the clock
                clock-names = "uart", "clk_uart_baud0";
                pinctrl-names = "default";
                pinctrl-0 = <&uart0_bus>;
 -              status = "disabled";
        };
diff --combined drivers/clk/clk.c
index c8d83acda0061977218d1fd084bc6faddff4091c,5bb6308fbf0d7d6c33c5d58019550a3e5beaea74..b6495a12f65f6a067f7599d7bcf3729a1ac01586
@@@ -21,6 -21,7 +21,7 @@@
  #include <linux/of.h>
  #include <linux/device.h>
  #include <linux/init.h>
+ #include <linux/pm_runtime.h>
  #include <linux/sched.h>
  #include <linux/clkdev.h>
  
@@@ -46,6 -47,7 +47,7 @@@ struct clk_core 
        const struct clk_ops    *ops;
        struct clk_hw           *hw;
        struct module           *owner;
+       struct device           *dev;
        struct clk_core         *parent;
        const char              **parent_names;
        struct clk_core         **parents;
@@@ -87,6 -89,26 +89,26 @@@ struct clk 
        struct hlist_node clks_node;
  };
  
+ /***           runtime pm          ***/
+ static int clk_pm_runtime_get(struct clk_core *core)
+ {
+       int ret = 0;
+       if (!core->dev)
+               return 0;
+       ret = pm_runtime_get_sync(core->dev);
+       return ret < 0 ? ret : 0;
+ }
+ static void clk_pm_runtime_put(struct clk_core *core)
+ {
+       if (!core->dev)
+               return;
+       pm_runtime_put_sync(core->dev);
+ }
  /***           locking             ***/
  static void clk_prepare_lock(void)
  {
@@@ -150,6 -172,8 +172,8 @@@ static void clk_enable_unlock(unsigned 
  
  static bool clk_core_is_prepared(struct clk_core *core)
  {
+       bool ret = false;
        /*
         * .is_prepared is optional for clocks that can prepare
         * fall back to software usage counter if it is missing
        if (!core->ops->is_prepared)
                return core->prepare_count;
  
-       return core->ops->is_prepared(core->hw);
+       if (!clk_pm_runtime_get(core)) {
+               ret = core->ops->is_prepared(core->hw);
+               clk_pm_runtime_put(core);
+       }
+       return ret;
  }
  
  static bool clk_core_is_enabled(struct clk_core *core)
  {
+       bool ret = false;
        /*
         * .is_enabled is only mandatory for clocks that gate
         * fall back to software usage counter if .is_enabled is missing
        if (!core->ops->is_enabled)
                return core->enable_count;
  
-       return core->ops->is_enabled(core->hw);
+       /*
+        * Check if clock controller's device is runtime active before
+        * calling .is_enabled callback. If not, assume that clock is
+        * disabled, because we might be called from atomic context, from
+        * which pm_runtime_get() is not allowed.
+        * This function is called mainly from clk_disable_unused_subtree,
+        * which ensures proper runtime pm activation of controller before
+        * taking enable spinlock, but the below check is needed if one tries
+        * to call it from other places.
+        */
+       if (core->dev) {
+               pm_runtime_get_noresume(core->dev);
+               if (!pm_runtime_active(core->dev)) {
+                       ret = false;
+                       goto done;
+               }
+       }
+       ret = core->ops->is_enabled(core->hw);
+ done:
+       clk_pm_runtime_put(core);
+       return ret;
  }
  
  /***    helper functions   ***/
@@@ -489,6 -542,8 +542,8 @@@ static void clk_core_unprepare(struct c
        if (core->ops->unprepare)
                core->ops->unprepare(core->hw);
  
+       clk_pm_runtime_put(core);
        trace_clk_unprepare_complete(core);
        clk_core_unprepare(core->parent);
  }
@@@ -530,10 -585,14 +585,14 @@@ static int clk_core_prepare(struct clk_
                return 0;
  
        if (core->prepare_count == 0) {
-               ret = clk_core_prepare(core->parent);
+               ret = clk_pm_runtime_get(core);
                if (ret)
                        return ret;
  
+               ret = clk_core_prepare(core->parent);
+               if (ret)
+                       goto runtime_put;
                trace_clk_prepare(core);
  
                if (core->ops->prepare)
  
                trace_clk_prepare_complete(core);
  
-               if (ret) {
-                       clk_core_unprepare(core->parent);
-                       return ret;
-               }
+               if (ret)
+                       goto unprepare;
        }
  
        core->prepare_count++;
  
        return 0;
+ unprepare:
+       clk_core_unprepare(core->parent);
+ runtime_put:
+       clk_pm_runtime_put(core);
+       return ret;
  }
  
  static int clk_core_prepare_lock(struct clk_core *core)
@@@ -745,6 -807,9 +807,9 @@@ static void clk_unprepare_unused_subtre
        if (core->flags & CLK_IGNORE_UNUSED)
                return;
  
+       if (clk_pm_runtime_get(core))
+               return;
        if (clk_core_is_prepared(core)) {
                trace_clk_unprepare(core);
                if (core->ops->unprepare_unused)
                        core->ops->unprepare(core->hw);
                trace_clk_unprepare_complete(core);
        }
+       clk_pm_runtime_put(core);
  }
  
  static void clk_disable_unused_subtree(struct clk_core *core)
        if (core->flags & CLK_OPS_PARENT_ENABLE)
                clk_core_prepare_enable(core->parent);
  
+       if (clk_pm_runtime_get(core))
+               goto unprepare_out;
        flags = clk_enable_lock();
  
        if (core->enable_count)
  
  unlock_out:
        clk_enable_unlock(flags);
+       clk_pm_runtime_put(core);
+ unprepare_out:
        if (core->flags & CLK_OPS_PARENT_ENABLE)
                clk_core_disable_unprepare(core->parent);
  }
@@@ -1038,9 -1110,13 +1110,13 @@@ EXPORT_SYMBOL_GPL(clk_get_accuracy)
  static unsigned long clk_recalc(struct clk_core *core,
                                unsigned long parent_rate)
  {
-       if (core->ops->recalc_rate)
-               return core->ops->recalc_rate(core->hw, parent_rate);
-       return parent_rate;
+       unsigned long rate = parent_rate;
+       if (core->ops->recalc_rate && !clk_pm_runtime_get(core)) {
+               rate = core->ops->recalc_rate(core->hw, parent_rate);
+               clk_pm_runtime_put(core);
+       }
+       return rate;
  }
  
  /**
@@@ -1565,6 -1641,7 +1641,7 @@@ static int clk_core_set_rate_nolock(str
  {
        struct clk_core *top, *fail_clk;
        unsigned long rate = req_rate;
+       int ret = 0;
  
        if (!core)
                return 0;
        if (!top)
                return -EINVAL;
  
+       ret = clk_pm_runtime_get(core);
+       if (ret)
+               return ret;
        /* notify that we are about to change rates */
        fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE);
        if (fail_clk) {
                pr_debug("%s: failed to set %s rate\n", __func__,
                                fail_clk->name);
                clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
-               return -EBUSY;
+               ret = -EBUSY;
+               goto err;
        }
  
        /* change the rates */
        clk_change_rate(top);
  
        core->req_rate = req_rate;
+ err:
+       clk_pm_runtime_put(core);
  
-       return 0;
+       return ret;
  }
  
  /**
@@@ -1826,12 -1910,16 +1910,16 @@@ static int clk_core_set_parent(struct c
                p_rate = parent->rate;
        }
  
+       ret = clk_pm_runtime_get(core);
+       if (ret)
+               goto out;
        /* propagate PRE_RATE_CHANGE notifications */
        ret = __clk_speculate_rates(core, p_rate);
  
        /* abort if a driver objects */
        if (ret & NOTIFY_STOP_MASK)
-               goto out;
+               goto runtime_put;
  
        /* do the re-parent */
        ret = __clk_set_parent(core, parent, p_index);
                __clk_recalc_accuracies(core);
        }
  
+ runtime_put:
+       clk_pm_runtime_put(core);
  out:
        clk_prepare_unlock();
  
@@@ -2350,7 -2440,7 +2440,7 @@@ static inline void clk_debug_unregister
   */
  static int __clk_core_init(struct clk_core *core)
  {
-       int i, ret = 0;
+       int i, ret;
        struct clk_core *orphan;
        struct hlist_node *tmp2;
        unsigned long rate;
  
        clk_prepare_lock();
  
+       ret = clk_pm_runtime_get(core);
+       if (ret)
+               goto unlock;
        /* check to see if a clock with this name is already registered */
        if (clk_core_lookup(core->name)) {
                pr_debug("%s: clk %s already initialized\n",
  
        kref_init(&core->ref);
  out:
+       clk_pm_runtime_put(core);
+ unlock:
        clk_prepare_unlock();
  
        if (!ret)
@@@ -2583,6 -2679,8 +2679,8 @@@ struct clk *clk_register(struct device 
                goto fail_name;
        }
        core->ops = hw->init->ops;
+       if (dev && pm_runtime_enabled(dev))
+               core->dev = dev;
        if (dev && dev->driver)
                core->owner = dev->driver->owner;
        core->hw = hw;
@@@ -3132,7 -3230,7 +3230,7 @@@ int of_clk_add_provider(struct device_n
        mutex_lock(&of_clk_mutex);
        list_add(&cp->link, &of_clk_providers);
        mutex_unlock(&of_clk_mutex);
 -      pr_debug("Added clock from %s\n", np->full_name);
 +      pr_debug("Added clock from %pOF\n", np);
  
        ret = of_clk_set_defaults(np, true);
        if (ret < 0)
@@@ -3167,7 -3265,7 +3265,7 @@@ int of_clk_add_hw_provider(struct devic
        mutex_lock(&of_clk_mutex);
        list_add(&cp->link, &of_clk_providers);
        mutex_unlock(&of_clk_mutex);
 -      pr_debug("Added clk_hw provider from %s\n", np->full_name);
 +      pr_debug("Added clk_hw provider from %pOF\n", np);
  
        ret = of_clk_set_defaults(np, true);
        if (ret < 0)
index b117783ed40478b03be6abe4ac1b107df8d7a424,ab494c104ce6dbc8368846595e3001f8caf5d6b8..5bfc92ee3129a5fec4df14c58f7024a64ed15058
@@@ -18,6 -18,7 +18,7 @@@
  #include <linux/syscore_ops.h>
  #include <linux/module.h>
  #include <linux/platform_device.h>
+ #include <linux/pm_runtime.h>
  
  #include <dt-bindings/clock/exynos-audss-clk.h>
  
@@@ -36,14 -37,13 +37,13 @@@ static struct clk *epll
  #define ASS_CLK_DIV 0x4
  #define ASS_CLK_GATE 0x8
  
- #ifdef CONFIG_PM_SLEEP
  static unsigned long reg_save[][2] = {
        { ASS_CLK_SRC,  0 },
        { ASS_CLK_DIV,  0 },
        { ASS_CLK_GATE, 0 },
  };
  
- static int exynos_audss_clk_suspend(struct device *dev)
+ static int __maybe_unused exynos_audss_clk_suspend(struct device *dev)
  {
        int i;
  
@@@ -53,7 -53,7 +53,7 @@@
        return 0;
  }
  
- static int exynos_audss_clk_resume(struct device *dev)
+ static int __maybe_unused exynos_audss_clk_resume(struct device *dev)
  {
        int i;
  
@@@ -62,7 -62,6 +62,6 @@@
  
        return 0;
  }
- #endif /* CONFIG_PM_SLEEP */
  
  struct exynos_audss_clk_drvdata {
        unsigned int has_adma_clk:1;
@@@ -135,6 -134,7 +134,7 @@@ static int exynos_audss_clk_probe(struc
        const struct exynos_audss_clk_drvdata *variant;
        struct clk_hw **clk_table;
        struct resource *res;
+       struct device *dev = &pdev->dev;
        int i, ret = 0;
  
        variant = of_device_get_match_data(&pdev->dev);
                return -EINVAL;
  
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       reg_base = devm_ioremap_resource(&pdev->dev, res);
+       reg_base = devm_ioremap_resource(dev, res);
        if (IS_ERR(reg_base)) {
-               dev_err(&pdev->dev, "failed to map audss registers\n");
+               dev_err(dev, "failed to map audss registers\n");
                return PTR_ERR(reg_base);
        }
  
        epll = ERR_PTR(-ENODEV);
  
-       clk_data = devm_kzalloc(&pdev->dev,
+       clk_data = devm_kzalloc(dev,
                                sizeof(*clk_data) +
                                sizeof(*clk_data->hws) * EXYNOS_AUDSS_MAX_CLKS,
                                GFP_KERNEL);
        clk_data->num = variant->num_clks;
        clk_table = clk_data->hws;
  
-       pll_ref = devm_clk_get(&pdev->dev, "pll_ref");
-       pll_in = devm_clk_get(&pdev->dev, "pll_in");
+       pll_ref = devm_clk_get(dev, "pll_ref");
+       pll_in = devm_clk_get(dev, "pll_in");
        if (!IS_ERR(pll_ref))
                mout_audss_p[0] = __clk_get_name(pll_ref);
        if (!IS_ERR(pll_in)) {
  
                        ret = clk_prepare_enable(epll);
                        if (ret) {
-                               dev_err(&pdev->dev,
+                               dev_err(dev,
                                        "failed to prepare the epll clock\n");
                                return ret;
                        }
                }
        }
-       clk_table[EXYNOS_MOUT_AUDSS] = clk_hw_register_mux(NULL, "mout_audss",
+       /*
+        * Enable runtime PM here to allow the clock core using runtime PM
+        * for the registered clocks. Additionally, we increase the runtime
+        * PM usage count before registering the clocks, to prevent the
+        * clock core from runtime suspending the device.
+        */
+       pm_runtime_get_noresume(dev);
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+       clk_table[EXYNOS_MOUT_AUDSS] = clk_hw_register_mux(dev, "mout_audss",
                                mout_audss_p, ARRAY_SIZE(mout_audss_p),
 -                              CLK_SET_RATE_NO_REPARENT,
 +                              CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
                                reg_base + ASS_CLK_SRC, 0, 1, 0, &lock);
  
-       cdclk = devm_clk_get(&pdev->dev, "cdclk");
-       sclk_audio = devm_clk_get(&pdev->dev, "sclk_audio");
+       cdclk = devm_clk_get(dev, "cdclk");
+       sclk_audio = devm_clk_get(dev, "sclk_audio");
        if (!IS_ERR(cdclk))
                mout_i2s_p[1] = __clk_get_name(cdclk);
        if (!IS_ERR(sclk_audio))
                mout_i2s_p[2] = __clk_get_name(sclk_audio);
-       clk_table[EXYNOS_MOUT_I2S] = clk_hw_register_mux(NULL, "mout_i2s",
+       clk_table[EXYNOS_MOUT_I2S] = clk_hw_register_mux(dev, "mout_i2s",
                                mout_i2s_p, ARRAY_SIZE(mout_i2s_p),
                                CLK_SET_RATE_NO_REPARENT,
                                reg_base + ASS_CLK_SRC, 2, 2, 0, &lock);
  
-       clk_table[EXYNOS_DOUT_SRP] = clk_hw_register_divider(NULL, "dout_srp",
+       clk_table[EXYNOS_DOUT_SRP] = clk_hw_register_divider(dev, "dout_srp",
 -                              "mout_audss", 0, reg_base + ASS_CLK_DIV, 0, 4,
 -                              0, &lock);
 +                              "mout_audss", CLK_SET_RATE_PARENT,
 +                              reg_base + ASS_CLK_DIV, 0, 4, 0, &lock);
  
-       clk_table[EXYNOS_DOUT_AUD_BUS] = clk_hw_register_divider(NULL,
+       clk_table[EXYNOS_DOUT_AUD_BUS] = clk_hw_register_divider(dev,
 -                              "dout_aud_bus", "dout_srp", 0,
 +                              "dout_aud_bus", "dout_srp", CLK_SET_RATE_PARENT,
                                reg_base + ASS_CLK_DIV, 4, 4, 0, &lock);
  
-       clk_table[EXYNOS_DOUT_I2S] = clk_hw_register_divider(NULL, "dout_i2s",
+       clk_table[EXYNOS_DOUT_I2S] = clk_hw_register_divider(dev, "dout_i2s",
                                "mout_i2s", 0, reg_base + ASS_CLK_DIV, 8, 4, 0,
                                &lock);
  
-       clk_table[EXYNOS_SRP_CLK] = clk_hw_register_gate(NULL, "srp_clk",
+       clk_table[EXYNOS_SRP_CLK] = clk_hw_register_gate(dev, "srp_clk",
                                "dout_srp", CLK_SET_RATE_PARENT,
                                reg_base + ASS_CLK_GATE, 0, 0, &lock);
  
-       clk_table[EXYNOS_I2S_BUS] = clk_hw_register_gate(NULL, "i2s_bus",
+       clk_table[EXYNOS_I2S_BUS] = clk_hw_register_gate(dev, "i2s_bus",
                                "dout_aud_bus", CLK_SET_RATE_PARENT,
                                reg_base + ASS_CLK_GATE, 2, 0, &lock);
  
-       clk_table[EXYNOS_SCLK_I2S] = clk_hw_register_gate(NULL, "sclk_i2s",
+       clk_table[EXYNOS_SCLK_I2S] = clk_hw_register_gate(dev, "sclk_i2s",
                                "dout_i2s", CLK_SET_RATE_PARENT,
                                reg_base + ASS_CLK_GATE, 3, 0, &lock);
  
-       clk_table[EXYNOS_PCM_BUS] = clk_hw_register_gate(NULL, "pcm_bus",
+       clk_table[EXYNOS_PCM_BUS] = clk_hw_register_gate(dev, "pcm_bus",
                                 "sclk_pcm", CLK_SET_RATE_PARENT,
                                reg_base + ASS_CLK_GATE, 4, 0, &lock);
  
-       sclk_pcm_in = devm_clk_get(&pdev->dev, "sclk_pcm_in");
+       sclk_pcm_in = devm_clk_get(dev, "sclk_pcm_in");
        if (!IS_ERR(sclk_pcm_in))
                sclk_pcm_p = __clk_get_name(sclk_pcm_in);
-       clk_table[EXYNOS_SCLK_PCM] = clk_hw_register_gate(NULL, "sclk_pcm",
+       clk_table[EXYNOS_SCLK_PCM] = clk_hw_register_gate(dev, "sclk_pcm",
                                sclk_pcm_p, CLK_SET_RATE_PARENT,
                                reg_base + ASS_CLK_GATE, 5, 0, &lock);
  
        if (variant->has_adma_clk) {
-               clk_table[EXYNOS_ADMA] = clk_hw_register_gate(NULL, "adma",
+               clk_table[EXYNOS_ADMA] = clk_hw_register_gate(dev, "adma",
                                "dout_srp", CLK_SET_RATE_PARENT,
                                reg_base + ASS_CLK_GATE, 9, 0, &lock);
        }
  
        for (i = 0; i < clk_data->num; i++) {
                if (IS_ERR(clk_table[i])) {
-                       dev_err(&pdev->dev, "failed to register clock %d\n", i);
+                       dev_err(dev, "failed to register clock %d\n", i);
                        ret = PTR_ERR(clk_table[i]);
                        goto unregister;
                }
        }
  
-       ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
+       ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
                                     clk_data);
        if (ret) {
-               dev_err(&pdev->dev, "failed to add clock provider\n");
+               dev_err(dev, "failed to add clock provider\n");
                goto unregister;
        }
  
+       pm_runtime_put_sync(dev);
        return 0;
  
  unregister:
        exynos_audss_clk_teardown();
+       pm_runtime_put_sync(dev);
+       pm_runtime_disable(dev);
  
        if (!IS_ERR(epll))
                clk_disable_unprepare(epll);
@@@ -266,6 -281,7 +281,7 @@@ static int exynos_audss_clk_remove(stru
        of_clk_del_provider(pdev->dev.of_node);
  
        exynos_audss_clk_teardown();
+       pm_runtime_disable(&pdev->dev);
  
        if (!IS_ERR(epll))
                clk_disable_unprepare(epll);
  }
  
  static const struct dev_pm_ops exynos_audss_clk_pm_ops = {
-       SET_LATE_SYSTEM_SLEEP_PM_OPS(exynos_audss_clk_suspend,
-                                    exynos_audss_clk_resume)
+       SET_RUNTIME_PM_OPS(exynos_audss_clk_suspend, exynos_audss_clk_resume,
+                          NULL)
+       SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+                                    pm_runtime_force_resume)
  };
  
  static struct platform_driver exynos_audss_clk_driver = {