[PATCH v3 3/5] clk: introduce the common clock framework
From: Shawn Guo <hidden>
Date: 2011-11-26 13:10:24
Also in:
linux-omap, lkml
On Mon, Nov 21, 2011 at 05:40:45PM -0800, Mike Turquette wrote: [...]
+/** + * DOC: Using the CLK_PARENT_SET_RATE flag + * + * __clk_set_rate changes the child's rate before the parent's to more + * easily handle failure conditions. + * + * This means clk might run out of spec for a short time if its rate is + * increased before the parent's rate is updated. + * + * To prevent this consider setting the CLK_GATE_SET_RATE flag on any + * clk where you also set the CLK_PARENT_SET_RATE flag
Eh, this is how flag CLK_GATE_SET_RATE is born? Does that mean to make the call succeed all the clocks on the propagating path need to be gated before clk_set_rate gets called?
+ */
+struct clk *__clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ struct clk *fail_clk = NULL;
+ int ret;
+ unsigned long old_rate = clk->rate;
+ unsigned long new_rate;
+ unsigned long parent_old_rate;
+ unsigned long parent_new_rate = 0;
+ struct clk *child;
+ struct hlist_node *tmp;
+
+ /* bail early if we can't change rate while clk is enabled */
+ if ((clk->flags & CLK_GATE_SET_RATE) && clk->enable_count)
+ return clk;
+
+ /* find the new rate and see if parent rate should change too */
+ WARN_ON(!clk->ops->round_rate);
+
+ new_rate = clk->ops->round_rate(clk, rate, &parent_new_rate);
+
+ /* FIXME propagate pre-rate change notification here */
+ /* XXX note that pre-rate change notifications will stack */
+
+ /* change the rate of this clk */
+ ret = clk->ops->set_rate(clk, new_rate);Yes, right here, the clock gets set to some unexpected rate, since the parent clock has not been set to the target rate yet at this point.
+ if (ret)
+ return clk;
+
+ /*
+ * change the rate of the parent clk if necessary
+ *
+ * hitting the nested 'if' path implies we have hit a .set_rate
+ * failure somewhere upstream while propagating __clk_set_rate
+ * up the clk tree. roll back the clk rates one by one and
+ * return the pointer to the clk that failed. clk_set_rate will
+ * use the pointer to propagate a rate-change abort notifier
+ * from the "highest" point.
+ */
+ if ((clk->flags & CLK_PARENT_SET_RATE) && parent_new_rate) {
+ parent_old_rate = clk->parent->rate;
+ fail_clk = __clk_set_rate(clk->parent, parent_new_rate);
+
+ /* roll back changes if parent rate change failed */
+ if (fail_clk) {
+ pr_warn("%s: failed to set parent %s rate to %lu\n",
+ __func__, fail_clk->name,
+ parent_new_rate);
+ clk->ops->set_rate(clk, old_rate);
+ }
+ return fail_clk;
+ }
+
+ /*
+ * set clk's rate & recalculate the rates of clk's children
+ *
+ * hitting this path implies we have successfully finished
+ * propagating recursive calls to __clk_set_rate up the clk tree
+ * (if necessary) and it is safe to propagate clk_recalc_rates and
+ * post-rate change notifiers down the clk tree from this point.
+ */
+ clk->rate = new_rate;
+ /* FIXME propagate post-rate change notifier for only this clk */
+ hlist_for_each_entry(child, tmp, &clk->children, child_node)
+ clk_recalc_rates(child);
+
+ return fail_clk;
+}[...]
quoted hunk ↗ jump to hunk
diff --git a/include/linux/clk.h b/include/linux/clk.h index 7213b52..3b0eb3f 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h@@ -3,6 +3,8 @@ * * Copyright (C) 2004 ARM Limited. * Written by Deep Blue Solutions Limited. + * Copyright (c) 2010-2011 Jeremy Kerr <jeremy.kerr@canonical.com> + * Copyright (C) 2011 Linaro Ltd <mturquette@linaro.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as@@ -13,17 +15,134 @@ #include <linux/kernel.h> +#include <linux/kernel.h>
Eh, why adding a duplication?
+#include <linux/errno.h> + struct device; +struct clk;
Do you really need this? [...]
+struct clk_hw_ops {
+ int (*prepare)(struct clk *clk);
+ void (*unprepare)(struct clk *clk);
+ int (*enable)(struct clk *clk);
+ void (*disable)(struct clk *clk);
+ unsigned long (*recalc_rate)(struct clk *clk);
+ long (*round_rate)(struct clk *clk, unsigned long,The return type seems interesting. If we intend to return a rate, it should be 'unsigned long' rather than 'long'. I'm just curious about the possible reason behind that.
+ unsigned long *); + int (*set_parent)(struct clk *clk, struct clk *); + struct clk * (*get_parent)(struct clk *clk); + int (*set_rate)(struct clk *clk, unsigned long);
Nit: would it be reasonable to put set_rate after round_rate to group *_rate functions together?
quoted hunk ↗ jump to hunk
+}; + +/** + * clk_init - initialize the data structures in a struct clk + * @dev: device initializing this clk, placeholder for now + * @clk: clk being initialized + * + * Initializes the lists in struct clk, queries the hardware for the + * parent and rate and sets them both. Adds the clk to the sysfs tree + * topology. + * + * Caller must populate .name, .flags and .ops before calling clk_init. + */ +void clk_init(struct device *dev, struct clk *clk); + +#endif /* !CONFIG_GENERIC_CLK */ /** * clk_get - lookup and obtain a reference to a clock producer.@@ -107,9 +226,16 @@ static inline void clk_unprepare(struct clk *clk) } #endif +int __clk_enable(struct clk *clk); +void __clk_disable(struct clk *clk); +int __clk_prepare(struct clk *clk); +void __clk_unprepare(struct clk *clk); +void __clk_reparent(struct clk *clk, struct clk *new_parent); +
Do we really need to export all these common clk internal functions? Regards, Shawn
/** * clk_get_rate - obtain the current clock rate (in Hz) for a clock source. * This is only valid once the clock source has been enabled. + * Returns zero if the clock rate is unknown. * @clk: clock source */ unsigned long clk_get_rate(struct clk *clk); -- 1.7.4.1