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..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]) @@ -329,6 +371,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); 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