From 70335921914c7ae5fef9ee4307079537df4bda22 Mon Sep 17 00:00:00 2001 From: Mike Tipton Date: Tue, 28 Jul 2020 19:12:17 -0700 Subject: [PATCH 1/4] 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 d3a7d983fddb..a5b219772ad8 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" @@ -394,4 +398,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 7df3da19f7fb..5fa895845b01 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 429fac9fcce7..5a866b297906 100644 --- a/drivers/clk/qcom/vdd-class.c +++ b/drivers/clk/qcom/vdd-class.c @@ -197,6 +197,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)) { From fdffc47313ee22a8126d20a4646c0dff99032deb Mon Sep 17 00:00:00 2001 From: Mike Tipton Date: Tue, 28 Jul 2020 19:17:38 -0700 Subject: [PATCH 2/4] clk: qcom: Add functions to suspend or resume a clk_regmap's device Add helper functions to suspend or resume the device associated with a clk_regmap. Change-Id: Ie4e47fce64e2052ffacdc3a8cd56fc69758d70fb Signed-off-by: Mike Tipton --- drivers/clk/qcom/clk-regmap.c | 24 ++++++++++++++++++++++++ drivers/clk/qcom/clk-regmap.h | 4 ++++ 2 files changed, 28 insertions(+) diff --git a/drivers/clk/qcom/clk-regmap.c b/drivers/clk/qcom/clk-regmap.c index 54d8ee1b33fb..513045c6425d 100644 --- a/drivers/clk/qcom/clk-regmap.c +++ b/drivers/clk/qcom/clk-regmap.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "clk-regmap.h" @@ -270,6 +271,8 @@ int devm_clk_register_regmap(struct device *dev, struct clk_regmap *rclk) { int ret; + rclk->dev = dev; + if (dev && dev_get_regmap(dev, NULL)) rclk->regmap = dev_get_regmap(dev, NULL); else if (dev && dev->parent) @@ -282,3 +285,24 @@ int devm_clk_register_regmap(struct device *dev, struct clk_regmap *rclk) return ret; } EXPORT_SYMBOL_GPL(devm_clk_register_regmap); + +int clk_runtime_get_regmap(struct clk_regmap *rclk) +{ + int ret; + + if (pm_runtime_enabled(rclk->dev)) { + ret = pm_runtime_get_sync(rclk->dev); + if (ret < 0) + return ret; + } + + return 0; +} +EXPORT_SYMBOL(clk_runtime_get_regmap); + +void clk_runtime_put_regmap(struct clk_regmap *rclk) +{ + if (pm_runtime_enabled(rclk->dev)) + pm_runtime_put_sync(rclk->dev); +} +EXPORT_SYMBOL(clk_runtime_put_regmap); diff --git a/drivers/clk/qcom/clk-regmap.h b/drivers/clk/qcom/clk-regmap.h index 0060f6b7504d..2241b20f0b55 100644 --- a/drivers/clk/qcom/clk-regmap.h +++ b/drivers/clk/qcom/clk-regmap.h @@ -50,6 +50,7 @@ struct clk_regmap { struct clk_vdd_class_data vdd_data; struct clk_regmap_ops *ops; struct list_head list_node; + struct device *dev; }; #define to_clk_regmap(_hw) container_of(_hw, struct clk_regmap, hw) @@ -71,4 +72,7 @@ struct clk_register_data { u32 offset; }; +int clk_runtime_get_regmap(struct clk_regmap *rclk); +void clk_runtime_put_regmap(struct clk_regmap *rclk); + #endif From 7c2b115d51de7b9f904c4e303561d059a1061e57 Mon Sep 17 00:00:00 2001 From: Mike Tipton Date: Tue, 28 Jul 2020 19:18:50 -0700 Subject: [PATCH 3/4] clk: qcom: clk-debug: Resume devices before accessing HW Resume and suspend devices in debugfs handlers around accessing HW registers to prevent bus errors. Change-Id: I9db208f0d82a504af60cf252e5d3691b9903f8cd Signed-off-by: Mike Tipton --- drivers/clk/qcom/clk-debug.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/clk/qcom/clk-debug.c b/drivers/clk/qcom/clk-debug.c index 9835ea8fbe8e..7f33397ac7a5 100644 --- a/drivers/clk/qcom/clk-debug.c +++ b/drivers/clk/qcom/clk-debug.c @@ -266,12 +266,22 @@ static u32 get_mux_divs(struct clk_hw *mux) static int clk_debug_measure_get(void *data, u64 *val) { + struct clk_regmap *rclk = NULL; struct clk_debug_mux *mux; struct clk_hw *hw = data; struct clk_hw *parent; int ret = 0; u32 regval; + if (clk_is_regmap_clk(hw)) + rclk = to_clk_regmap(hw); + + if (rclk) { + ret = clk_runtime_get_regmap(rclk); + if (ret) + return ret; + } + mutex_lock(&clk_debug_lock); ret = clk_find_and_set_parent(measure, hw); @@ -306,6 +316,8 @@ static int clk_debug_measure_get(void *data, u64 *val) } exit: mutex_unlock(&clk_debug_lock); + if (rclk) + clk_runtime_put_regmap(rclk); return ret; } @@ -501,8 +513,14 @@ static void clk_debug_print_hw(struct clk_hw *hw, struct seq_file *f) if (clk_is_regmap_clk(hw)) { rclk = to_clk_regmap(hw); + + if (clk_runtime_get_regmap(rclk)) + return; + if (rclk->ops && rclk->ops->list_registers) rclk->ops->list_registers(f, hw); + + clk_runtime_put_regmap(rclk); } } From 5004b2140509bc6f874a72990267f00cf4fef5df Mon Sep 17 00:00:00 2001 From: Mike Tipton Date: Tue, 28 Jul 2020 19:14:13 -0700 Subject: [PATCH 4/4] clk: qcom: lahaina: Add runtime_pm support Add runtime_pm support for clock providers which have register access requirements that aren't always satisfied. Stop registers clocks previously flagged as CLK_IS_CRITICAL, since doing so means the device is always active and suspend is never called. Instead, just enable these clocks by default in HW during probe. Change-Id: I34cf74080745b627b3978fafd9eb8bbde4bb655e Signed-off-by: Mike Tipton --- drivers/clk/qcom/camcc-lahaina.c | 51 +++++++++------------ drivers/clk/qcom/dispcc-lahaina.c | 49 +++++++++----------- drivers/clk/qcom/videocc-lahaina.c | 72 ++++++++++-------------------- 3 files changed, 66 insertions(+), 106 deletions(-) diff --git a/drivers/clk/qcom/camcc-lahaina.c b/drivers/clk/qcom/camcc-lahaina.c index d5ed88ad6ae2..74bfce7e3ebd 100644 --- a/drivers/clk/qcom/camcc-lahaina.c +++ b/drivers/clk/qcom/camcc-lahaina.c @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -1931,24 +1932,6 @@ static struct clk_branch cam_cc_csiphy5_clk = { }, }; -static struct clk_branch cam_cc_gdsc_clk = { - .halt_reg = 0xc148, - .halt_check = BRANCH_HALT, - .clkr = { - .enable_reg = 0xc148, - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "cam_cc_gdsc_clk", - .parent_data = &(const struct clk_parent_data){ - .hw = &cam_cc_xo_clk_src.clkr.hw, - }, - .num_parents = 1, - .flags = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, - }, - }, -}; - static struct clk_branch cam_cc_icp_ahb_clk = { .halt_reg = 0xc094, .halt_check = BRANCH_HALT, @@ -2866,7 +2849,6 @@ static struct clk_regmap *cam_cc_lahaina_clocks[] = { [CAM_CC_CSIPHY4_CLK] = &cam_cc_csiphy4_clk.clkr, [CAM_CC_CSIPHY5_CLK] = &cam_cc_csiphy5_clk.clkr, [CAM_CC_FAST_AHB_CLK_SRC] = &cam_cc_fast_ahb_clk_src.clkr, - [CAM_CC_GDSC_CLK] = &cam_cc_gdsc_clk.clkr, [CAM_CC_ICP_AHB_CLK] = &cam_cc_icp_ahb_clk.clkr, [CAM_CC_ICP_CLK] = &cam_cc_icp_clk.clkr, [CAM_CC_ICP_CLK_SRC] = &cam_cc_icp_clk_src.clkr, @@ -2972,7 +2954,7 @@ static const struct regmap_config cam_cc_lahaina_regmap_config = { .fast_io = true, }; -static const struct qcom_cc_desc cam_cc_lahaina_desc = { +static struct qcom_cc_desc cam_cc_lahaina_desc = { .config = &cam_cc_lahaina_regmap_config, .clks = cam_cc_lahaina_clocks, .num_clks = ARRAY_SIZE(cam_cc_lahaina_clocks), @@ -3014,23 +2996,22 @@ static int cam_cc_lahaina_fixup(struct platform_device *pdev, static int cam_cc_lahaina_probe(struct platform_device *pdev) { struct regmap *regmap; - struct clk *clk; int ret; - clk = devm_clk_get(&pdev->dev, "cfg_ahb_clk"); - if (IS_ERR(clk)) { - if (PTR_ERR(clk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Unable to get ahb clock handle\n"); - return PTR_ERR(clk); - } - devm_clk_put(&pdev->dev, clk); - regmap = qcom_cc_map(pdev, &cam_cc_lahaina_desc); if (IS_ERR(regmap)) { dev_err(&pdev->dev, "Failed to map cam CC registers\n"); return PTR_ERR(regmap); } + ret = qcom_cc_runtime_init(pdev, &cam_cc_lahaina_desc); + if (ret) + return ret; + + ret = pm_runtime_get_sync(&pdev->dev); + if (ret) + return ret; + clk_lucid_5lpe_pll_configure(&cam_cc_pll0, regmap, &cam_cc_pll0_config); clk_lucid_5lpe_pll_configure(&cam_cc_pll1, regmap, &cam_cc_pll1_config); clk_zonda_5lpe_pll_configure(&cam_cc_pll2, regmap, &cam_cc_pll2_config); @@ -3043,12 +3024,19 @@ static int cam_cc_lahaina_probe(struct platform_device *pdev) if (ret) return ret; + /* + * Keep clocks always enabled: + * cam_cc_gdsc_clk + */ + regmap_update_bits(regmap, 0xc148, BIT(0), BIT(0)); + ret = qcom_cc_really_probe(pdev, &cam_cc_lahaina_desc, regmap); if (ret) { dev_err(&pdev->dev, "Failed to register CAM CC clocks\n"); return ret; } + pm_runtime_put_sync(&pdev->dev); dev_info(&pdev->dev, "Registered CAM CC clocks\n"); return ret; @@ -3059,12 +3047,17 @@ static void cam_cc_lahaina_sync_state(struct device *dev) qcom_cc_sync_state(dev, &cam_cc_lahaina_desc); } +static const struct dev_pm_ops cam_cc_lahaina_pm_ops = { + SET_RUNTIME_PM_OPS(qcom_cc_runtime_suspend, qcom_cc_runtime_resume, NULL) +}; + static struct platform_driver cam_cc_lahaina_driver = { .probe = cam_cc_lahaina_probe, .driver = { .name = "lahaina-cam_cc", .of_match_table = cam_cc_lahaina_match_table, .sync_state = cam_cc_lahaina_sync_state, + .pm = &cam_cc_lahaina_pm_ops, }, }; diff --git a/drivers/clk/qcom/dispcc-lahaina.c b/drivers/clk/qcom/dispcc-lahaina.c index 23ca6170c4d6..94a412c531fc 100644 --- a/drivers/clk/qcom/dispcc-lahaina.c +++ b/drivers/clk/qcom/dispcc-lahaina.c @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -1422,24 +1423,6 @@ static struct clk_branch disp_cc_sleep_clk = { }, }; -static struct clk_branch disp_cc_xo_clk = { - .halt_reg = 0x605c, - .halt_check = BRANCH_HALT, - .clkr = { - .enable_reg = 0x605c, - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "disp_cc_xo_clk", - .parent_data = &(const struct clk_parent_data){ - .hw = &disp_cc_xo_clk_src.clkr.hw, - }, - .num_parents = 1, - .flags = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, - }, - }, -}; - static struct clk_regmap *disp_cc_lahaina_clocks[] = { [DISP_CC_MDSS_AHB_CLK] = &disp_cc_mdss_ahb_clk.clkr, [DISP_CC_MDSS_AHB_CLK_SRC] = &disp_cc_mdss_ahb_clk_src.clkr, @@ -1502,7 +1485,6 @@ static struct clk_regmap *disp_cc_lahaina_clocks[] = { [DISP_CC_PLL1] = &disp_cc_pll1.clkr, [DISP_CC_SLEEP_CLK] = &disp_cc_sleep_clk.clkr, [DISP_CC_SLEEP_CLK_SRC] = &disp_cc_sleep_clk_src.clkr, - [DISP_CC_XO_CLK] = &disp_cc_xo_clk.clkr, [DISP_CC_XO_CLK_SRC] = &disp_cc_xo_clk_src.clkr, }; @@ -1519,7 +1501,7 @@ static const struct regmap_config disp_cc_lahaina_regmap_config = { .fast_io = true, }; -static const struct qcom_cc_desc disp_cc_lahaina_desc = { +static struct qcom_cc_desc disp_cc_lahaina_desc = { .config = &disp_cc_lahaina_regmap_config, .clks = disp_cc_lahaina_clocks, .num_clks = ARRAY_SIZE(disp_cc_lahaina_clocks), @@ -1538,20 +1520,19 @@ MODULE_DEVICE_TABLE(of, disp_cc_lahaina_match_table); static int disp_cc_lahaina_probe(struct platform_device *pdev) { struct regmap *regmap; - struct clk *clk; int ret; regmap = qcom_cc_map(pdev, &disp_cc_lahaina_desc); if (IS_ERR(regmap)) return PTR_ERR(regmap); - clk = devm_clk_get(&pdev->dev, "cfg_ahb_clk"); - if (IS_ERR(clk)) { - if (PTR_ERR(clk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Unable to get ahb clock handle\n"); - return PTR_ERR(clk); - } - devm_clk_put(&pdev->dev, clk); + ret = qcom_cc_runtime_init(pdev, &disp_cc_lahaina_desc); + if (ret) + return ret; + + ret = pm_runtime_get_sync(&pdev->dev); + if (ret) + return ret; clk_lucid_5lpe_pll_configure(&disp_cc_pll0, regmap, &disp_cc_pll0_config); @@ -1561,12 +1542,19 @@ static int disp_cc_lahaina_probe(struct platform_device *pdev) /* Enable clock gating for MDP clocks */ regmap_update_bits(regmap, DISP_CC_MISC_CMD, 0x10, 0x10); + /* + * Keep clocks always enabled: + * disp_cc_xo_clk + */ + regmap_update_bits(regmap, 0x605c, BIT(0), BIT(0)); + ret = qcom_cc_really_probe(pdev, &disp_cc_lahaina_desc, regmap); if (ret) { dev_err(&pdev->dev, "Failed to register DISP CC clocks\n"); return ret; } + pm_runtime_put_sync(&pdev->dev); dev_info(&pdev->dev, "Registered DISP CC clocks\n"); return ret; @@ -1577,12 +1565,17 @@ static void disp_cc_lahaina_sync_state(struct device *dev) qcom_cc_sync_state(dev, &disp_cc_lahaina_desc); } +static const struct dev_pm_ops disp_cc_lahaina_pm_ops = { + SET_RUNTIME_PM_OPS(qcom_cc_runtime_suspend, qcom_cc_runtime_resume, NULL) +}; + static struct platform_driver disp_cc_lahaina_driver = { .probe = disp_cc_lahaina_probe, .driver = { .name = "disp_cc-lahaina", .of_match_table = disp_cc_lahaina_match_table, .sync_state = disp_cc_lahaina_sync_state, + .pm = &disp_cc_lahaina_pm_ops, }, }; diff --git a/drivers/clk/qcom/videocc-lahaina.c b/drivers/clk/qcom/videocc-lahaina.c index 0b628014e238..b4985b3d4367 100644 --- a/drivers/clk/qcom/videocc-lahaina.c +++ b/drivers/clk/qcom/videocc-lahaina.c @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -361,26 +362,6 @@ static struct clk_regmap_div video_cc_mvs1c_div2_div_clk_src = { }, }; -static struct clk_branch video_cc_ahb_clk = { - .halt_reg = 0xe58, - .halt_check = BRANCH_HALT_VOTED, - .hwcg_reg = 0xe58, - .hwcg_bit = 1, - .clkr = { - .enable_reg = 0xe58, - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "video_cc_ahb_clk", - .parent_data = &(const struct clk_parent_data){ - .hw = &video_cc_ahb_clk_src.clkr.hw, - }, - .num_parents = 1, - .flags = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, - }, - }, -}; - static struct clk_branch video_cc_mvs0_clk = { .halt_reg = 0xd34, .halt_check = BRANCH_HALT_VOTED, @@ -495,26 +476,7 @@ static struct clk_branch video_cc_sleep_clk = { }, }; -static struct clk_branch video_cc_xo_clk = { - .halt_reg = 0xeec, - .halt_check = BRANCH_HALT, - .clkr = { - .enable_reg = 0xeec, - .enable_mask = BIT(0), - .hw.init = &(struct clk_init_data){ - .name = "video_cc_xo_clk", - .parent_data = &(const struct clk_parent_data){ - .hw = &video_cc_xo_clk_src.clkr.hw, - }, - .num_parents = 1, - .flags = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, - .ops = &clk_branch2_ops, - }, - }, -}; - static struct clk_regmap *video_cc_lahaina_clocks[] = { - [VIDEO_CC_AHB_CLK] = &video_cc_ahb_clk.clkr, [VIDEO_CC_AHB_CLK_SRC] = &video_cc_ahb_clk_src.clkr, [VIDEO_CC_MVS0_CLK] = &video_cc_mvs0_clk.clkr, [VIDEO_CC_MVS0_CLK_SRC] = &video_cc_mvs0_clk_src.clkr, @@ -531,7 +493,6 @@ static struct clk_regmap *video_cc_lahaina_clocks[] = { &video_cc_mvs1c_div2_div_clk_src.clkr, [VIDEO_CC_SLEEP_CLK] = &video_cc_sleep_clk.clkr, [VIDEO_CC_SLEEP_CLK_SRC] = &video_cc_sleep_clk_src.clkr, - [VIDEO_CC_XO_CLK] = &video_cc_xo_clk.clkr, [VIDEO_CC_XO_CLK_SRC] = &video_cc_xo_clk_src.clkr, [VIDEO_PLL0] = &video_pll0.clkr, [VIDEO_PLL1] = &video_pll1.clkr, @@ -555,7 +516,7 @@ static const struct regmap_config video_cc_lahaina_regmap_config = { .fast_io = true, }; -static const struct qcom_cc_desc video_cc_lahaina_desc = { +static struct qcom_cc_desc video_cc_lahaina_desc = { .config = &video_cc_lahaina_regmap_config, .clks = video_cc_lahaina_clocks, .num_clks = ARRAY_SIZE(video_cc_lahaina_clocks), @@ -574,30 +535,38 @@ MODULE_DEVICE_TABLE(of, video_cc_lahaina_match_table); static int video_cc_lahaina_probe(struct platform_device *pdev) { struct regmap *regmap; - struct clk *clk; int ret; regmap = qcom_cc_map(pdev, &video_cc_lahaina_desc); if (IS_ERR(regmap)) return PTR_ERR(regmap); - clk = devm_clk_get(&pdev->dev, "cfg_ahb_clk"); - if (IS_ERR(clk)) { - if (PTR_ERR(clk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Unable to get ahb clock handle\n"); - return PTR_ERR(clk); - } - devm_clk_put(&pdev->dev, clk); + ret = qcom_cc_runtime_init(pdev, &video_cc_lahaina_desc); + if (ret) + return ret; + + ret = pm_runtime_get_sync(&pdev->dev); + if (ret) + return ret; clk_lucid_5lpe_pll_configure(&video_pll0, regmap, &video_pll0_config); clk_lucid_5lpe_pll_configure(&video_pll1, regmap, &video_pll1_config); + /* + * Keep clocks always enabled: + * video_cc_ahb_clk + * video_cc_xo_clk + */ + regmap_update_bits(regmap, 0xe58, BIT(0), BIT(0)); + regmap_update_bits(regmap, 0xeec, BIT(0), BIT(0)); + ret = qcom_cc_really_probe(pdev, &video_cc_lahaina_desc, regmap); if (ret) { dev_err(&pdev->dev, "Failed to register VIDEO CC clocks\n"); return ret; } + pm_runtime_put_sync(&pdev->dev); dev_info(&pdev->dev, "Registered VIDEO CC clocks\n"); return ret; @@ -608,12 +577,17 @@ static void video_cc_lahaina_sync_state(struct device *dev) qcom_cc_sync_state(dev, &video_cc_lahaina_desc); } +static const struct dev_pm_ops video_cc_lahaina_pm_ops = { + SET_RUNTIME_PM_OPS(qcom_cc_runtime_suspend, qcom_cc_runtime_resume, NULL) +}; + static struct platform_driver video_cc_lahaina_driver = { .probe = video_cc_lahaina_probe, .driver = { .name = "video_cc-lahaina", .of_match_table = video_cc_lahaina_match_table, .sync_state = video_cc_lahaina_sync_state, + .pm = &video_cc_lahaina_pm_ops, }, };