A common clock framework
One of the big problem areas that has been identified in the ARM kernel trees is the diversity of implementations for various things that could be shared—either within the ARM tree or more widely with the rest of the kernel. That problem has led to a large amount of duplicated code in the ARM tree, both via cut-and-paste and code that is conceptually similar but uses different data structures and APIs. The latter makes the creation of a single kernel image that can boot on multiple ARM platforms impossible, so there are efforts to consolidate these implementations. The common clock framework is one such effort.
In a typical ARM system-on-chip (SoC), there can be dozens of different clocks for use by various I/O and other devices in the SoC. Typically those clocks are hooked together into elaborate tree-like structures. In those trees, child clocks can sometimes only change their frequency if the parent (and any other children) are correspondingly changed; disabling certain clocks will affect other clocks in the system and so on. Each ARM platform/SoC has its own way of encapsulating that information and presenting it to other parts of the system (like power and thermal management controllers), which makes it difficult to create platform-independent solutions.
The first problem that a common clock framework faces is the sheer number of different struct clk definitions scattered throughout the ARM tree. There are more than two dozen definitions in arch/arm currently, but the proposal for a common framework not surprisingly reduces that number to one. Implementations can wrap the struct clk in another structure that holds hardware-specific data, but the common structure looks like:
struct clk {
const char *name;
const struct clk_hw_ops *ops;
struct clk *parent;
unsigned long rate;
unsigned long flags;
unsigned int enable_count;
unsigned int prepare_count;
struct hlist_head children;
struct hlist_node child_node;
};
The parent and children/child_node fields allow the clocks to be arranged into trees, while the rate field tracks the current clock frequency (in Hz). The flags field is used to describe the clock type (e.g. whether a rate change needs to be done on the parent clock, or that the clock must be disabled before changing the rate). The two *_count fields are for tracking calls to the enable and prepare operations, while the bulk of the "work" is done within the struct clk_hw_ops field (ops).
Each of the entries in the clk_hw_ops structure correspond to a function in the driver-facing API for the clock framework. That API does some sanity checking before calling the corresponding operation from clk_hw_ops:
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,
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);
};
clk_prepare() is used to initialize
the clock to a state where it could be enabled, and that call must be made
before clk_enable(), which actually starts the clock running.
clk_disable() and clk_unprepare() do the reverse and
should be called in that order. The difference is that
clk_prepare() can sleep, while clk_enable() must not, so
having two separate calls allows the clock initialization to be split into
atomic and non-atomic pieces.
clk_get_parent() and clk_set_parent() do what the names imply, simply returning or changing the parent field, though setting the parent only succeeds if the clock is not already in use (otherwise -EBUSY is returned). clk_recalc_rate() queries the hardware, rather than the cached rate field, for the current frequency of the clock. clk_round_rate() rounds a frequency in Hz to a rate that the clock can actually use, and can also be used to determine the correct frequency for the parent clock when changing rates. All of those are more or less helper functions for clk_set_rate().
clk_set_rate() changes the frequency of a clock, but it must take into account some other factors. If the CLK_PARENT_SET_RATE flag value is set for the clock, clk_set_rate() needs to propagate the change to the parent clock (which may also have that flag set, necessitating a recursive traversal of the tree, attempting to set the rate at each level).
Drivers can also register their interest in being notified of rate changes with the clk_notifier_register() function. Three different types of notification can be requested: before the clock's rate changes, after it has been changed, or if the change gets aborted after the pre-change notifications have been called (i.e. PRE_RATE_CHANGE, POST_RATE_CHANGE, and ABORT_RATE_CHANGE). In each case, both the old and new values for the rate get passed as part of the notification callback. The patch to add notifications creates another operation in clk_hw_ops called speculate_rate(), which notes potential rate changes and sends any needed pre-change notifications as it walks the sub-tree.
The patch set also exports the clock hierarchy into debugfs. Each top-level clock gets a directory in ../debug/clk that contains read-only files to report the clock's rate, flags, prepare and enable counts, and the number of notifiers registered. Subdirectories are created for each child clock containing the same information.
The common clock framework has been around for some time in various forms.
The current incarnation is being shepherded by Mike Turquette, but he notes
that it is based on work originally done by Jeremy Kerr and Ben
Herrenschmidt. Beyond that: "Many others contributed to those
patches and promptly had their work stolen by me
".
Turquette has also posted a patch set with
an example
that replaces the OMAP4 clocks using the framework.
The comments on the most recent iteration have been fairly light, but still substantive, so we are clearly a ways off from seeing a version in the mainline. It's clearly on the radar of ARM developers, and would clean up a fair amount of code duplication within that tree, so we should see something in the mainline soon—hopefully in one of the next few kernel releases.
| Index entries for this article | |
|---|---|
| Kernel | Clocks |