mirror of
https://github.com/torvalds/linux.git
synced 2026-07-31 11:37:06 +02:00
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:
parent
9530c87bec
commit
5d36546f55
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user