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 <mdtipton@codeaurora.org>
This commit is contained in:
Mike Tipton 2020-07-28 19:12:17 -07:00 committed by Mike Tipton
parent 9530c87bec
commit 5d36546f55
3 changed files with 133 additions and 0 deletions

View File

@ -12,6 +12,10 @@
#include <linux/reset-controller.h>
#include <linux/of.h>
#include <linux/clk/qcom.h>
#include <linux/clk.h>
#include <linux/interconnect.h>
#include <linux/pm_clock.h>
#include <linux/pm_runtime.h>
#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");

View File

@ -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

View File

@ -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)) {