@@ -1,42 +1,37 @@
Introduction:
=============
-This documentation entry describes the Common Clock Framework [CCF]
-port from Linux kernel (v5.1.12) to U-Boot.
+This documentation entry describes the Common Clock Framework [CCF] port from
+Linux kernel (v5.1.12) to U-Boot.
-This code is supposed to bring CCF to IMX based devices (imx6q, imx7
-imx8). Moreover, it also provides some common clock code, which would
-allow easy porting of CCF Linux code to other platforms.
+This code is supposed to bring CCF to IMX based devices (imx6q, imx7 imx8).
+Moreover, it also provides some common clock code, which would allow easy
+porting of CCF Linux code to other platforms.
Design decisions:
=================
-* U-Boot's driver model [DM] for clk differs from Linux CCF. The most
- notably difference is the lack of support for hierarchical clocks and
- "clock as a manager driver" (single clock DTS node acts as a starting
- point for all other clocks).
+* U-Boot's driver model [DM] for clk differs from Linux CCF. The most notably
+ difference is the lack of support for hierarchical clocks and "clock as a
+ manager driver" (single clock DTS node acts as a starting point for all other
+ clocks).
-* The clk_get_rate() caches the previously read data if CLK_GET_RATE_NOCACHE
- is not set (no need for recursive access).
+* The clk_get_rate() caches the previously read data if CLK_GET_RATE_NOCACHE is
+ not set (no need for recursive access).
-* On purpose the "manager" clk driver (clk-imx6q.c) is not using large
- table to store pointers to clocks - e.g. clk[IMX6QDL_CLK_USDHC2_SEL] = ....
- Instead we use udevice's linked list for the same class (UCLASS_CLK).
+* On purpose the "manager" clk driver (clk-imx6q.c) is not using large table to
+ store pointers to clocks - e.g. clk[IMX6QDL_CLK_USDHC2_SEL] = .... Instead we
+ use udevice's linked list for the same class (UCLASS_CLK).
Rationale:
----------
- When porting the code as is from Linux, one would need ~1KiB of RAM to
- store it. This is way too much if we do plan to use this driver in SPL.
+ When porting the code as is from Linux, one would need ~1KiB of RAM to store
+ it. This is way too much if we do plan to use this driver in SPL.
* The "central" structure of this patch series is struct udevice and its
uclass_priv field contains the struct clk pointer (to the originally created
one).
-* Up till now U-Boot's driver model (DM) CLK operates on udevice (main
- access to clock is by udevice ops)
- In the CCF the access to struct clk (embodying pointer to *dev) is
- possible via dev_get_clk_ptr() (it is a wrapper on dev_get_uclass_priv()).
-
* To keep things simple the struct udevice's uclass_priv pointer is used to
store back pointer to corresponding struct clk. However, it is possible to
modify clk-uclass.c file and add there struct uc_clk_priv, which would have
@@ -45,13 +40,17 @@ Design decisions:
setting .per_device_auto_alloc_size = sizeof(struct uc_clk_priv)) the
uclass_priv stores the pointer to struct clk.
+* Non-CCF clocks do not have a pointer to a clock in clk->dev->priv. In the case
+ of composite clocks, clk->dev->priv may not match clk. Drivers should always
+ use the struct clk which is passed to them, and not clk->dev->priv.
+
* It is advised to add common clock code (like already added rate and flags) to
the struct clk, which is a top level description of the clock.
* U-Boot's driver model already provides the facility to automatically allocate
- (via private_alloc_size) device private data (accessible via dev->priv).
- It may look appealing to use this feature to allocate private structures for
- CCF clk devices e.g. divider (struct clk_divider *divider) for IMX6Q clock.
+ (via private_alloc_size) device private data (accessible via dev->priv). It
+ may look appealing to use this feature to allocate private structures for CCF
+ clk devices e.g. divider (struct clk_divider *divider) for IMX6Q clock.
The above feature had not been used for following reasons:
- The original CCF Linux kernel driver is the "manager" for clocks - it
@@ -64,21 +63,23 @@ Design decisions:
* I've added the clk_get_parent(), which reads parent's dev->uclass_priv to
provide parent's struct clk pointer. This seems the easiest way to get
- child/parent relationship for struct clk in U-Boot's udevice based clocks.
+ child/parent relationship for struct clk in U-Boot's udevice based clocks. In
+ the future arbitrary parents may be supported by adding a get_parent function
+ to clk_ops.
* Linux's CCF 'struct clk_core' corresponds to U-Boot's udevice in 'struct clk'.
Clock IP block agnostic flags from 'struct clk_core' (e.g. NOCACHE) have been
- moved from this struct one level up to 'struct clk'.
+ moved from this struct one level up to 'struct clk'. Many flags are
+ unimplemented at the moment.
* For tests the new ./test/dm/clk_ccf.c and ./drivers/clk/clk_sandbox_ccf.c
files have been introduced. The latter setups the CCF clock structure for
- sandbox by reusing, if possible, generic clock primitives - like divier
- and mux. The former file provides code to tests this setup.
+ sandbox by reusing, if possible, generic clock primitives - like divier and
+ mux. The former file provides code to tests this setup.
For sandbox new CONFIG_SANDBOX_CLK_CCF Kconfig define has been introduced.
- All new primitives added for new architectures must have corresponding test
- in the two aforementioned files.
-
+ All new primitives added for new architectures must have corresponding test in
+ the two aforementioned files.
Testing (sandbox):
==================
@@ -147,6 +147,13 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
goto err;
}
+ if (composite->mux)
+ composite->mux->dev = clk->dev;
+ if (composite->rate)
+ composite->rate->dev = clk->dev;
+ if (composite->gate)
+ composite->gate->dev = clk->dev;
+
return clk;
err:
@@ -72,8 +72,7 @@ unsigned long divider_recalc_rate(struct clk *hw, unsigned long parent_rate,
static ulong clk_divider_recalc_rate(struct clk *clk)
{
- struct clk_divider *divider = to_clk_divider(clk_dev_binded(clk) ?
- dev_get_clk_ptr(clk->dev) : clk);
+ struct clk_divider *divider = to_clk_divider(clk);
unsigned long parent_rate = clk_get_parent_rate(clk);
unsigned int val;
@@ -152,8 +151,7 @@ int divider_get_val(unsigned long rate, unsigned long parent_rate,
static ulong clk_divider_set_rate(struct clk *clk, unsigned long rate)
{
- struct clk_divider *divider = to_clk_divider(clk_dev_binded(clk) ?
- dev_get_clk_ptr(clk->dev) : clk);
+ struct clk_divider *divider = to_clk_divider(clk);
unsigned long parent_rate = clk_get_parent_rate(clk);
int value;
u32 val;
@@ -20,8 +20,7 @@
static ulong clk_factor_recalc_rate(struct clk *clk)
{
- struct clk_fixed_factor *fix =
- to_clk_fixed_factor(dev_get_clk_ptr(clk->dev));
+ struct clk_fixed_factor *fix = to_clk_fixed_factor(clk);
unsigned long parent_rate = clk_get_parent_rate(clk);
unsigned long long int rate;
@@ -45,8 +45,7 @@
*/
static void clk_gate_endisable(struct clk *clk, int enable)
{
- struct clk_gate *gate = to_clk_gate(clk_dev_binded(clk) ?
- dev_get_clk_ptr(clk->dev) : clk);
+ struct clk_gate *gate = to_clk_gate(clk);
int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
u32 reg;
@@ -88,8 +87,7 @@ static int clk_gate_disable(struct clk *clk)
int clk_gate_is_enabled(struct clk *clk)
{
- struct clk_gate *gate = to_clk_gate(clk_dev_binded(clk) ?
- dev_get_clk_ptr(clk->dev) : clk);
+ struct clk_gate *gate = to_clk_gate(clk);
u32 reg;
#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
@@ -37,8 +37,7 @@
int clk_mux_val_to_index(struct clk *clk, u32 *table, unsigned int flags,
unsigned int val)
{
- struct clk_mux *mux = to_clk_mux(clk_dev_binded(clk) ?
- dev_get_clk_ptr(clk->dev) : clk);
+ struct clk_mux *mux = to_clk_mux(clk);
int num_parents = mux->num_parents;
if (table) {
@@ -81,8 +80,7 @@ unsigned int clk_mux_index_to_val(u32 *table, unsigned int flags, u8 index)
u8 clk_mux_get_parent(struct clk *clk)
{
- struct clk_mux *mux = to_clk_mux(clk_dev_binded(clk) ?
- dev_get_clk_ptr(clk->dev) : clk);
+ struct clk_mux *mux = to_clk_mux(clk);
u32 val;
#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
@@ -99,8 +97,7 @@ u8 clk_mux_get_parent(struct clk *clk)
static int clk_fetch_parent_index(struct clk *clk,
struct clk *parent)
{
- struct clk_mux *mux = to_clk_mux(clk_dev_binded(clk) ?
- dev_get_clk_ptr(clk->dev) : clk);
+ struct clk_mux *mux = to_clk_mux(clk);
int i;
@@ -117,8 +114,7 @@ static int clk_fetch_parent_index(struct clk *clk,
static int clk_mux_set_parent(struct clk *clk, struct clk *parent)
{
- struct clk_mux *mux = to_clk_mux(clk_dev_binded(clk) ?
- dev_get_clk_ptr(clk->dev) : clk);
+ struct clk_mux *mux = to_clk_mux(clk);
int index;
u32 val;
u32 reg;
@@ -39,7 +39,7 @@ struct clk_gate2 {
static int clk_gate2_enable(struct clk *clk)
{
- struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev));
+ struct clk_gate2 *gate = to_clk_gate2(clk);
u32 reg;
reg = readl(gate->reg);
@@ -52,7 +52,7 @@ static int clk_gate2_enable(struct clk *clk)
static int clk_gate2_disable(struct clk *clk)
{
- struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev));
+ struct clk_gate2 *gate = to_clk_gate2(clk);
u32 reg;
reg = readl(gate->reg);