Common Clock Framework: How To Use It – Gregory Clement (Free Electrons)

The clock framework gives drivers the opportunity to control a board/SoC’s clocks. This is useful for power management (turn off unused clocks) and to control timing parameters (e.g. baudrate, memory timings). The clock API exists for a long time already, but each cpu had its own implementation, which made life difficult for ARM common zImage.

The common clock framework implements the clk_* API, it has some basic clock drivers and makes it possible to implement custom clock drivers in an abstract way. It also represents the clocks as a tree, which is exposed in debugfs and declared in device tree. The device tree specifies the clocks, their type (which clock driver to use) and their relationships. It is accessed through the clk_* functions, which map to struct clk_ops methods.

The core of the framework is struct clk. Clock drivers implement the clk_ops substructure, with the .hw field of struct clk as context. New clock drivers should only be implemented for new types of hardware clocks, so it should be independent of the SoC. The core takes care of the clock tree, concurrency, propagating rate changes through the tree, and notification callbacks when the rate changes.

To implement a hardware clock driver, you need to register the clk_ops struct. Not all members need to be implemented, most have defaults. For instance, a gateable clock needs .enable, .disable and .is_enabled. A clock that can change rate needs recalc_rate, set_rate and either round_rate or determine_rate.

To make the clock available, first .prepare is called. This does not yet enable the clock. In this function you may sleep, so you can use e.g. I2C control in this function. .enable just ungates the clock and is called from atomic context so must not sleep. is_enabled checks in hardware if the clock is enabled, e.g. during boot. Similar for is_prepared. In the API, there’s a clk_prepare_enable which does both (can’t be called from atomic).

round_rate selects the rate closest to the requested one. determine_rate does the same but even better by changing the clock’s parent. The actual setting is done by set_rate. recalc_rate is called when a parent changes rate.

get_parent and set_parent must be implemented for mux clocks i.e. clocks that can have multiple parents. get_parent is only called at boot time; after that, the kernel’s cached information is used.

The clock framework will, after everything has been initialized, scan the clock tree and disable the clocks that are not used. This is done with a special disable_unused method (default to disable), so it can be done in a different way in this case. This is required for complexities on the OMAP SoCs.

Base clocks: fixed-rate; gate (same rate as parent, just enable/disable); mux (select a parent, same rate as this parent, no enable/disable); fixed-factor (fixed factor rate compared to parent, no rate change); divider (selectable divider). Most of the time you have a single clock that can do several of these. This is represented by a composite clock, which can combine a mux, a rate and a gate.

To create the clocks, register it with CLK_OF_DECLARE which calls the setup function. The setup function reads out the required arguments and configures the clock accordingly. In the device tree, you should only write information which cannot be discovered, so static information about the clock tree (e.g. a fixed divider) can still be put in source code. This way, it is possible to just define an array of clocks in the device tree; the CPU-specific clock setup code will iterate the array and create appropriate clocks with correct types and rates.

A device driver gets a clock with clk_get (which stores a refcount). Activate it with clk_enable/prepare, then the device is clocked and can be used. clk_get will get from the device tree the first clock that is referred by the device.

Most ARM SoCs now use the common clock framework, and it’s also finding its way into MIPS and x86.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s