From e92f678ecfa7efe5e68b73f51156f3a6c5bfdfc2 Mon Sep 17 00:00:00 2001 From: Mike Tipton Date: Wed, 4 Nov 2020 16:40:10 -0800 Subject: [PATCH 1/3] clk: qcom: Add qcom_clk_hw_get_name() The existing clk_hw_get_name() only works after the clock has been registered with the framework since it relies on hw->core->name and hw->core is NULL pre-registration. hw->init->name can be used pre-registration, but this doesn't work post-registration since the framework sets hw->init to NULL. Provide qcom_clk_hw_get_name(), which can be called in both pre and post-registration contexts. Change-Id: I334e545f5112961b7ab6879daca55c70236338d6 Signed-off-by: Mike Tipton --- drivers/clk/qcom/common.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h index 5fa895845b01..a33c8a4eabd1 100644 --- a/drivers/clk/qcom/common.h +++ b/drivers/clk/qcom/common.h @@ -84,4 +84,9 @@ int qcom_cc_runtime_init(struct platform_device *pdev, int qcom_cc_runtime_suspend(struct device *dev); int qcom_cc_runtime_resume(struct device *dev); +static inline const char *qcom_clk_hw_get_name(const struct clk_hw *hw) +{ + return hw->init ? hw->init->name : clk_hw_get_name(hw); +} + #endif From 16f59d369a7414041be6f2ee2447f23d16587661 Mon Sep 17 00:00:00 2001 From: Mike Tipton Date: Wed, 4 Nov 2020 16:40:22 -0800 Subject: [PATCH 2/3] clk: qcom: Add QCOM_CLK_IS_CRITICAL flag Add QCOM_CLK_IS_CRITICAL, which can be used to enable a clock by default in HW and to skip registering it with the framework. Change-Id: I803a447fae742ca2e20973046090d2b8331ab598 Signed-off-by: Mike Tipton --- drivers/clk/qcom/clk-regmap.c | 9 +++++++++ drivers/clk/qcom/clk-regmap.h | 2 ++ drivers/clk/qcom/common.c | 8 ++++++++ 3 files changed, 19 insertions(+) diff --git a/drivers/clk/qcom/clk-regmap.c b/drivers/clk/qcom/clk-regmap.c index c82bc3775706..21102b2ceee1 100644 --- a/drivers/clk/qcom/clk-regmap.c +++ b/drivers/clk/qcom/clk-regmap.c @@ -269,6 +269,7 @@ EXPORT_SYMBOL(clk_is_regmap_clk); */ int devm_clk_register_regmap(struct device *dev, struct clk_regmap *rclk) { + const struct clk_ops *ops; int ret; rclk->dev = dev; @@ -278,6 +279,14 @@ int devm_clk_register_regmap(struct device *dev, struct clk_regmap *rclk) else if (dev && dev->parent) rclk->regmap = dev_get_regmap(dev->parent, NULL); + if (rclk->flags & QCOM_CLK_IS_CRITICAL) { + ops = rclk->hw.init->ops; + if (ops && ops->enable) + ops->enable(&rclk->hw); + + return 0; + } + ret = devm_clk_hw_register(dev, &rclk->hw); if (!ret) list_add(&rclk->list_node, &clk_regmap_list); diff --git a/drivers/clk/qcom/clk-regmap.h b/drivers/clk/qcom/clk-regmap.h index 330159fe1448..124ef53e0aaa 100644 --- a/drivers/clk/qcom/clk-regmap.h +++ b/drivers/clk/qcom/clk-regmap.h @@ -51,6 +51,8 @@ struct clk_regmap { struct clk_regmap_ops *ops; struct list_head list_node; struct device *dev; +#define QCOM_CLK_IS_CRITICAL BIT(0) + unsigned long flags; }; #define to_clk_regmap(_hw) container_of(_hw, struct clk_regmap, hw) diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c index a5b219772ad8..3f20d378c293 100644 --- a/drivers/clk/qcom/common.c +++ b/drivers/clk/qcom/common.c @@ -329,6 +329,14 @@ int qcom_cc_really_probe(struct platform_device *pdev, return ret; clk_hw_populate_clock_opp_table(dev->of_node, &rclks[i]->hw); + + /* + * Critical clocks are enabled by devm_clk_register_regmap() + * and registration skipped. So remove from rclks so that the + * get() callback returns NULL and client requests are stubbed. + */ + if (rclks[i]->flags & QCOM_CLK_IS_CRITICAL) + rclks[i] = NULL; } ret = devm_of_clk_add_hw_provider(dev, qcom_cc_clk_hw_get, cc); From 0022172cdd686b211c98ff9ad66333d930730fbf Mon Sep 17 00:00:00 2001 From: Mike Tipton Date: Wed, 4 Nov 2020 17:47:24 -0800 Subject: [PATCH 3/3] clk: qcom: Add properties for marking clocks as critical Some clocks are critical only on specific boards, i.e. clocks used to interface with external PMICs over I2C. Which clocks are critical depend on which I2C bus is chosen to connect the PMICs. To support this, add properties to set QCOM_CLK_IS_CRITICAL on a per-board basis. The qcom,critical-clocks property can be used to set QCOM_CLK_IS_CRITICAL on individual clocks. The qcom,critical-devices property can be used to set QCOM_CLK_IS_CRITICAL on all clocks used by specified devices. Change-Id: I42d61ec13225026ba7d0b78079cf7a2ab67339f2 Signed-off-by: Mike Tipton --- drivers/clk/qcom/common.c | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c index 3f20d378c293..bfe3f583f971 100644 --- a/drivers/clk/qcom/common.c +++ b/drivers/clk/qcom/common.c @@ -232,6 +232,47 @@ static void qcom_cc_drop_protected(struct device *dev, struct qcom_cc *cc) } } +/* Set QCOM_CLK_IS_CRITICAL on clocks specified in dt */ +static void qcom_cc_set_critical(struct device *dev, struct qcom_cc *cc) +{ + struct of_phandle_args args; + struct device_node *np; + struct property *prop; + const __be32 *p; + u32 clock_idx; + u32 i; + int cnt; + + of_property_for_each_u32(dev->of_node, "qcom,critical-clocks", prop, p, i) { + if (i >= cc->num_rclks) + continue; + + cc->rclks[i]->flags |= QCOM_CLK_IS_CRITICAL; + } + + of_property_for_each_u32(dev->of_node, "qcom,critical-devices", prop, p, i) { + np = of_find_node_by_phandle(i); + if (!np) + continue; + + cnt = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + + for (i = 0; i < cnt; i++) { + of_parse_phandle_with_args(np, "clocks", "#clock-cells", + i, &args); + clock_idx = args.args[0]; + + if (args.np != dev->of_node || clock_idx >= cc->num_rclks) + continue; + + cc->rclks[clock_idx]->flags |= QCOM_CLK_IS_CRITICAL; + of_node_put(args.np); + } + + of_node_put(np); + } +} + static struct clk_hw *qcom_cc_clk_hw_get(struct of_phandle_args *clkspec, void *data) { @@ -310,6 +351,7 @@ int qcom_cc_really_probe(struct platform_device *pdev, cc->num_clk_hws = num_clk_hws; qcom_cc_drop_protected(dev, cc); + qcom_cc_set_critical(dev, cc); for (i = 0; i < num_clk_hws; i++) { if (!clk_hws[i])