From 5d36546f55dc88aa7fae2e14b7113fe6daa7ec3a Mon Sep 17 00:00:00 2001 From: Mike Tipton Date: Tue, 28 Jul 2020 19:12:17 -0700 Subject: [PATCH] clk: qcom: Add common runtime_pm support Add common runtime_pm initialization and callback functions to allow clock providers to vote clocks, interconnects, and regulators in runtime_suspend and runtime_resume. Change-Id: I97588feae47016b8375849c5926495c1d73f792d Signed-off-by: Mike Tipton --- drivers/clk/qcom/common.c | 123 +++++++++++++++++++++++++++++++++++ drivers/clk/qcom/common.h | 7 ++ drivers/clk/qcom/vdd-class.c | 3 + 3 files changed, 133 insertions(+) diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c index 19da2567eca6..4ec0c6e1b804 100644 --- a/drivers/clk/qcom/common.c +++ b/drivers/clk/qcom/common.c @@ -12,6 +12,10 @@ #include #include #include +#include +#include +#include +#include #include "common.h" #include "clk-opp.h" @@ -390,4 +394,123 @@ int qcom_clk_get_voltage(struct clk *clk, unsigned long rate) } EXPORT_SYMBOL(qcom_clk_get_voltage); +int qcom_cc_runtime_init(struct platform_device *pdev, + struct qcom_cc_desc *desc) +{ + struct device *dev = &pdev->dev; + struct clk *clk; + int ret; + + clk = clk_get_optional(dev, "iface"); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) != -EPROBE_DEFER) + dev_err(dev, "unable to get iface clock\n"); + return PTR_ERR(clk); + } + clk_put(clk); + + ret = clk_regulator_init(dev, desc); + if (ret) + return ret; + + desc->path = of_icc_get(dev, NULL); + if (IS_ERR(desc->path)) { + if (PTR_ERR(desc->path) != -EPROBE_DEFER) + dev_err(dev, "error getting path\n"); + return PTR_ERR(desc->path); + } + + platform_set_drvdata(pdev, desc); + pm_runtime_enable(dev); + + ret = pm_clk_create(dev); + if (ret) + goto disable_pm_runtime; + + ret = pm_clk_add(dev, "iface"); + if (ret < 0) { + dev_err(dev, "failed to acquire iface clock\n"); + goto destroy_pm_clk; + } + + return 0; + +destroy_pm_clk: + pm_clk_destroy(dev); + +disable_pm_runtime: + pm_runtime_disable(dev); + icc_put(desc->path); + + return ret; +} +EXPORT_SYMBOL(qcom_cc_runtime_init); + +int qcom_cc_runtime_resume(struct device *dev) +{ + struct qcom_cc_desc *desc = dev_get_drvdata(dev); + struct clk_vdd_class_data vdd_data = {0}; + int ret; + int i; + + for (i = 0; i < desc->num_clk_regulators; i++) { + vdd_data.vdd_class = desc->clk_regulators[i]; + if (!vdd_data.vdd_class) + continue; + + ret = clk_vote_vdd_level(&vdd_data, 1); + if (ret) { + dev_warn(dev, "%s: failed to vote voltage\n", __func__); + return ret; + } + } + + if (desc->path) { + ret = icc_set_bw(desc->path, 0, 1); + if (ret) { + dev_warn(dev, "%s: failed to vote bw\n", __func__); + return ret; + } + } + + ret = pm_clk_resume(dev); + if (ret) + dev_warn(dev, "%s: failed to enable clocks\n", __func__); + + return ret; +} +EXPORT_SYMBOL(qcom_cc_runtime_resume); + +int qcom_cc_runtime_suspend(struct device *dev) +{ + struct qcom_cc_desc *desc = dev_get_drvdata(dev); + struct clk_vdd_class_data vdd_data = {0}; + int ret; + int i; + + ret = pm_clk_suspend(dev); + if (ret) + dev_warn(dev, "%s: failed to disable clocks\n", __func__); + + if (desc->path) { + ret = icc_set_bw(desc->path, 0, 0); + if (ret) + dev_warn(dev, "%s: failed to unvote bw\n", __func__); + } + + for (i = 0; i < desc->num_clk_regulators; i++) { + vdd_data.vdd_class = desc->clk_regulators[i]; + if (!vdd_data.vdd_class) + continue; + + ret = clk_unvote_vdd_level(&vdd_data, 1); + if (ret) + dev_warn(dev, "%s: failed to unvote voltage\n", + __func__); + } + + return 0; +} +EXPORT_SYMBOL(qcom_cc_runtime_suspend); + MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h index 35cbc9958b4f..6a2a85f3eab1 100644 --- a/drivers/clk/qcom/common.h +++ b/drivers/clk/qcom/common.h @@ -33,6 +33,7 @@ struct qcom_cc_desc { size_t num_clk_hws; struct clk_vdd_class **clk_regulators; size_t num_clk_regulators; + struct icc_path *path; }; /** @@ -77,4 +78,10 @@ extern int qcom_cc_probe_by_index(struct platform_device *pdev, int index, const struct qcom_cc_desc *desc); extern const struct clk_ops clk_dummy_ops; void qcom_cc_sync_state(struct device *dev, const struct qcom_cc_desc *desc); + +int qcom_cc_runtime_init(struct platform_device *pdev, + struct qcom_cc_desc *desc); +int qcom_cc_runtime_suspend(struct device *dev); +int qcom_cc_runtime_resume(struct device *dev); + #endif diff --git a/drivers/clk/qcom/vdd-class.c b/drivers/clk/qcom/vdd-class.c index 602715b05ad7..cca6ec118324 100644 --- a/drivers/clk/qcom/vdd-class.c +++ b/drivers/clk/qcom/vdd-class.c @@ -185,6 +185,9 @@ int clk_regulator_init(struct device *dev, const struct qcom_cc_desc *desc) vdd_class = desc->clk_regulators[i]; for (cnt = 0; cnt < vdd_class->num_regulators; cnt++) { + if (vdd_class->regulator[cnt]) + continue; + name = vdd_class->regulator_names[cnt]; regulator = devm_regulator_get(dev, name); if (IS_ERR(regulator)) {