generic struct clk patches
Subsystems (5)
cover generic struct clk patches
I got a request from Mike to post the updates I'd done to the generic struct clk so this is a repost of the patches Jeremy did plus what I've got sitting on top of them. Everything is exactly the same as the last posting, just resending.
PATCH 1/11 clk: Add a generic clock infrastructure
From: Jeremy Kerr <[email protected]>
We currently have ~21 definitions of struct clk in the ARM architecture, each defined on a per-platform basis. This makes it difficult to define platform- (or architecture-) independent clock sources without making assumptions about struct clk, and impossible to compile two platforms with different struct clks into a single image.
This change is an effort to unify struct clk where possible, by defining a common struct clk, and a set of clock operations. Different clock implementations can set their own operations, and have a standard interface for generic code. The callback interface is exposed to the kernel proper, while the clock implementations only need to be seen by the platform internals.
The interface is split into two halves:
* struct clk, which is the generic-device-driver interface. This provides a set of functions which drivers may use to request enable/disable, query or manipulate in a hardware-independent manner.
* struct clk_hw and struct clk_hw_ops, which is the hardware-specific interface. Clock drivers implement the ops, which allow the core clock code to implement the generic 'struct clk' API.
This allows us to share clock code among platforms, and makes it possible to dynamically create clock devices in platform-independent code.
Platforms can enable the generic struct clock through CONFIG_GENERIC_CLK. In this case, the clock infrastructure consists of a common, opaque struct clk, and a set of clock operations (defined per type of clock):
struct clk_hw_ops {
int (*prepare)(struct clk_hw *);
void (*unprepare)(struct clk_hw *);
int (*enable)(struct clk_hw *);
void (*disable)(struct clk_hw *);
unsigned long (*recalc_rate)(struct clk_hw *);
int (*set_rate)(struct clk_hw *,
unsigned long, unsigned long *);
long (*round_rate)(struct clk_hw *, unsigned long);
int (*set_parent)(struct clk_hw *, struct clk *);
struct clk * (*get_parent)(struct clk_hw *);
};Platform clock code can register a clock through clk_register, passing a set of operations, and a pointer to hardware-specific data:
struct clk_hw_foo {
struct clk_hw clk;
void __iomem *enable_reg;
};
#define to_clk_foo(c) offsetof(c, clk_hw_foo, clk)
static int clk_foo_enable(struct clk_hw *clk)
{
struct clk_foo *foo = to_clk_foo(clk);
raw_writeb(foo->enable_reg, 1);
return 0;
}
struct clk_hw_ops clk_foo_ops = {
.enable = clk_foo_enable,
};And in the platform initialisation code:
struct clk_foo my_clk_foo;
void init_clocks(void)
{
my_clk_foo.enable_reg = ioremap(...);
clk_register(&clk_foo_ops, &my_clk_foo, NULL);
}Changes from Thomas Gleixner <[email protected]>.
The common clock definitions are based on a development patch from Ben Herrenschmidt <[email protected]>.
TODO:
* We don't keep any internal reference to the clock topology at present.
--- drivers/clk/Kconfig | 3 + drivers/clk/Makefile | 1 + drivers/clk/clk.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/clk/clkdev.c | 7 ++ include/linux/clk.h | 102 ++++++++++++++++++++-- 5 files changed, 332 insertions(+), 10 deletions(-) create mode 100644 drivers/clk/clk.c
drivers/clk/Kconfig +3 -0
@@ -5,3 +5,6 @@ config CLKDEV_LOOKUP config HAVE_MACH_CLKDEV bool + +config GENERIC_CLK + bool
drivers/clk/Makefile +1 -0
@@ -1,2 +1,3 @@ obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o +obj-$(CONFIG_GENERIC_CLK) += clk.o
drivers/clk/clk.c new file +229 -0
229 lines not on this page · raw message
drivers/clk/clkdev.c +7 -0
@@ -23,6 +23,13 @@static LIST_HEAD (clocks);static DEFINE_MUTEX (clocks_mutex); +/* For USE_COMMON_STRUCT_CLK, these are provided in clk.c, but not exported +* through other headers; we don't want them used anywhere but here. */ +#ifdef CONFIG_USE_COMMON_STRUCT_CLK +extern int __clk_get (struct clk *clk); +extern void __clk_put (struct clk *clk); +#endif +/* * Find the correct struct clk for the device and connection ID. * We do slightly fuzzy matching here:
include/linux/clk.h +92 -10
133 lines not on this page · raw message
Signature · 1 line
1.7.5.4
PATCH 2/11 clk: Implement clk_set_rate
From: Jeremy Kerr <[email protected]>
Implemenent clk_set_rate by adding a set_rate callback to clk_hw_ops, and core code to handle propagation of rate changes up and down the clock tree.
--- drivers/clk/clk.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++---- include/linux/clk.h | 12 ++++++++ 2 files changed, 80 insertions(+), 6 deletions(-)
drivers/clk/clk.c +68 -6
@@ -21,6 +21,8 @@ struct clk {unsigned int enable_count;unsigned int prepare_count;struct clk *parent; +struct hlist_head children; +struct hlist_node child_node;unsigned long rate; };
@@ -176,10 +178,61 @@ long clk_round_rate(struct clk *clk, unsigned long rate) }EXPORT_SYMBOL_GPL (clk_round_rate); +/* +* clk_recalc_rates - Given a clock (with a recently updated clk->rate), +* notify its children that the rate may need to be recalculated, using +* ops->recalc_rate(). +*/ +static void clk_recalc_rates (struct clk *clk) +{ +struct hlist_node *tmp; +struct clk *child; + +if (clk->ops->recalc_rate) + clk->rate = clk->ops->recalc_rate (clk->hw); + +hlist_for_each_entry (child, tmp, &clk->children, child_node) +clk_recalc_rates (child); +} +int clk_set_rate (struct clk *clk,unsigned long rate) { -/* not yet implemented */ -return -ENOSYS ; +unsigned long parent_rate, new_rate; +int ret; + +if (!clk->ops->set_rate) +return -ENOSYS ; + + new_rate = rate; + +/* prevent racing with updates to the clock topology */ +mutex_lock (&prepare_lock); + +propagate: + ret = clk->ops->set_rate (clk->hw, new_rate, &parent_rate); + +if (ret <0 ) +return ret; + +/* ops->set_rate may require the parent's rate to change (to +* parent_rate), we need to propagate the set_rate call to the +* parent. +*/ +if (ret ==CLK_SET_RATE_PROPAGATE ) { + new_rate = parent_rate; + clk = clk->parent; + goto propagate; + } + +/* If successful (including propagation to the parent clock(s)), +* recalculate the rates of the clock, including children. We'll be +* calling this on the 'parent-most' clock that we propagated to. +*/ +clk_recalc_rates (clk); + +mutex_unlock (&prepare_lock); + +return 0 ; }EXPORT_SYMBOL_GPL (clk_set_rate);
@@ -213,16 +266,25 @@ struct clk *clk_register(struct clk_hw_ops *ops, struct clk_hw *hw, clk->hw = hw; hw->clk = clk; -/* Query the hardware for parent and initial rate */ +/* Query the hardware for parent and initial rate. We may alter +* the clock topology, making this clock available from the parent's +* children list. So, we need to protect against concurrent +* accesses through set_rate +*/ +mutex_lock (&prepare_lock); -if (clk->ops->get_parent) -/* We don't to lock against prepare/enable here, as -* the clock is not yet accessible from anywhere */ +if (clk->ops->get_parent) { clk->parent = clk->ops->get_parent (clk->hw); +if (clk->parent) +hlist_add_head (&clk->child_node, + &clk->parent->children); + }if (clk->ops->recalc_rate) clk->rate = clk->ops->recalc_rate (clk->hw); +mutex_unlock (&prepare_lock); +return clk; }
include/linux/clk.h +12 -0
@@ -58,6 +58,12 @@ struct clk_hw {* parent. Currently only called when the clock is first * registered. * +* @set_rate Change the rate of this clock. If this callback returns +* CLK_SET_RATE_PROPAGATE, the rate change will be propagated to +* the parent clock (which may propagate again). The requested +* rate of the parent is passed back from the callback in the +* second 'unsigned long *' argument. +* * The clk_enable/clk_disable and clk_prepare/clk_unprepare pairs allow * implementations to split any work between atomic (enable) and sleepable * (prepare) contexts. If a clock requires sleeping code to be turned on, this
@@ -76,9 +82,15 @@ struct clk_hw_ops {void (*disable)(struct clk_hw *);unsigned long (*recalc_rate)(struct clk_hw *);long (*round_rate)(struct clk_hw *,unsigned long ); +int (*set_rate)(struct clk_hw *, +unsigned long ,unsigned long *);struct clk * (*get_parent)(struct clk_hw *); }; +enum { +CLK_SET_RATE_PROPAGATE =1 , +}; +/** * clk_prepare - prepare clock for atomic enabling. *
Signature · 1 line
1.7.5.4
PATCH 2/11 clk: Implement clk_set_rate
Quoted 54 lines · Mark Brown
From: Jeremy Kerr <[email protected]>
Implemenent
clk_set_rateby adding aset_ratecallback toclk_hw_ops, and core code to handle propagation of rate changes up and down the clock tree.Signed-off-by Jeremy Kerr <[email protected]>Signed-off-by Mark Brown <[email protected]>--- drivers/clk/clk.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++---- include/linux/clk.h | 12 ++++++++ 2 files changed, 80 insertions(+), 6 deletions(-)drivers/clk/clk.c +22
-2@@ -21,6 +21,8 @@ struct clk {unsigned int enable_count;unsigned int prepare_count;struct clk *parent; +struct hlist_head children; +struct hlist_node child_node;unsigned long rate; };@@ -176,10 +178,61 @@ long clk_round_rate(struct clk *clk, unsigned long rate) }EXPORT_SYMBOL_GPL (clk_round_rate); +/* +* clk_recalc_rates - Given a clock (with a recently updated clk->rate), +* notify its children that the rate may need to be recalculated, using +* ops->recalc_rate(). +*/ +static void clk_recalc_rates (struct clk *clk) +{ +struct hlist_node *tmp; +struct clk *child; + +if (clk->ops->recalc_rate) + clk->rate = clk->ops->recalc_rate (clk->hw); + +hlist_for_each_entry (child, tmp, &clk->children, child_node) +clk_recalc_rates (child); +} +int clk_set_rate (struct clk *clk,unsigned long rate) { -/* not yet implemented */ -return -ENOSYS ; +unsigned long parent_rate, new_rate; +int ret; +
drivers/clk/clk.c long clk_round_rate(struct clk *clk, unsigned long rate) · line 212
Quoted 10 lines
drivers/clk/clk.c +10
-0+if (!clk->ops->set_rate) +return -ENOSYS ; + + new_rate = rate; + +/* prevent racing with updates to the clock topology */ +mutex_lock (&prepare_lock); + +propagate: + ret = clk->ops->set_rate (clk->hw, new_rate, &parent_rate);
You have to check for clk->ops->set_rate != NULL here for the propagation case.
drivers/clk/clk.c long clk_round_rate(struct clk *clk, unsigned long rate) · line 215
Quoted 3 lines
drivers/clk/clk.c +3
-0+ +if (ret <0 ) +return ret;
This returns with a mutex held. Also, some rates may already have changed here. I suggest a goto recalc_rates here.
drivers/clk/clk.c long clk_round_rate(struct clk *clk, unsigned long rate) · line 224
Quoted 9 lines
drivers/clk/clk.c +9
-0+ +/* ops->set_rate may require the parent's rate to change (to +* parent_rate), we need to propagate the set_rate call to the +* parent. +*/ +if (ret ==CLK_SET_RATE_PROPAGATE ) { + new_rate = parent_rate; + clk = clk->parent; + goto propagate;
before propagating you should check if the parent already has the desired rate. The parent may be a fixed clock which does not have a set_rate function, still this function shall succeed in this case.
drivers/clk/clk.c long clk_round_rate(struct clk *clk, unsigned long rate) · line 230
Quoted 6 lines
drivers/clk/clk.c +6
-0+ } + +/* If successful (including propagation to the parent clock(s)), +* recalculate the rates of the clock, including children. We'll be +* calling this on the 'parent-most' clock that we propagated to. +*/
recalc_rates:
Quoted 80 lines
drivers/clk/clk.c +18
-4+clk_recalc_rates (clk); + +mutex_unlock (&prepare_lock); + +return 0 ; }EXPORT_SYMBOL_GPL (clk_set_rate);@@ -213,16 +266,25 @@ struct clk *clk_register(struct clk_hw_ops *ops, struct clk_hw *hw, clk->hw = hw; hw->clk = clk; -/* Query the hardware for parent and initial rate */ +/* Query the hardware for parent and initial rate. We may alter +* the clock topology, making this clock available from the parent's +* children list. So, we need to protect against concurrent +* accesses through set_rate +*/ +mutex_lock (&prepare_lock); -if (clk->ops->get_parent) -/* We don't to lock against prepare/enable here, as -* the clock is not yet accessible from anywhere */ +if (clk->ops->get_parent) { clk->parent = clk->ops->get_parent (clk->hw); +if (clk->parent) +hlist_add_head (&clk->child_node, + &clk->parent->children); + }if (clk->ops->recalc_rate) clk->rate = clk->ops->recalc_rate (clk->hw); +mutex_unlock (&prepare_lock); +return clk; }include/linux/clk.h +12
-0@@ -58,6 +58,12 @@ struct clk_hw {* parent. Currently only called when the clock is first * registered. * +* @set_rate Change the rate of this clock. If this callback returns +* CLK_SET_RATE_PROPAGATE, the rate change will be propagated to +* the parent clock (which may propagate again). The requested +* rate of the parent is passed back from the callback in the +* second 'unsigned long *' argument. +* * The clk_enable/clk_disable and clk_prepare/clk_unprepare pairs allow * implementations to split any work between atomic (enable) and sleepable * (prepare) contexts. If a clock requires sleeping code to be turned on, this @@ -76,9 +82,15 @@ struct clk_hw_ops {void (*disable)(struct clk_hw *);unsigned long (*recalc_rate)(struct clk_hw *);long (*round_rate)(struct clk_hw *,unsigned long ); +int (*set_rate)(struct clk_hw *, +unsigned long ,unsigned long *);struct clk * (*get_parent)(struct clk_hw *); }; +enum { +CLK_SET_RATE_PROPAGATE =1 , +}; +/** * clk_prepare - prepare clock for atomic enabling. * Signature · 7 lines
1.7.5.4 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Signature · 4 lines
Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
PATCH 2/11 clk: Implement clk_set_rate
Quoted 8 lines · Sascha Hauer
drivers/clk/clk.c long clk_round_rate(struct clk *clk, unsigned long rate) · line 212
Quoted 4 lines · Mark Brown
From: Jeremy Kerr <[email protected]>
drivers/clk/clk.c +2
-0+propagate: + ret = clk->ops->set_rate (clk->hw, new_rate, &parent_rate);You have to check for
clk->ops->set_rate!= NULL here for the propagation case.
Note that I'm just forwarding on Jeremy's patches here, I'm not likely to be able to spend too much time developing the core.
PATCH 2/11 clk: Implement clk_set_rate
Quoted 4 lines · Mark Brown
Quoted 2 lines · Sascha Hauer
Quoted 1 line · Mark Brown
From: Jeremy Kerr <[email protected]>
Quoted 8 lines
Quoted 5 lines
drivers/clk/clk.c long clk_round_rate(struct clk *clk, unsigned long rate) · line 212
Quoted 2 lines
drivers/clk/clk.c +2
-0+propagate: + ret = clk->ops->set_rate (clk->hw, new_rate, &parent_rate);You have to check for
clk->ops->set_rate!= NULL here for the propagation case.Note that I'm just forwarding on Jeremy's patches here, I'm not likely to be able to spend too much time developing the core.
Ok, then we have at least some pointer in the archives for the one who continues on these patches.
Sascha
Signature · 4 lines
Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
PATCH 2/11 clk: Implement clk_set_rate
Quoted 7 lines · Sascha Hauer
Quoted 2 lines · Mark Brown
Note that I'm just forwarding on Jeremy's patches here, I'm not likely to be able to spend too much time developing the core.
Ok, then we have at least some pointer in the archives for the one who continues on these patches.
Yes, the review is definitely valuable - I just wanted to avoid any disappointment if you were expecting me to do anything with it (and perhaps even encourage you to do so yourself!).
PATCH 2/11 clk: Implement clk_set_rate
In reply to Mark BrownQuoted 5 lines · Mark Brown
From: Jeremy Kerr <[email protected]>
Implemenent
clk_set_rateby adding aset_ratecallback toclk_hw_ops, and core code to handle propagation of rate changes up and down the clock tree.
Propagating rates up the tree is wrong. If a parent rate must be changed then clk_set_rate should be called on the parent explicitly, then clk_set_rate can be called on the target child clk.
I don't see any other way to do it that is safe, save for some batch operation mechanism that takes predefined clock sub-trees into account (which is likely only feasible for SoCs since their data manuals provide all this info).
Quoted 5 lines
(quoted diff) +5
-0+/* + * clk_recalc_rates - Given a clock (with a recently updated clk->rate), + * ? ? notify its children that the rate may need to be recalculated, using + * ? ? ops->recalc_rate(). + */
Review
Quoted 10 lines
(quoted diff) +10
-0+static void clk_recalc_rates(struct clk *clk) +{ + ? ? ? struct hlist_node *tmp; + ? ? ? struct clk *child; + + ? ? ? if (clk->ops->recalc_rate) + ? ? ? ? ? ? ? clk->rate = clk->ops->recalc_rate(clk->hw); + + ? ? ? hlist_for_each_entry(child, tmp, &clk->children, child_node) + ? ? ? ? ? ? ? clk_recalc_rates(child);
We need a rate post-change notifier here that drivers can subscribe to. Since many devices do not support having their clocks changed on the fly while the device is enabled, a pre-change and post-change notifier are necessary to notify devices downstream of the clock being changed. Imagine doing a rate change high up in the tree (PLL) and you can see that many devices will need these notifiers propagated to them.
Quoted 8 lines
(quoted diff) +2
-0+} +?int
clk_set_rate(struct clk *clk, unsigned long rate)?{
- ? ? ? /* not yet implemented */
- ? ? ? return-ENOSYS;
+ ? ? ? unsigned longparent_rate,new_rate;
+ ? ? ? int ret;
drivers/clk/clk.c long clk_round_rate(struct clk *clk, unsigned long rate) · line 211
Quoted 10 lines
drivers/clk/clk.c +10
-0+ + ? ? ? if (!clk->ops->set_rate) + ? ? ? ? ? ? ? return -ENOSYS; + + ? ? ? new_rate = rate; + + ? ? ? /* prevent racing with updates to the clock topology */ + ? ? ? mutex_lock(&prepare_lock); + +propagate:
Need a rate pre-change notifier here that walks the tree. Drivers subscribing to this notifier can save context and call their pm_runtime_put or clk_disable bits so that they can survive the rate change without any glitches before the rate actually changes.
Quoted 4 lines
+ ? ? ? ret =
clk->ops->set_rate(clk->hw, new_rate, &parent_rate);(quoted diff) +3
-0+ + ? ? ? if (ret < 0) + ? ? ? ? ? ? ? return ret;
Review
Quoted 10 lines
(quoted diff) +10
-0+ + ? ? ? /* ops->set_rate may require the parent's rate to change (to + ? ? ? ?* parent_rate), we need to propagate the set_rate call to the + ? ? ? ?* parent. + ? ? ? ?*/ + ? ? ? if (ret == CLK_SET_RATE_PROPAGATE) { + ? ? ? ? ? ? ? new_rate = parent_rate; + ? ? ? ? ? ? ? clk = clk->parent; + ? ? ? ? ? ? ? goto propagate; + ? ? ? }
Again, I feel this is wrong. Rate change propagation should only flow downstream. Any rate change arbitration that requires changes upstream should be considered and handled by the driver.
Quoted 6 lines
+
+ ? ? ? /* If successful (including propagation to the parent clock(s)),
+ ? ? ? ?* recalculate the rates of the clock, including children. ?We'll be
+ ? ? ? ?* calling this on the 'parent-most' clock that we propagated to.
+ ? ? ? ?*/
+ ? ? ?clk_recalc_rates(clk);
As mentioned above, recalc should propagate rate post-change notification.
Regards,
Mike
PATCH 2/11 clk: Implement clk_set_rate
In reply to Mark Browndrivers/clk/clk.c struct clk { · line 25
Quoted 10 lines · Mark Brown
drivers/clk/clk.c +2
-0@@ -21,6 +21,8 @@ struct clk {unsigned int enable_count;unsigned int prepare_count;struct clk *parent; +struct hlist_head children; +struct hlist_node child_node;
Shouldn't these hlist nodes be initialized in clk_register() via INIT_HLIST_*()? I guess it's not explicitly needed because kzalloc is used, but perhaps it is better to be explicit should the kzalloc change to a kmalloc.
Quoted 30 lines
drivers/clk/clk.c +13
-4@@ -213,16 +266,25 @@ struct clk *clk_register(struct clk_hw_ops *ops, struct clk_hw *hw, clk->hw = hw; hw->clk = clk; -/* Query the hardware for parent and initial rate */ +/* Query the hardware for parent and initial rate. We may alter +* the clock topology, making this clock available from the parent's +* children list. So, we need to protect against concurrent +* accesses through set_rate +*/ +mutex_lock (&prepare_lock); -if (clk->ops->get_parent) -/* We don't to lock against prepare/enable here, as -* the clock is not yet accessible from anywhere */ +if (clk->ops->get_parent) { clk->parent = clk->ops->get_parent (clk->hw); +if (clk->parent) +hlist_add_head (&clk->child_node, + &clk->parent->children); + }if (clk->ops->recalc_rate) clk->rate = clk->ops->recalc_rate (clk->hw); +mutex_unlock (&prepare_lock); +return clk; }
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
PATCH 3/11 clk: Add fixed-rate clock
In reply to Mark BrownFrom: Jeremy Kerr <[email protected]>
--- drivers/clk/Kconfig | 4 ++++ drivers/clk/Makefile | 1 + drivers/clk/clk-fixed.c | 17 +++++++++++++++++ include/linux/clk.h | 14 ++++++++++++++ 4 files changed, 36 insertions(+), 0 deletions(-) create mode 100644 drivers/clk/clk-fixed.c
drivers/clk/Kconfig +4 -0
@@ -8,3 +8,7 @@ config HAVE_MACH_CLKDEV config GENERIC_CLK bool + +config GENERIC_CLK_FIXED + bool + depends on GENERIC_CLK
drivers/clk/Makefile +1 -0
@@ -1,3 +1,4 @@ obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o obj-$(CONFIG_GENERIC_CLK) += clk.o +obj-$(CONFIG_GENERIC_CLK_FIXED) += clk-fixed.o
drivers/clk/clk-fixed.c new file +17 -0
@@ -0,0 +1,17 @@ + +#include <linux/clk.h> +#include <linux/module.h> + +#define to_clk_fixed (c) container_of(c, struct clk_hw_fixed, hw) + +static unsigned long clk_fixed_recalc_rate (struct clk_hw *hw) +{ +return to_clk_fixed (hw)->rate; +} + +struct clk_hw_ops clk_fixed_ops = { + .recalc_rate = clk_fixed_recalc_rate, +}; +EXPORT_SYMBOL_GPL (clk_fixed_ops); + +
include/linux/clk.h +14 -0
@@ -110,6 +110,20 @@ int clk_prepare(struct clk *clk);*/ void clk_unprepare (struct clk *clk); +/* Base clock implementations. Platform clock implementations can use these +* directly, or 'subclass' as approprate */ + +#ifdef CONFIG_GENERIC_CLK_FIXED + +struct clk_hw_fixed { +struct clk_hw hw; +unsigned long rate; +}; + +extern struct clk_hw_ops clk_fixed_ops; + +#endif /* CONFIG_GENERIC_CLK_FIXED */ +#else /* !CONFIG_GENERIC_CLK */ /*
Signature · 1 line
1.7.5.4
PATCH 4/11 clk: Add simple gated clock
In reply to Mark BrownFrom: Jeremy Kerr <[email protected]>
--- drivers/clk/Kconfig | 4 ++++ drivers/clk/Makefile | 1 + drivers/clk/clk-gate.c | 41 +++++++++++++++++++++++++++++++++++++++++ include/linux/clk.h | 13 +++++++++++++ 4 files changed, 59 insertions(+), 0 deletions(-) create mode 100644 drivers/clk/clk-gate.c
drivers/clk/Kconfig +4 -0
@@ -12,3 +12,7 @@ config GENERIC_CLK config GENERIC_CLK_FIXED bool depends on GENERIC_CLK + +config GENERIC_CLK_GATE + bool + depends on GENERIC_CLK
drivers/clk/Makefile +1 -0
@@ -2,3 +2,4 @@ obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o obj-$(CONFIG_GENERIC_CLK) += clk.o obj-$(CONFIG_GENERIC_CLK_FIXED) += clk-fixed.o +obj-$(CONFIG_GENERIC_CLK_GATE) += clk-gate.o
drivers/clk/clk-gate.c new file +41 -0
41 lines not on this page · raw message
include/linux/clk.h +13 -0
@@ -124,6 +124,19 @@ extern struct clk_hw_ops clk_fixed_ops;#endif /* CONFIG_GENERIC_CLK_FIXED */ +#ifdef CONFIG_GENERIC_CLK_GATE + +struct clk_gate { +struct clk_hw hw; +void __iomem *reg; +u8 bit_idx; +}; + +extern struct clk_hw_ops clk_gate_ops; + +#endif /* CONFIG_GENERIC_CLK_GATE */ + +#else /* !CONFIG_GENERIC_CLK */ /*
Signature · 1 line
1.7.5.4
PATCH 4/11 clk: Add simple gated clock
Hi Mark, Jeremy,
Quoted 4 lines · Mark Brown
From: Jeremy Kerr <[email protected]>
Signed-off-by Jeremy Kerr <[email protected]>Signed-off-by Mark Brown <[email protected]>
I've just ported my (currently out-of-tree) platform to use the common struct clk and it all works nicely (if I add a naive implementation of clk_set_parent()).
Our platform has gateable clocks where bits in the control register are set to disable the clocks rather than enable them. I've used the patch below to add support for that.
Jamie
8<----
Subject: [PATCH] clk: allow gateable clocks to work with both polarities
Some devices (picoxcell in particular) have gateable clocks where the control register is a set-to-disable register. Create a new set of clock ops that can use struct clk_gate with this register configuration.
--- drivers/clk/clk-gate.c | 45 +++++++++++++++++++++++++++++++++++++-------- include/linux/clk.h | 3 ++- 2 files changed, 39 insertions(+), 9 deletions(-)
drivers/clk/clk-gate.c +37 -8
@@ -10,7 +10,7 @@ static unsigned long clk_gate_get_rate(struct clk_hw *clk)return clk_get_rate (clk_get_parent (clk->clk)); } -static int clk_gate_enable (struct clk_hw *clk) +static void clk_gate_set_bit (struct clk_hw *clk) {struct clk_gate *gate =to_clk_gate (clk);u32 reg;
@@ -18,11 +18,9 @@ static int clk_gate_enable(struct clk_hw *clk) reg =__raw_readl (gate->reg); reg |=1 << gate->bit_idx;__raw_writel (reg, gate->reg); - -return 0 ; } -static void clk_gate_disable (struct clk_hw *clk) +static void clk_gate_clear_bit (struct clk_hw *clk) {struct clk_gate *gate =to_clk_gate (clk);u32 reg;
@@ -32,10 +30,41 @@ static void clk_gate_disable(struct clk_hw *clk)__raw_writel (reg, gate->reg); } -struct clk_hw_ops clk_gate_ops = { +static int clk_gate_enable_set (struct clk_hw *clk) +{ +clk_gate_set_bit (clk); + +return 0 ; +} + +static void clk_gate_disable_clear (struct clk_hw *clk) +{ +clk_gate_clear_bit (clk); +} + +struct clk_hw_ops clk_gate_set_enable_ops = { + .recalc_rate = clk_gate_get_rate, + .enable = clk_gate_enable_set, + .disable = clk_gate_disable_clear, +}; +EXPORT_SYMBOL_GPL (clk_gate_set_enable_ops); + +static int clk_gate_enable_clear (struct clk_hw *clk) +{ +clk_gate_clear_bit (clk); + +return 0 ; +} + +static void clk_gate_disable_set (struct clk_hw *clk) +{ +clk_gate_set_bit (clk); +} + +struct clk_hw_ops clk_gate_set_disable_ops = { .recalc_rate = clk_gate_get_rate, - .enable = clk_gate_enable, - .disable = clk_gate_disable, + .enable = clk_gate_enable_clear, + .disable = clk_gate_disable_set, }; -EXPORT_SYMBOL_GPL (clk_gate_ops); +EXPORT_SYMBOL_GPL (clk_gate_set_disable_ops);
include/linux/clk.h +2 -1
@@ -132,7 +132,8 @@ struct clk_gate { u8 bit_idx; }; -extern struct clk_hw_ops clk_gate_ops; +extern struct clk_hw_ops clk_gate_set_enable_ops; +extern struct clk_hw_ops clk_gate_set_disable_ops;#endif /* CONFIG_GENERIC_CLK_GATE */
Signature · 1 line
1.7.4.1
PATCH 4/11 clk: Add simple gated clock
In reply to Mark Browndrivers/clk/clk-gate.c line 19
Quoted 7 lines · Mark Brown
drivers/clk/clk-gate.c new file +7
-0+static int clk_gate_enable (struct clk_hw *clk) +{ +struct clk_gate *gate =to_clk_gate (clk); +u32 reg; + + reg =__raw_readl (gate->reg); + reg |=1 << gate->bit_idx;
The BIT() macro might be clearer.
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
PATCH 5/11 clk: Prototype and document clk_register()
In reply to Mark BrownThis allows the compiler to ensure drivers are using the correct prototype.
--- include/linux/clk.h | 12 ++++++++++++ 1 files changed, 12 insertions(+), 0 deletions(-)
include/linux/clk.h +12 -0
@@ -136,6 +136,18 @@ extern struct clk_hw_ops clk_gate_ops;#endif /* CONFIG_GENERIC_CLK_GATE */ +/** +* clk_register - register and initialize a new clock +* +* @ops: ops for the new clock +* @hw: struct clk_hw to be passed to the ops of the new clock +* @name: name to use for the new clock +* +* Register a new clock with the clk subsytem. Returns either a +* struct clk for the new clock or a NULL pointer. +*/ +struct clk *clk_register (struct clk_hw_ops *ops,struct clk_hw *hw, +const char *name);#else /* !CONFIG_GENERIC_CLK */
Signature · 1 line
1.7.5.4
PATCH 5/11 clk: Prototype and document clk_register()
include/linux/clk.h extern struct clk_hw_ops clk_gate_ops; · line 146
Quoted 12 lines · Mark Brown
include/linux/clk.h +8
-0@@ -136,6 +136,18 @@ extern struct clk_hw_ops clk_gate_ops;#endif /* CONFIG_GENERIC_CLK_GATE */ +/** +* clk_register - register and initialize a new clock +* +* @ops: ops for the new clock +* @hw: struct clk_hw to be passed to the ops of the new clock +* @name: name to use for the new clock +* +* Register a new clock with the clk subsytem. Returns either a
s/subsytem/subsystem/
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
PATCH 6/11 clk: Provide a dummy clk_unregister()
In reply to Mark BrownEven though unregistration is not actually supported by the clk API it is still useful to provide a clk_unregister() so that drivers can implement their unregistration code. This saves having to go back later and update them once unregistration is possible.
--- include/linux/clk.h | 15 +++++++++++++++ 1 files changed, 15 insertions(+), 0 deletions(-)
include/linux/clk.h +15 -0
@@ -149,6 +149,21 @@ extern struct clk_hw_ops clk_gate_ops;struct clk *clk_register (struct clk_hw_ops *ops,struct clk_hw *hw,const char *name); +/** +* clk_unregister - remove a clock +* +* @clk: clock to unregister +* +* Remove a clock from the clk subsystem. This is currently not +* implemented but is provided to allow unregistration code to be +* written in drivers ready for use when an implementation is +* provided. +*/ +static inline int clk_unregister (struct clk *clk) +{ +return -ENOTSUPP ; +} +#else /* !CONFIG_GENERIC_CLK */ /*
Signature · 1 line
1.7.5.4
PATCH 6/11 clk: Provide a dummy clk_unregister()
Hi Mark,
Quoted 22 lines · Mark Brown
Even though unregistration is not actually supported by the clk API it is still useful to provide a
clk_unregister()so that drivers can implement their unregistration code. This saves having to go back later and update them once unregistration is possible.Signed-off-by Mark Brown <[email protected]>--- include/linux/clk.h | 15 +++++++++++++++ 1 files changed, 15 insertions(+), 0 deletions(-)include/linux/clk.h +4
-0@@ -149,6 +149,21 @@ extern struct clk_hw_ops clk_gate_ops;struct clk *clk_register (struct clk_hw_ops *ops,struct clk_hw *hw,const char *name); +/** +* clk_unregister - remove a clock +* +* @clk: clock to unregister
include/linux/clk.h extern struct clk_hw_ops clk_gate_ops; · line 165
Quoted 10 lines
include/linux/clk.h +10
-0+* +* Remove a clock from the clk subsystem. This is currently not +* implemented but is provided to allow unregistration code to be +* written in drivers ready for use when an implementation is +* provided. +*/ +static inline int clk_unregister (struct clk *clk) +{ +return -ENOTSUPP ; +}
ENOTSUPP is only defined in include/linux/errno.h which isn't included here. I think either linux/errno.h needs to be included or use EOPNOTSUPP?
Jamie
PATCH 7/11 clk: Constify struct clk_hw_ops
In reply to Mark Brown--- drivers/clk/clk.c | 4 ++-- include/linux/clk.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-)
drivers/clk/clk.c +2 -2
@@ -16,7 +16,7 @@struct clk {const char *name; -struct clk_hw_ops *ops; +const struct clk_hw_ops *ops;struct clk_hw *hw;unsigned int enable_count;unsigned int prepare_count;
@@ -252,7 +252,7 @@ int clk_set_parent(struct clk *clk, struct clk *parent) }EXPORT_SYMBOL_GPL (clk_set_parent); -struct clk *clk_register (struct clk_hw_ops *ops,struct clk_hw *hw, +struct clk *clk_register (const struct clk_hw_ops *ops,struct clk_hw *hw,const char *name) {struct clk *clk;
include/linux/clk.h +1 -1
@@ -146,7 +146,7 @@ extern struct clk_hw_ops clk_gate_ops;* Register a new clock with the clk subsytem. Returns either a * struct clk for the new clock or a NULL pointer. */ -struct clk *clk_register (struct clk_hw_ops *ops,struct clk_hw *hw, +struct clk *clk_register (const struct clk_hw_ops *ops,struct clk_hw *hw,const char *name);/**
Signature · 1 line
1.7.5.4
PATCH 8/11 clk: Avoid clock name collisions
In reply to Mark BrownCurrently the generic clk API identifies clocks internally using the name of the clock. This is OK for on-SoC clocks where we have enough control to disambiguate but doesn't work well for clocks provided on external chips where a system design may include more than one instance of the same chip (the Wolfson Speyside system is an example of this) or may have namespace collisions.
Address this by allowing the clock provider to supply a struct device for the clock for use in disambiguation. As a first pass if it is provided we prefix the clock name with the dev_name() of the device.
In order to avoid needless noise in names and memory usage it is strongly recommended that on-SoC clocks do not provide a struct device until the implementation is improved.
--- drivers/clk/clk.c | 36 ++++++++++++++++++++++++++++++++---- include/linux/clk.h | 14 ++++++++++---- 2 files changed, 42 insertions(+), 8 deletions(-)
drivers/clk/clk.c +32 -4
@@ -13,6 +13,7 @@#include <linux/mutex.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/device.h> struct clk {const char *name;
@@ -252,20 +253,44 @@ int clk_set_parent(struct clk *clk, struct clk *parent) }EXPORT_SYMBOL_GPL (clk_set_parent); -struct clk *clk_register (const struct clk_hw_ops *ops,struct clk_hw *hw, -const char *name) +struct clk *clk_register (struct device *dev,const struct clk_hw_ops *ops, +struct clk_hw *hw,const char *name) {struct clk *clk; +char *new_name; +size_t name_len; clk =kzalloc (sizeof (*clk),GFP_KERNEL );if (!clk)return NULL ; - clk->name = name; clk->ops = ops; clk->hw = hw; hw->clk = clk; +/* Since we currently match clock providers on a purely string +* based method add a prefix based on the device name if a +* device is provided. When we have support for device tree +* based clock matching it should be possible to avoid this +* mangling and instead use the struct device to hook into +* the bindings. +* +* As we don't currently support unregistering clocks we don't +* need to worry about cleanup as yet. +*/ +if (dev) { + name_len =strlen (name) +strlen (dev_name (dev)) +2 ; + new_name =kzalloc (name_len,GFP_KERNEL ); +if (!new_name) + goto err; + +snprintf (new_name, name_len,"%s-%s" ,dev_name (dev), name); + + clk->name = new_name; + }else { + clk->name = name; + } +/* Query the hardware for parent and initial rate. We may alter * the clock topology, making this clock available from the parent's * children list. So, we need to protect against concurrent
@@ -285,7 +310,10 @@ struct clk *clk_register(const struct clk_hw_ops *ops, struct clk_hw *hw,mutex_unlock (&prepare_lock); -return clk; + +err: +kfree (clk); +return NULL ; }EXPORT_SYMBOL_GPL (clk_register);
include/linux/clk.h +10 -4
@@ -139,15 +139,21 @@ extern struct clk_hw_ops clk_gate_ops;/** * clk_register - register and initialize a new clock * +* @dev: device providing the clock or NULL * @ops: ops for the new clock * @hw: struct clk_hw to be passed to the ops of the new clock * @name: name to use for the new clock * -* Register a new clock with the clk subsytem. Returns either a -* struct clk for the new clock or a NULL pointer. +* Register a new clock with the clk subsytem. If dev is provided +* then it will be used to disambiguate between multiple instances of +* the same device in the system, typically this should only be done +* for devices that are not part of the core SoC unless device tree is +* in use. +* +* Returns either a struct clk for the new clock or a NULL pointer. */ -struct clk *clk_register (const struct clk_hw_ops *ops,struct clk_hw *hw, -const char *name); +struct clk *clk_register (struct device *dev,const struct clk_hw_ops *ops, +struct clk_hw *hw,const char *name);/** * clk_unregister - remove a clock
Signature · 1 line
1.7.5.4
PATCH 8/11 clk: Avoid clock name collisions
drivers/clk/clk.c int clk_set_parent(struct clk *clk, struct clk *parent) · line 288
Quoted 8 lines · Mark Brown
drivers/clk/clk.c +8
-0+if (dev) { + name_len =strlen (name) +strlen (dev_name (dev)) +2 ; + new_name =kzalloc (name_len,GFP_KERNEL ); +if (!new_name) + goto err; + +snprintf (new_name, name_len,"%s-%s" ,dev_name (dev), name); +
Perhaps use kasprintf() instead?
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
PATCH 9/11 clk: Add Kconfig option to build all generic clk drivers
In reply to Mark BrownCurrently drivers for the generic clk subsystem must be selected by platforms using them in order to enable build. When doing development on the API or generic build time testing it is useful to be able to build unused drivers in order to improve coverage so supply a Kconfig option which allows this.
--- drivers/clk/Kconfig | 11 ++++++++++- 1 files changed, 10 insertions(+), 1 deletions(-)
drivers/clk/Kconfig +10 -1
@@ -1,4 +1,3 @@ - config CLKDEV_LOOKUP bool select HAVE_CLK
@@ -9,6 +8,16 @@ config HAVE_MACH_CLKDEV config GENERIC_CLK bool +config GENERIC_CLK_BUILD_TEST + bool "Build all generic clock drivers" + depends on EXPERIMENTAL && GENERIC_CLK + select GENERIC_CLK_FIXED + select GENERIC_CLK_GATE + help + Enable all possible generic clock drivers. This is only + useful for improving build coverage, it is not useful for + production kernel builds. + config GENERIC_CLK_FIXED bool depends on GENERIC_CLK
Signature · 1 line
1.7.5.4
PATCH 10/11 clk: Add initial WM831x clock driver
In reply to Mark BrownThe WM831x and WM832x series of PMICs contain a flexible clocking subsystem intended to provide always on and system core clocks. It features:
- A 32.768kHz crystal oscillator which can optionally be used to pass through an externally generated clock.
- A FLL which can be clocked from either the 32.768kHz oscillator or the CLKIN pin.
- A CLKOUT pin which can bring out either the oscillator or the FLL output.
- The 32.768kHz clock can also optionally be brought out on the GPIO pins of the device.
This driver fully supports the 32.768kHz oscillator and CLKOUT. The FLL is supported only in AUTO mode, the full flexibility of the FLL cannot currently be used. The use of clock references other than the internal oscillator is not currently supported, and since clk_set_parent() is not implemented in the generic clock API the clock tree configuration cannot be changed at runtime.
Due to a lack of access to systems where the core SoC has been converted to use the generic clock API this driver has been compile tested only.
--- MAINTAINERS | 1 + drivers/clk/Kconfig | 5 + drivers/clk/Makefile | 1 + drivers/clk/clk-wm831x.c | 386 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 393 insertions(+), 0 deletions(-) create mode 100644 drivers/clk/clk-wm831x.c
MAINTAINERS +1 -0
@@ -7213,6 +7213,7 @@ T: git git://opensource.wolfsonmicro.com/linux-2.6-audioplus W: http://opensource.wolfsonmicro.com/content/linux-drivers-wolfson-devices S: Supported F: Documentation/hwmon/wm83?? +F: drivers/clk/clk-wm83*.c F: drivers/leds/leds-wm83*.c F: drivers/mfd/wm8*.c F: drivers/power/wm83*.c
drivers/clk/Kconfig +5 -0
@@ -13,6 +13,7 @@ config GENERIC_CLK_BUILD_TEST depends on EXPERIMENTAL && GENERIC_CLK select GENERIC_CLK_FIXED select GENERIC_CLK_GATE + select GENERIC_CLK_WM831X if MFD_WM831X=y help Enable all possible generic clock drivers. This is only useful for improving build coverage, it is not useful for
@@ -25,3 +26,7 @@ config GENERIC_CLK_FIXED config GENERIC_CLK_GATE bool depends on GENERIC_CLK + +config GENERIC_CLK_WM831X + tristate + depends on GENERIC_CLK && MFD_WM831X
drivers/clk/Makefile +1 -0
@@ -3,3 +3,4 @@ obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o obj-$(CONFIG_GENERIC_CLK) += clk.o obj-$(CONFIG_GENERIC_CLK_FIXED) += clk-fixed.o obj-$(CONFIG_GENERIC_CLK_GATE) += clk-gate.o +obj-$(CONFIG_GENERIC_CLK_WM831X) += clk-wm831x.o
drivers/clk/clk-wm831x.c new file +386 -0
386 lines not on this page · raw message
Signature · 1 line
1.7.5.4
PATCH 11/11 x86: Enable generic clk API on x86
In reply to Mark BrownEnable the generic clk API on x86, enabling use of the API by drivers for x86 modules and also improving build coverage for clock API using devices.
--- arch/x86/Kconfig | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
arch/x86/Kconfig +1 -0
@@ -74,6 +74,7 @@ config X86 select HAVE_BPF_JIT if (X86_64 && NET) select CLKEVT_I8253 select ARCH_HAVE_NMI_SAFE_CMPXCHG + select GENERIC_CLK config INSTRUCTION_DECODER def_bool (KPROBES || PERF_EVENTS)
Signature · 1 line
1.7.5.4
PATCH 1/11 clk: Add a generic clock infrastructure
In reply to Mark BrownQuoted 18 lines · Mark Brown
drivers/clk/clk.c new file +18
-0+struct clk *clk_register (struct clk_hw_ops *ops,struct clk_hw *hw, +const char *name) +{ +struct clk *clk; + + clk =kzalloc (sizeof (*clk),GFP_KERNEL ); +if (!clk) +return NULL ; + + clk->name = name; + clk->ops = ops; + clk->hw = hw; + hw->clk = clk; + +/* Query the hardware for parent and initial rate */ + +if (clk->ops->get_parent) +/* We don't to lock against prepare/enable here, as
drivers/clk/clk.c line 229
Quoted 10 lines
drivers/clk/clk.c new file +10
-0+* the clock is not yet accessible from anywhere */ + clk->parent = clk->ops->get_parent (clk->hw); + +if (clk->ops->recalc_rate) + clk->rate = clk->ops->recalc_rate (clk->hw); + + +return clk; +} +EXPORT_SYMBOL_GPL (clk_register);
How will this play with clkdev? I think we'll need to add another function on top of this one that allows you to register a clk_hw and an array of clk_lookups at the same time. Otherwise we're going to get about 10 different implementations of the same code.
Something like:
struct clk *clk_register(struct clk_hw_ops *ops, struct clk_hw *hw,
const char *name, struct clk_lookup *lookups, size_t num_lookups)
?
include/linux/clk.h line 17
Quoted 11 lines
include/linux/clk.h +3
-0@@ -11,17 +12,103 @@#ifndef __LINUX_CLK_H#define __LINUX_CLK_H +#include <linux/err.h> +#include <linux/spinlock.h> +
I don't see these includes used in this header.
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
PATCH 1/11 clk: Add a generic clock infrastructure
In reply to Mark Browninclude/linux/clk.h line 110
Quoted 9 lines · Mark Brown
include/linux/clk.h +3
-1-struct clk ; +static inline int clk_prepare (struct clk *clk) {return 0 ; } +static inline void clk_unprepare (struct clk *clk) { } +
You should add might_sleep() inside these stubs. That way, if any device driver developers wants to move over to the new APIs before their arch does, they get to see the warnings when they call prepare/unprepare in atomic context.
Thanks,
Saravana
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
PATCH 1/11 clk: Add a generic clock infrastructure
Quoted 10 lines · Saravana Kannan
include/linux/clk.h line 109
Quoted 3 lines · Mark Brown
include/linux/clk.h +2
-1-struct clk ; +static inline int clk_prepare (struct clk *clk) {return 0 ; } +static inline void clk_unprepare (struct clk *clk) { }You should add
might_sleep()inside these stubs. That way, if any device driver developers wants to move over to the new APIs before their arch does, they get to see the warnings when they call prepare/unprepare in atomic context.
Just to repeat previous clarifications as I said when I reposted I've no intention of working on this, I'm just republishing Jeremy's work as Mike needed a copy.