mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-25 10:43:41 +07:00
3fa2252b7a
Some of Qualcomm's clocks can change their parent and rate at the same time with a single register write. Add support for this hardware to the common clock framework by adding a new set_rate_and_parent() op. When the clock framework determines that both the parent and the rate are going to change during clk_set_rate() it will call the .set_rate_and_parent() op if available and fall back to calling .set_parent() followed by .set_rate() otherwise. Reviewed-by: James Hogan <james.hogan@imgtec.com> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> Signed-off-by: Mike Turquette <mturquette@linaro.org>
258 lines
9.6 KiB
Plaintext
258 lines
9.6 KiB
Plaintext
The Common Clk Framework
|
|
Mike Turquette <mturquette@ti.com>
|
|
|
|
This document endeavours to explain the common clk framework details,
|
|
and how to port a platform over to this framework. It is not yet a
|
|
detailed explanation of the clock api in include/linux/clk.h, but
|
|
perhaps someday it will include that information.
|
|
|
|
Part 1 - introduction and interface split
|
|
|
|
The common clk framework is an interface to control the clock nodes
|
|
available on various devices today. This may come in the form of clock
|
|
gating, rate adjustment, muxing or other operations. This framework is
|
|
enabled with the CONFIG_COMMON_CLK option.
|
|
|
|
The interface itself is divided into two halves, each shielded from the
|
|
details of its counterpart. First is the common definition of struct
|
|
clk which unifies the framework-level accounting and infrastructure that
|
|
has traditionally been duplicated across a variety of platforms. Second
|
|
is a common implementation of the clk.h api, defined in
|
|
drivers/clk/clk.c. Finally there is struct clk_ops, whose operations
|
|
are invoked by the clk api implementation.
|
|
|
|
The second half of the interface is comprised of the hardware-specific
|
|
callbacks registered with struct clk_ops and the corresponding
|
|
hardware-specific structures needed to model a particular clock. For
|
|
the remainder of this document any reference to a callback in struct
|
|
clk_ops, such as .enable or .set_rate, implies the hardware-specific
|
|
implementation of that code. Likewise, references to struct clk_foo
|
|
serve as a convenient shorthand for the implementation of the
|
|
hardware-specific bits for the hypothetical "foo" hardware.
|
|
|
|
Tying the two halves of this interface together is struct clk_hw, which
|
|
is defined in struct clk_foo and pointed to within struct clk. This
|
|
allows for easy navigation between the two discrete halves of the common
|
|
clock interface.
|
|
|
|
Part 2 - common data structures and api
|
|
|
|
Below is the common struct clk definition from
|
|
include/linux/clk-private.h, modified for brevity:
|
|
|
|
struct clk {
|
|
const char *name;
|
|
const struct clk_ops *ops;
|
|
struct clk_hw *hw;
|
|
char **parent_names;
|
|
struct clk **parents;
|
|
struct clk *parent;
|
|
struct hlist_head children;
|
|
struct hlist_node child_node;
|
|
...
|
|
};
|
|
|
|
The members above make up the core of the clk tree topology. The clk
|
|
api itself defines several driver-facing functions which operate on
|
|
struct clk. That api is documented in include/linux/clk.h.
|
|
|
|
Platforms and devices utilizing the common struct clk use the struct
|
|
clk_ops pointer in struct clk to perform the hardware-specific parts of
|
|
the operations defined in clk.h:
|
|
|
|
struct clk_ops {
|
|
int (*prepare)(struct clk_hw *hw);
|
|
void (*unprepare)(struct clk_hw *hw);
|
|
int (*enable)(struct clk_hw *hw);
|
|
void (*disable)(struct clk_hw *hw);
|
|
int (*is_enabled)(struct clk_hw *hw);
|
|
unsigned long (*recalc_rate)(struct clk_hw *hw,
|
|
unsigned long parent_rate);
|
|
long (*round_rate)(struct clk_hw *hw, unsigned long,
|
|
unsigned long *);
|
|
long (*determine_rate)(struct clk_hw *hw,
|
|
unsigned long rate,
|
|
unsigned long *best_parent_rate,
|
|
struct clk **best_parent_clk);
|
|
int (*set_parent)(struct clk_hw *hw, u8 index);
|
|
u8 (*get_parent)(struct clk_hw *hw);
|
|
int (*set_rate)(struct clk_hw *hw, unsigned long);
|
|
int (*set_rate_and_parent)(struct clk_hw *hw,
|
|
unsigned long rate,
|
|
unsigned long parent_rate, u8 index);
|
|
unsigned long (*recalc_accuracy)(struct clk_hw *hw,
|
|
unsigned long parent_accuracy);
|
|
void (*init)(struct clk_hw *hw);
|
|
};
|
|
|
|
Part 3 - hardware clk implementations
|
|
|
|
The strength of the common struct clk comes from its .ops and .hw pointers
|
|
which abstract the details of struct clk from the hardware-specific bits, and
|
|
vice versa. To illustrate consider the simple gateable clk implementation in
|
|
drivers/clk/clk-gate.c:
|
|
|
|
struct clk_gate {
|
|
struct clk_hw hw;
|
|
void __iomem *reg;
|
|
u8 bit_idx;
|
|
...
|
|
};
|
|
|
|
struct clk_gate contains struct clk_hw hw as well as hardware-specific
|
|
knowledge about which register and bit controls this clk's gating.
|
|
Nothing about clock topology or accounting, such as enable_count or
|
|
notifier_count, is needed here. That is all handled by the common
|
|
framework code and struct clk.
|
|
|
|
Let's walk through enabling this clk from driver code:
|
|
|
|
struct clk *clk;
|
|
clk = clk_get(NULL, "my_gateable_clk");
|
|
|
|
clk_prepare(clk);
|
|
clk_enable(clk);
|
|
|
|
The call graph for clk_enable is very simple:
|
|
|
|
clk_enable(clk);
|
|
clk->ops->enable(clk->hw);
|
|
[resolves to...]
|
|
clk_gate_enable(hw);
|
|
[resolves struct clk gate with to_clk_gate(hw)]
|
|
clk_gate_set_bit(gate);
|
|
|
|
And the definition of clk_gate_set_bit:
|
|
|
|
static void clk_gate_set_bit(struct clk_gate *gate)
|
|
{
|
|
u32 reg;
|
|
|
|
reg = __raw_readl(gate->reg);
|
|
reg |= BIT(gate->bit_idx);
|
|
writel(reg, gate->reg);
|
|
}
|
|
|
|
Note that to_clk_gate is defined as:
|
|
|
|
#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, clk)
|
|
|
|
This pattern of abstraction is used for every clock hardware
|
|
representation.
|
|
|
|
Part 4 - supporting your own clk hardware
|
|
|
|
When implementing support for a new type of clock it only necessary to
|
|
include the following header:
|
|
|
|
#include <linux/clk-provider.h>
|
|
|
|
include/linux/clk.h is included within that header and clk-private.h
|
|
must never be included from the code which implements the operations for
|
|
a clock. More on that below in Part 5.
|
|
|
|
To construct a clk hardware structure for your platform you must define
|
|
the following:
|
|
|
|
struct clk_foo {
|
|
struct clk_hw hw;
|
|
... hardware specific data goes here ...
|
|
};
|
|
|
|
To take advantage of your data you'll need to support valid operations
|
|
for your clk:
|
|
|
|
struct clk_ops clk_foo_ops {
|
|
.enable = &clk_foo_enable;
|
|
.disable = &clk_foo_disable;
|
|
};
|
|
|
|
Implement the above functions using container_of:
|
|
|
|
#define to_clk_foo(_hw) container_of(_hw, struct clk_foo, hw)
|
|
|
|
int clk_foo_enable(struct clk_hw *hw)
|
|
{
|
|
struct clk_foo *foo;
|
|
|
|
foo = to_clk_foo(hw);
|
|
|
|
... perform magic on foo ...
|
|
|
|
return 0;
|
|
};
|
|
|
|
Below is a matrix detailing which clk_ops are mandatory based upon the
|
|
hardware capabilities of that clock. A cell marked as "y" means
|
|
mandatory, a cell marked as "n" implies that either including that
|
|
callback is invalid or otherwise unnecessary. Empty cells are either
|
|
optional or must be evaluated on a case-by-case basis.
|
|
|
|
clock hardware characteristics
|
|
-----------------------------------------------------------
|
|
| gate | change rate | single parent | multiplexer | root |
|
|
|------|-------------|---------------|-------------|------|
|
|
.prepare | | | | | |
|
|
.unprepare | | | | | |
|
|
| | | | | |
|
|
.enable | y | | | | |
|
|
.disable | y | | | | |
|
|
.is_enabled | y | | | | |
|
|
| | | | | |
|
|
.recalc_rate | | y | | | |
|
|
.round_rate | | y [1] | | | |
|
|
.determine_rate | | y [1] | | | |
|
|
.set_rate | | y | | | |
|
|
| | | | | |
|
|
.set_parent | | | n | y | n |
|
|
.get_parent | | | n | y | n |
|
|
| | | | | |
|
|
.recalc_accuracy| | | | | |
|
|
| | | | | |
|
|
.init | | | | | |
|
|
-----------------------------------------------------------
|
|
[1] either one of round_rate or determine_rate is required.
|
|
|
|
Finally, register your clock at run-time with a hardware-specific
|
|
registration function. This function simply populates struct clk_foo's
|
|
data and then passes the common struct clk parameters to the framework
|
|
with a call to:
|
|
|
|
clk_register(...)
|
|
|
|
See the basic clock types in drivers/clk/clk-*.c for examples.
|
|
|
|
Part 5 - static initialization of clock data
|
|
|
|
For platforms with many clocks (often numbering into the hundreds) it
|
|
may be desirable to statically initialize some clock data. This
|
|
presents a problem since the definition of struct clk should be hidden
|
|
from everyone except for the clock core in drivers/clk/clk.c.
|
|
|
|
To get around this problem struct clk's definition is exposed in
|
|
include/linux/clk-private.h along with some macros for more easily
|
|
initializing instances of the basic clock types. These clocks must
|
|
still be initialized with the common clock framework via a call to
|
|
__clk_init.
|
|
|
|
clk-private.h must NEVER be included by code which implements struct
|
|
clk_ops callbacks, nor must it be included by any logic which pokes
|
|
around inside of struct clk at run-time. To do so is a layering
|
|
violation.
|
|
|
|
To better enforce this policy, always follow this simple rule: any
|
|
statically initialized clock data MUST be defined in a separate file
|
|
from the logic that implements its ops. Basically separate the logic
|
|
from the data and all is well.
|
|
|
|
Part 6 - Disabling clock gating of unused clocks
|
|
|
|
Sometimes during development it can be useful to be able to bypass the
|
|
default disabling of unused clocks. For example, if drivers aren't enabling
|
|
clocks properly but rely on them being on from the bootloader, bypassing
|
|
the disabling means that the driver will remain functional while the issues
|
|
are sorted out.
|
|
|
|
To bypass this disabling, include "clk_ignore_unused" in the bootargs to the
|
|
kernel.
|