@@ -3902,6 +3902,48 @@ static void clk_core_free_parent_map(struct clk_core *core)
kfree(core->parents);
}
+static void
+__clk_register_critical_clock(struct clk_core *core, struct clk_hw *hw)
+{
+ struct device_node *np = core->of_node;
+ struct of_phandle_args clkspec;
+ u32 clksize, clktotal;
+ int ret, i, index;
+
+ if (!np)
+ return;
+
+ if (of_property_read_u32(np, "#clock-cells", &clksize))
+ return;
+
+ /* Clock node with #clock-cells = <0> uses critical-clocks; */
+ if (clksize == 0) {
+ if (of_property_read_bool(np, "critical-clocks"))
+ core->flags |= CLK_IS_CRITICAL;
+ return;
+ }
+
+ if (!core->ops->match_clkspec)
+ return;
+
+ clkspec.np = np;
+ clktotal = of_property_count_u32_elems(np, "critical-clocks");
+ clktotal /= clksize;
+ for (index = 0; index < clktotal; index++) {
+ for (i = 0; i < clksize; i++) {
+ ret = of_property_read_u32_index(np, "critical-clocks",
+ (index * clksize) + i,
+ &(clkspec.args[i]));
+ if (ret) {
+ pr_warn("Skipping critical-clocks index %d (ret=%d)\n",
+ i, ret);
+ }
+ }
+ if (!core->ops->match_clkspec(hw, &clkspec))
+ core->flags |= CLK_IS_CRITICAL;
+ }
+}
+
static struct clk *
__clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)
{
@@ -3946,6 +3988,8 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)
core->min_rate = 0;
core->max_rate = ULONG_MAX;
+ __clk_register_critical_clock(core, hw);
+
ret = clk_core_populate_parent_map(core, init);
if (ret)
goto fail_parents;
@@ -205,6 +205,8 @@ struct clk_duty {
* directory is provided as an argument. Called with
* prepare_lock held. Returns 0 on success, -EERROR otherwise.
*
+ * @match_clkspec: Check whether clk_hw matches DT clock specifier.
+ * Returns 0 on success, -EERROR otherwise.
*
* The clk_enable/clk_disable and clk_prepare/clk_unprepare pairs allow
* implementations to split any work between atomic (enable) and sleepable
@@ -252,6 +254,7 @@ struct clk_ops {
int (*init)(struct clk_hw *hw);
void (*terminate)(struct clk_hw *hw);
void (*debug_init)(struct clk_hw *hw, struct dentry *dentry);
+ int (*match_clkspec)(struct clk_hw *hw, struct of_phandle_args *clkspec);
};
/**
Some platforms require select clock to be always running, e.g. because those clock supply vital devices which are not otherwise attached to the system and thus do not have a matching DT node and clock consumer. An example is a system where the SoC serves as a crystal oscillator replacement for a programmable logic device. The "critical-clocks" property of a clock controller allows listing clock which must never be turned off. Clock listed in the "critical-clocks" property may have other consumers in DT, listing the clock in "critical-clocks" only assures those clock are never turned off, and none of these optional additional consumers can turn the clock off either. This is achieved by adding CLK_IS_CRITICAL flag to these critical clock. This flag has thus far been added to select clock by hard-coding it in various clock drivers, this patch provides generic DT interface to add the flag to arbitrary clock that may be critical. The implementation is modeled after "protected-clocks", except the protected clock property is currently driver specific. This patch attempts to provide a generic implementation of "critical-clocks" instead. Unlike "assigned-clocks", the "critical-clocks" must be parsed much earlier in __clk_register() to assign CLK_IS_CRITICAL flag to clk_init_data .flags field. The new match_clkspec() callback is used to determine whether struct clk_hw that is currently being registered matches the clock specifier in the DT "critical-clocks" property, and if so, then the CLK_IS_CRITICAL is added to these newly registered clock. This callback can only be driver specific. Signed-off-by: Marek Vasut <marex@denx.de> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Michael Turquette <mturquette@baylibre.com> Cc: Rob Herring <robh+dt@kernel.org> Cc: Stephen Boyd <sboyd@kernel.org> Cc: devicetree@vger.kernel.org To: linux-clk@vger.kernel.org --- V2: - Warn in case critical-clock field cannot be parsed and skip those clock - Use match_clkspec() only for non-zero clock-cells controllers - Pull the critical-clock code into __clk_register_critical_clock() - Update commit message V3: - Pick np from clk_core->of_node --- drivers/clk/clk.c | 44 ++++++++++++++++++++++++++++++++++++ include/linux/clk-provider.h | 3 +++ 2 files changed, 47 insertions(+)