From 2457612d6d374bd67d3317f11cf24fc0bba02274 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 31 Jul 2019 07:01:23 +0000 Subject: [PATCH 01/16] clk: introduce clk_dev_binded When support Clock Common Framework, U-Boot use dev for clk tree information, there is no clk->parent. When support composite clk, it contains mux/gate/divider, but the mux/gate/divider is not binded with device. So we could not use dev_get_uclass_priv to get the correct clk_mux/gate/divider. So add clk_dev_binded to let choose the correct method. Signed-off-by: Peng Fan --- drivers/clk/clk.c | 8 ++++++++ include/clk.h | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 7d748c9fc73..39b3087067a 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -55,3 +55,11 @@ const char *clk_hw_get_name(const struct clk *hw) { return hw->dev->name; } + +bool clk_dev_binded(struct clk *clk) +{ + if (clk->dev && (clk->dev->flags & DM_FLAG_BOUND)) + return true; + + return false; +} diff --git a/include/clk.h b/include/clk.h index f8f56d9cf01..2ebc905e04b 100644 --- a/include/clk.h +++ b/include/clk.h @@ -356,4 +356,13 @@ static inline bool clk_valid(struct clk *clk) * @return zero on success, or -ENOENT on error */ int clk_get_by_id(ulong id, struct clk **clkp); + +/** + * clk_dev_binded() - Check whether the clk has a device binded + * + * @clk A pointer to the clk + * + * @return true on binded, or false on no + */ +bool clk_dev_binded(struct clk *clk); #endif From 5b27ff89866e36764c5e3e997bf505956ce49527 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 31 Jul 2019 07:01:26 +0000 Subject: [PATCH 02/16] clk: use clk_dev_binded Preparing to support composite clk. Signed-off-by: Peng Fan --- drivers/clk/clk-divider.c | 4 ++-- drivers/clk/clk-mux.c | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 6921c76a48f..2ed9ed6ab8c 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -69,8 +69,8 @@ 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(dev_get_clk_ptr(clk->dev)); + struct clk_divider *divider = to_clk_divider(clk_dev_binded(clk) ? + dev_get_clk_ptr(clk->dev) : clk); unsigned long parent_rate = clk_get_parent_rate(clk); unsigned int val; diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 3c075aa09ec..81d1e7ebeeb 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -35,7 +35,8 @@ 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); + struct clk_mux *mux = to_clk_mux(clk_dev_binded(clk) ? + dev_get_clk_ptr(clk->dev) : clk); int num_parents = mux->num_parents; if (table) { @@ -61,7 +62,8 @@ int clk_mux_val_to_index(struct clk *clk, u32 *table, unsigned int flags, static u8 clk_mux_get_parent(struct clk *clk) { - struct clk_mux *mux = to_clk_mux(clk); + struct clk_mux *mux = to_clk_mux(clk_dev_binded(clk) ? + dev_get_clk_ptr(clk->dev) : clk); u32 val; #if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF) From 4b044082c19eff716cba9e816fbb1225caaeb8a7 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 31 Jul 2019 07:01:28 +0000 Subject: [PATCH 03/16] clk: mux: add set parent support Add set parent support for clk mux Signed-off-by: Peng Fan --- drivers/clk/clk-mux.c | 70 ++++++++++++++++++++++++++++++++++-- include/linux/clk-provider.h | 2 ++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 81d1e7ebeeb..5acc0b8cbd0 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -60,7 +60,24 @@ int clk_mux_val_to_index(struct clk *clk, u32 *table, unsigned int flags, return val; } -static u8 clk_mux_get_parent(struct clk *clk) +unsigned int clk_mux_index_to_val(u32 *table, unsigned int flags, u8 index) +{ + unsigned int val = index; + + if (table) { + val = table[index]; + } else { + if (flags & CLK_MUX_INDEX_BIT) + val = 1 << index; + + if (flags & CLK_MUX_INDEX_ONE) + val++; + } + + return val; +} + +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); @@ -77,8 +94,57 @@ static u8 clk_mux_get_parent(struct clk *clk) return clk_mux_val_to_index(clk, mux->table, mux->flags, val); } +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); + + int i; + + if (!parent) + return -EINVAL; + + for (i = 0; i < mux->num_parents; i++) { + if (!strcmp(parent->dev->name, mux->parent_names[i])) + return i; + } + + return -EINVAL; +} + +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); + int index; + u32 val; + u32 reg; + + index = clk_fetch_parent_index(clk, parent); + if (index < 0) { + printf("Could not fetch index\n"); + return index; + } + + val = clk_mux_index_to_val(mux->table, mux->flags, index); + + if (mux->flags & CLK_MUX_HIWORD_MASK) { + reg = mux->mask << (mux->shift + 16); + } else { + reg = readl(mux->reg); + reg &= ~(mux->mask << mux->shift); + } + val = val << mux->shift; + reg |= val; + writel(reg, mux->reg); + + return 0; +} + const struct clk_ops clk_mux_ops = { - .get_rate = clk_generic_get_rate, + .get_rate = clk_generic_get_rate, + .set_parent = clk_mux_set_parent, }; struct clk *clk_hw_register_mux_table(struct device *dev, const char *name, diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 43a25e9c6a8..7e44045c16d 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -66,6 +66,8 @@ struct clk_mux { }; #define to_clk_mux(_clk) container_of(_clk, struct clk_mux, clk) +extern const struct clk_ops clk_mux_ops; +u8 clk_mux_get_parent(struct clk *clk); struct clk_div_table { unsigned int val; From 1b0d09cddb6d6b4d30f561dc8e4fb2ce904a5b58 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 31 Jul 2019 07:01:31 +0000 Subject: [PATCH 04/16] clk: export mux/divider ops Export mux/divider ops and divider_recalc_rate for composite usage Signed-off-by: Peng Fan --- include/linux/clk-provider.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 7e44045c16d..6d62f862d2a 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -96,6 +96,11 @@ struct clk_divider { #define CLK_DIVIDER_ROUND_CLOSEST BIT(4) #define CLK_DIVIDER_READ_ONLY BIT(5) #define CLK_DIVIDER_MAX_AT_ZERO BIT(6) +extern const struct clk_ops clk_divider_ops; +unsigned long divider_recalc_rate(struct clk *hw, unsigned long parent_rate, + unsigned int val, + const struct clk_div_table *table, + unsigned long flags, unsigned long width); struct clk_fixed_factor { struct clk clk; From 1c643303187c595eb6782329529d2aec3731e473 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 31 Jul 2019 07:01:34 +0000 Subject: [PATCH 05/16] clk: add clk-gate support Import clk-gate support from Linux Kernel 5.1-rc5 Signed-off-by: Peng Fan --- drivers/clk/Makefile | 2 +- drivers/clk/clk-gate.c | 148 +++++++++++++++++++++++++++++++++++ include/linux/clk-provider.h | 18 +++++ 3 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/clk-gate.c diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index b7fec605c6c..39154eca59c 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -7,7 +7,7 @@ obj-$(CONFIG_$(SPL_TPL_)CLK) += clk-uclass.o obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_rate.o obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_factor.o -obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk.o clk-divider.o clk-mux.o +obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk.o clk-divider.o clk-mux.o clk-gate.o obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk-fixed-factor.o obj-y += analogbits/ diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c new file mode 100644 index 00000000000..a3a1fdd3b2f --- /dev/null +++ b/drivers/clk/clk-gate.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2010-2011 Canonical Ltd + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd + * Copyright 2019 NXP + * + * Gated clock implementation + */ + +#include +#include +#include +#include +#include +#include +#include +#include "clk.h" + +#define UBOOT_DM_CLK_GATE "clk_gate" + +/** + * DOC: basic gatable clock which can gate and ungate it's output + * + * Traits of this clock: + * prepare - clk_(un)prepare only ensures parent is (un)prepared + * enable - clk_enable and clk_disable are functional & control gating + * rate - inherits rate from parent. No clk_set_rate support + * parent - fixed parent. No clk_set_parent support + */ + +/* + * It works on following logic: + * + * For enabling clock, enable = 1 + * set2dis = 1 -> clear bit -> set = 0 + * set2dis = 0 -> set bit -> set = 1 + * + * For disabling clock, enable = 0 + * set2dis = 1 -> set bit -> set = 1 + * set2dis = 0 -> clear bit -> set = 0 + * + * So, result is always: enable xor set2dis. + */ +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); + int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0; + u32 reg; + + set ^= enable; + + if (gate->flags & CLK_GATE_HIWORD_MASK) { + reg = BIT(gate->bit_idx + 16); + if (set) + reg |= BIT(gate->bit_idx); + } else { + reg = readl(gate->reg); + + if (set) + reg |= BIT(gate->bit_idx); + else + reg &= ~BIT(gate->bit_idx); + } + + writel(reg, gate->reg); +} + +static int clk_gate_enable(struct clk *clk) +{ + clk_gate_endisable(clk, 1); + + return 0; +} + +static int clk_gate_disable(struct clk *clk) +{ + clk_gate_endisable(clk, 0); + + return 0; +} + +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); + u32 reg; + + reg = readl(gate->reg); + + /* if a set bit disables this clk, flip it before masking */ + if (gate->flags & CLK_GATE_SET_TO_DISABLE) + reg ^= BIT(gate->bit_idx); + + reg &= BIT(gate->bit_idx); + + return reg ? 1 : 0; +} + +const struct clk_ops clk_gate_ops = { + .enable = clk_gate_enable, + .disable = clk_gate_disable, + .get_rate = clk_generic_get_rate, +}; + +struct clk *clk_register_gate(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 bit_idx, + u8 clk_gate_flags, spinlock_t *lock) +{ + struct clk_gate *gate; + struct clk *clk; + int ret; + + if (clk_gate_flags & CLK_GATE_HIWORD_MASK) { + if (bit_idx > 15) { + pr_err("gate bit exceeds LOWORD field\n"); + return ERR_PTR(-EINVAL); + } + } + + /* allocate the gate */ + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + /* struct clk_gate assignments */ + gate->reg = reg; + gate->bit_idx = bit_idx; + gate->flags = clk_gate_flags; + + clk = &gate->clk; + + ret = clk_register(clk, UBOOT_DM_CLK_GATE, name, parent_name); + if (ret) { + kfree(gate); + return ERR_PTR(ret); + } + + return clk; +} + +U_BOOT_DRIVER(clk_gate) = { + .name = UBOOT_DM_CLK_GATE, + .id = UCLASS_CLK, + .ops = &clk_gate_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 6d62f862d2a..8b04ecd7a5c 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -69,6 +69,24 @@ struct clk_mux { extern const struct clk_ops clk_mux_ops; u8 clk_mux_get_parent(struct clk *clk); +struct clk_gate { + struct clk clk; + void __iomem *reg; + u8 bit_idx; + u8 flags; +}; + +#define to_clk_gate(_clk) container_of(_clk, struct clk_gate, clk) + +#define CLK_GATE_SET_TO_DISABLE BIT(0) +#define CLK_GATE_HIWORD_MASK BIT(1) + +extern const struct clk_ops clk_gate_ops; +struct clk *clk_register_gate(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 bit_idx, + u8 clk_gate_flags, spinlock_t *lock); + struct clk_div_table { unsigned int val; unsigned int div; From fe69b030deed82cb0364e8dfb1b7d1ea9dbec063 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 31 Jul 2019 07:01:37 +0000 Subject: [PATCH 06/16] clk: divider set rate supporrt Signed-off-by: Peng Fan --- drivers/clk/clk-divider.c | 88 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 2ed9ed6ab8c..822e09b0844 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include "clk.h" @@ -86,8 +87,95 @@ static ulong clk_divider_recalc_rate(struct clk *clk) divider->flags, divider->width); } +static bool _is_valid_table_div(const struct clk_div_table *table, + unsigned int div) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div == div) + return true; + return false; +} + +static bool _is_valid_div(const struct clk_div_table *table, unsigned int div, + unsigned long flags) +{ + if (flags & CLK_DIVIDER_POWER_OF_TWO) + return is_power_of_2(div); + if (table) + return _is_valid_table_div(table, div); + return true; +} + +static unsigned int _get_table_val(const struct clk_div_table *table, + unsigned int div) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div == div) + return clkt->val; + return 0; +} + +static unsigned int _get_val(const struct clk_div_table *table, + unsigned int div, unsigned long flags, u8 width) +{ + if (flags & CLK_DIVIDER_ONE_BASED) + return div; + if (flags & CLK_DIVIDER_POWER_OF_TWO) + return __ffs(div); + if (flags & CLK_DIVIDER_MAX_AT_ZERO) + return (div == clk_div_mask(width) + 1) ? 0 : div; + if (table) + return _get_table_val(table, div); + return div - 1; +} +int divider_get_val(unsigned long rate, unsigned long parent_rate, + const struct clk_div_table *table, u8 width, + unsigned long flags) +{ + unsigned int div, value; + + div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); + + if (!_is_valid_div(table, div, flags)) + return -EINVAL; + + value = _get_val(table, div, flags, width); + + return min_t(unsigned int, value, clk_div_mask(width)); +} + +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); + unsigned long parent_rate = clk_get_parent_rate(clk); + int value; + u32 val; + + value = divider_get_val(rate, parent_rate, divider->table, + divider->width, divider->flags); + if (value < 0) + return value; + + if (divider->flags & CLK_DIVIDER_HIWORD_MASK) { + val = clk_div_mask(divider->width) << (divider->shift + 16); + } else { + val = readl(divider->reg); + val &= ~(clk_div_mask(divider->width) << divider->shift); + } + val |= (u32)value << divider->shift; + writel(val, divider->reg); + + return clk_get_rate(clk); +} + const struct clk_ops clk_divider_ops = { .get_rate = clk_divider_recalc_rate, + .set_rate = clk_divider_set_rate, }; static struct clk *_register_divider(struct device *dev, const char *name, From 4f305bf1b688ecb9508a2d74fa76ff34e90bc21f Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 31 Jul 2019 07:01:39 +0000 Subject: [PATCH 07/16] clk: fixed_rate: export clk_fixed_rate Export the structure for others to use. Signed-off-by: Peng Fan --- drivers/clk/clk_fixed_rate.c | 8 +------- include/linux/clk-provider.h | 7 +++++++ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/clk/clk_fixed_rate.c b/drivers/clk/clk_fixed_rate.c index 1fdf8c4e540..08cce0d79b7 100644 --- a/drivers/clk/clk_fixed_rate.c +++ b/drivers/clk/clk_fixed_rate.c @@ -6,13 +6,7 @@ #include #include #include - -struct clk_fixed_rate { - struct clk clk; - unsigned long fixed_rate; -}; - -#define to_clk_fixed_rate(dev) ((struct clk_fixed_rate *)dev_get_platdata(dev)) +#include static ulong clk_fixed_rate_get_rate(struct clk *clk) { diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 8b04ecd7a5c..f42df9b90fe 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -129,6 +129,13 @@ struct clk_fixed_factor { #define to_clk_fixed_factor(_clk) container_of(_clk, struct clk_fixed_factor,\ clk) +struct clk_fixed_rate { + struct clk clk; + unsigned long fixed_rate; +}; + +#define to_clk_fixed_rate(dev) ((struct clk_fixed_rate *)dev_get_platdata(dev)) + int clk_register(struct clk *clk, const char *drv_name, const char *name, const char *parent_name); From b6c56d90b879a8f94c6a8d470e7d207f9b507401 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 31 Jul 2019 07:01:42 +0000 Subject: [PATCH 08/16] clk: imx: import clk heplers Import some clk helpers from Linux Kernel for i.MX8MM usage Signed-off-by: Peng Fan --- drivers/clk/imx/clk.h | 81 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index e6d51830e88..1d480d8722b 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -36,6 +36,23 @@ static inline struct clk *imx_clk_gate2(const char *name, const char *parent, shift, 0x3, 0); } +static inline struct clk *imx_clk_gate4(const char *name, const char *parent, + void __iomem *reg, u8 shift) +{ + return clk_register_gate2(NULL, name, parent, + CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, + reg, shift, 0x3, 0); +} + +static inline struct clk *imx_clk_gate4_flags(const char *name, + const char *parent, void __iomem *reg, u8 shift, + unsigned long flags) +{ + return clk_register_gate2(NULL, name, parent, + flags | CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, + reg, shift, 0x3, 0); +} + static inline struct clk *imx_clk_fixed_factor(const char *name, const char *parent, unsigned int mult, unsigned int div) { @@ -50,6 +67,14 @@ static inline struct clk *imx_clk_divider(const char *name, const char *parent, reg, shift, width, 0); } +static inline struct clk *imx_clk_divider2(const char *name, const char *parent, + void __iomem *reg, u8 shift, u8 width) +{ + return clk_register_divider(NULL, name, parent, + CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, + reg, shift, width, 0); +} + struct clk *imx_clk_pfd(const char *name, const char *parent_name, void __iomem *reg, u8 idx); @@ -57,6 +82,16 @@ struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg, u8 shift, u8 width, const char * const *parents, int num_parents, void (*fixup)(u32 *val)); +static inline struct clk *imx_clk_mux_flags(const char *name, + void __iomem *reg, u8 shift, u8 width, + const char * const *parents, int num_parents, + unsigned long flags) +{ + return clk_register_mux(NULL, name, parents, num_parents, + flags | CLK_SET_RATE_NO_REPARENT, reg, shift, + width, 0); +} + static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg, u8 shift, u8 width, const char * const *parents, int num_parents) @@ -66,4 +101,50 @@ static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg, width, 0); } +static inline struct clk *imx_clk_mux2(const char *name, void __iomem *reg, + u8 shift, u8 width, const char * const *parents, + int num_parents) +{ + return clk_register_mux(NULL, name, parents, num_parents, + CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE, + reg, shift, width, 0); +} + +static inline struct clk *imx_clk_gate(const char *name, const char *parent, + void __iomem *reg, u8 shift) +{ + return clk_register_gate(NULL, name, parent, CLK_SET_RATE_PARENT, reg, + shift, 0, NULL); +} + +static inline struct clk *imx_clk_gate_flags(const char *name, const char *parent, + void __iomem *reg, u8 shift, unsigned long flags) +{ + return clk_register_gate(NULL, name, parent, flags | CLK_SET_RATE_PARENT, reg, + shift, 0, NULL); +} + +static inline struct clk *imx_clk_gate3(const char *name, const char *parent, + void __iomem *reg, u8 shift) +{ + return clk_register_gate(NULL, name, parent, + CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, + reg, shift, 0, NULL); +} + +struct clk *imx8m_clk_composite_flags(const char *name, + const char * const *parent_names, + int num_parents, void __iomem *reg, unsigned long flags); + +#define __imx8m_clk_composite(name, parent_names, reg, flags) \ + imx8m_clk_composite_flags(name, parent_names, \ + ARRAY_SIZE(parent_names), reg, \ + flags | CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE) + +#define imx8m_clk_composite(name, parent_names, reg) \ + __imx8m_clk_composite(name, parent_names, reg, 0) + +#define imx8m_clk_composite_critical(name, parent_names, reg) \ + __imx8m_clk_composite(name, parent_names, reg, CLK_IS_CRITICAL) + #endif /* __MACH_IMX_CLK_H */ From 4b91ec076d800b3ca535dfd99b79798094bd4e52 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 31 Jul 2019 07:01:45 +0000 Subject: [PATCH 09/16] clk: imx: gate2 add set rate Add set rate for imx clk-gate2 Signed-off-by: Peng Fan --- drivers/clk/imx/clk-gate2.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/clk/imx/clk-gate2.c b/drivers/clk/imx/clk-gate2.c index 571be320883..1b9db6e791f 100644 --- a/drivers/clk/imx/clk-gate2.c +++ b/drivers/clk/imx/clk-gate2.c @@ -60,7 +60,18 @@ static int clk_gate2_disable(struct clk *clk) return 0; } +static ulong clk_gate2_set_rate(struct clk *clk, ulong rate) +{ + struct clk *parent = clk_get_parent(clk); + + if (parent) + return clk_set_rate(parent, rate); + + return -ENODEV; +} + static const struct clk_ops clk_gate2_ops = { + .set_rate = clk_gate2_set_rate, .enable = clk_gate2_enable, .disable = clk_gate2_disable, .get_rate = clk_generic_get_rate, From 91944ef09d603f6aa41649b2a1fdd64fea9651f9 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 31 Jul 2019 07:01:49 +0000 Subject: [PATCH 10/16] dm: clk: ignore default settings when node not valid When the device not binded with a node, we need ignore the parents and rate settings. Cc: Simon Glass Cc: Jagan Teki Cc: Philipp Tomsich Cc: Neil Armstrong Cc: Andreas Dannenberg Signed-off-by: Peng Fan --- drivers/clk/clk-uclass.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 85dfe712f5a..cee4d912b0e 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -285,6 +285,9 @@ int clk_set_defaults(struct udevice *dev) { int ret; + if (!dev_of_valid(dev)) + return 0; + /* If this not in SPL and pre-reloc state, don't take any action. */ if (!(IS_ENABLED(CONFIG_SPL_BUILD) || (gd->flags & GD_FLG_RELOC))) return 0; From d669d1ae03977c70a4adb8a085cbfd701ae95b28 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 31 Jul 2019 07:01:52 +0000 Subject: [PATCH 11/16] clk-provider: include clk-uclass.h Because clk-provider use clk_ops, so let's include clk-uclass.h Signed-off-by: Peng Fan --- include/linux/clk-provider.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index f42df9b90fe..522e73e8517 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -8,6 +8,7 @@ */ #ifndef __LINUX_CLK_PROVIDER_H #define __LINUX_CLK_PROVIDER_H +#include static inline void clk_dm(ulong id, struct clk *clk) { From 00097635888f9104da7f7cceaf1858ec8987e86f Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 31 Jul 2019 07:01:54 +0000 Subject: [PATCH 12/16] clk: add composite clk support Import clk composite clk support from Linux Kernel 5.1-rc5 Signed-off-by: Peng Fan --- drivers/clk/Kconfig | 14 +++ drivers/clk/Makefile | 1 + drivers/clk/clk-composite.c | 160 +++++++++++++++++++++++++++++++++++ include/linux/clk-provider.h | 22 +++++ 4 files changed, 197 insertions(+) create mode 100644 drivers/clk/clk-composite.c diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 5e92446c18c..a3f0171b45f 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -53,6 +53,13 @@ config SPL_CLK_CCF Enable this option if you want to (re-)use the Linux kernel's Common Clock Framework [CCF] code in U-Boot's SPL. +config SPL_CLK_COMPOSITE_CCF + bool "SPL Common Clock Framework [CCF] composite clk support " + depends on SPL_CLK_CCF + help + Enable this option if you want to (re-)use the Linux kernel's Common + Clock Framework [CCF] composite code in U-Boot's SPL. + config CLK_CCF bool "Common Clock Framework [CCF] support " depends on CLK_IMX6Q || SANDBOX_CLK_CCF @@ -60,6 +67,13 @@ config CLK_CCF Enable this option if you want to (re-)use the Linux kernel's Common Clock Framework [CCF] code in U-Boot's clock driver. +config CLK_COMPOSITE_CCF + bool "Common Clock Framework [CCF] composite clk support " + depends on CLK_CCF + help + Enable this option if you want to (re-)use the Linux kernel's Common + Clock Framework [CCF] composite code in U-Boot's clock driver. + config CLK_STM32F bool "Enable clock driver support for STM32F family" depends on CLK && (STM32F7 || STM32F4) diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 39154eca59c..68aabe1ca99 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_rate.o obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_factor.o obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk.o clk-divider.o clk-mux.o clk-gate.o obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk-fixed-factor.o +obj-$(CONFIG_$(SPL_TPL_)CLK_COMPOSITE_CCF) += clk-composite.o obj-y += analogbits/ obj-y += imx/ diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c new file mode 100644 index 00000000000..a5626c33d1e --- /dev/null +++ b/drivers/clk/clk-composite.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2013 NVIDIA CORPORATION. All rights reserved. + * Copyright 2019 NXP + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +#define UBOOT_DM_CLK_COMPOSITE "clk_composite" + +static u8 clk_composite_get_parent(struct clk *clk) +{ + struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ? + (struct clk *)dev_get_clk_ptr(clk->dev) : clk); + struct clk *mux = composite->mux; + + return clk_mux_get_parent(mux); +} + +static int clk_composite_set_parent(struct clk *clk, struct clk *parent) +{ + struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ? + (struct clk *)dev_get_clk_ptr(clk->dev) : clk); + const struct clk_ops *mux_ops = composite->mux_ops; + struct clk *mux = composite->mux; + + return mux_ops->set_parent(mux, parent); +} + +static unsigned long clk_composite_recalc_rate(struct clk *clk) +{ + struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ? + (struct clk *)dev_get_clk_ptr(clk->dev) : clk); + const struct clk_ops *rate_ops = composite->rate_ops; + struct clk *rate = composite->rate; + + return rate_ops->get_rate(rate); +} + +static ulong clk_composite_set_rate(struct clk *clk, unsigned long rate) +{ + struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ? + (struct clk *)dev_get_clk_ptr(clk->dev) : clk); + const struct clk_ops *rate_ops = composite->rate_ops; + struct clk *clk_rate = composite->rate; + + return rate_ops->set_rate(clk_rate, rate); +} + +static int clk_composite_enable(struct clk *clk) +{ + struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ? + (struct clk *)dev_get_clk_ptr(clk->dev) : clk); + const struct clk_ops *gate_ops = composite->gate_ops; + struct clk *gate = composite->gate; + + return gate_ops->enable(gate); +} + +static int clk_composite_disable(struct clk *clk) +{ + struct clk_composite *composite = to_clk_composite(clk_dev_binded(clk) ? + (struct clk *)dev_get_clk_ptr(clk->dev) : clk); + const struct clk_ops *gate_ops = composite->gate_ops; + struct clk *gate = composite->gate; + + gate_ops->disable(gate); + + return 0; +} + +struct clk_ops clk_composite_ops = { + /* This will be set according to clk_register_composite */ +}; + +struct clk *clk_register_composite(struct device *dev, const char *name, + const char * const *parent_names, + int num_parents, struct clk *mux, + const struct clk_ops *mux_ops, + struct clk *rate, + const struct clk_ops *rate_ops, + struct clk *gate, + const struct clk_ops *gate_ops, + unsigned long flags) +{ + struct clk *clk; + struct clk_composite *composite; + int ret; + struct clk_ops *composite_ops = &clk_composite_ops; + + composite = kzalloc(sizeof(*composite), GFP_KERNEL); + if (!composite) + return ERR_PTR(-ENOMEM); + + if (mux && mux_ops) { + composite->mux = mux; + composite->mux_ops = mux_ops; + if (mux_ops->set_parent) + composite_ops->set_parent = clk_composite_set_parent; + mux->data = (ulong)composite; + } + + if (rate && rate_ops) { + if (!rate_ops->get_rate) { + clk = ERR_PTR(-EINVAL); + goto err; + } + composite_ops->get_rate = clk_composite_recalc_rate; + + /* .set_rate requires either .round_rate or .determine_rate */ + if (rate_ops->set_rate) + composite_ops->set_rate = clk_composite_set_rate; + + composite->rate = rate; + composite->rate_ops = rate_ops; + rate->data = (ulong)composite; + } + + if (gate && gate_ops) { + if (!gate_ops->enable || !gate_ops->disable) { + clk = ERR_PTR(-EINVAL); + goto err; + } + + composite->gate = gate; + composite->gate_ops = gate_ops; + composite_ops->enable = clk_composite_enable; + composite_ops->disable = clk_composite_disable; + gate->data = (ulong)composite; + } + + clk = &composite->clk; + ret = clk_register(clk, UBOOT_DM_CLK_COMPOSITE, name, + parent_names[clk_composite_get_parent(clk)]); + if (ret) { + clk = ERR_PTR(ret); + goto err; + } + + return clk; + +err: + kfree(composite); + return clk; +} + +U_BOOT_DRIVER(clk_composite) = { + .name = UBOOT_DM_CLK_COMPOSITE, + .id = UCLASS_CLK, + .ops = &clk_composite_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 522e73e8517..b9547736ee2 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -137,6 +137,28 @@ struct clk_fixed_rate { #define to_clk_fixed_rate(dev) ((struct clk_fixed_rate *)dev_get_platdata(dev)) +struct clk_composite { + struct clk clk; + struct clk_ops ops; + + struct clk *mux; + struct clk *rate; + struct clk *gate; + + const struct clk_ops *mux_ops; + const struct clk_ops *rate_ops; + const struct clk_ops *gate_ops; +}; + +#define to_clk_composite(_clk) container_of(_clk, struct clk_composite, clk) + +struct clk *clk_register_composite(struct device *dev, const char *name, + const char * const *parent_names, int num_parents, + struct clk *mux_clk, const struct clk_ops *mux_ops, + struct clk *rate_clk, const struct clk_ops *rate_ops, + struct clk *gate_clk, const struct clk_ops *gate_ops, + unsigned long flags); + int clk_register(struct clk *clk, const char *drv_name, const char *name, const char *parent_name); From 2b12957d01763bf2a52a4727327a7b3b80bc9111 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 31 Jul 2019 07:01:57 +0000 Subject: [PATCH 13/16] clk: gate: support sandbox Introduce io_gate_val for sandbox clk gate test usage Signed-off-by: Peng Fan --- drivers/clk/clk-gate.c | 11 +++++++++++ include/linux/clk-provider.h | 3 +++ 2 files changed, 14 insertions(+) diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index a3a1fdd3b2f..70b87945545 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -55,7 +55,11 @@ static void clk_gate_endisable(struct clk *clk, int enable) if (set) reg |= BIT(gate->bit_idx); } else { +#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF) + reg = gate->io_gate_val; +#else reg = readl(gate->reg); +#endif if (set) reg |= BIT(gate->bit_idx); @@ -86,7 +90,11 @@ int clk_gate_is_enabled(struct clk *clk) dev_get_clk_ptr(clk->dev) : clk); u32 reg; +#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF) + reg = gate->io_gate_val; +#else reg = readl(gate->reg); +#endif /* if a set bit disables this clk, flip it before masking */ if (gate->flags & CLK_GATE_SET_TO_DISABLE) @@ -128,6 +136,9 @@ struct clk *clk_register_gate(struct device *dev, const char *name, gate->reg = reg; gate->bit_idx = bit_idx; gate->flags = clk_gate_flags; +#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF) + gate->io_gate_val = *(u32 *)reg; +#endif clk = &gate->clk; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index b9547736ee2..02ff1a311ac 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -75,6 +75,9 @@ struct clk_gate { void __iomem *reg; u8 bit_idx; u8 flags; +#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF) + u32 io_gate_val; +#endif }; #define to_clk_gate(_clk) container_of(_clk, struct clk_gate, clk) From 7bd64322100296dbc39b839a5fbabb9e8f74341b Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 31 Jul 2019 07:02:00 +0000 Subject: [PATCH 14/16] configs: sandbox: Enable composite clk Enable composite clk for sandbox test Signed-off-by: Peng Fan --- configs/sandbox_defconfig | 1 + configs/sandbox_flattree_defconfig | 1 + 2 files changed, 2 insertions(+) diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 62594e3c215..7ff7122fb6b 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -93,6 +93,7 @@ CONFIG_BOOTCOUNT_LIMIT=y CONFIG_DM_BOOTCOUNT=y CONFIG_DM_BOOTCOUNT_RTC=y CONFIG_CLK=y +CONFIG_CLK_COMPOSITE_CCF=y CONFIG_SANDBOX_CLK_CCF=y CONFIG_CPU=y CONFIG_DM_DEMO=y diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig index 2429ae46216..610457e0aa4 100644 --- a/configs/sandbox_flattree_defconfig +++ b/configs/sandbox_flattree_defconfig @@ -66,6 +66,7 @@ CONFIG_DEBUG_DEVRES=y CONFIG_ADC=y CONFIG_ADC_SANDBOX=y CONFIG_CLK=y +CONFIG_CLK_COMPOSITE_CCF=y CONFIG_SANDBOX_CLK_CCF=y CONFIG_CPU=y CONFIG_DM_DEMO=y From 8f611dc71cb58f2a6a81fbb29e15c0cf3f393965 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 31 Jul 2019 07:02:02 +0000 Subject: [PATCH 15/16] clk: sandbox: add composite clk Add composite clk to sandbox driver Signed-off-by: Peng Fan --- drivers/clk/clk_sandbox_ccf.c | 80 +++++++++++++++++++++++++++++++++++ include/sandbox-clk.h | 1 + 2 files changed, 81 insertions(+) diff --git a/drivers/clk/clk_sandbox_ccf.c b/drivers/clk/clk_sandbox_ccf.c index edeb0f2cf3e..e126f18d8e9 100644 --- a/drivers/clk/clk_sandbox_ccf.c +++ b/drivers/clk/clk_sandbox_ccf.c @@ -130,6 +130,80 @@ U_BOOT_DRIVER(sandbox_clk_gate2) = { .ops = &clk_gate2_ops, }; +static unsigned long sandbox_clk_composite_divider_recalc_rate(struct clk *clk) +{ + struct clk_divider *divider = (struct clk_divider *)to_clk_divider(clk); + struct clk_composite *composite = (struct clk_composite *)clk->data; + ulong parent_rate = clk_get_parent_rate(&composite->clk); + unsigned int val; + + val = divider->io_divider_val; + val >>= divider->shift; + val &= clk_div_mask(divider->width); + + return divider_recalc_rate(clk, parent_rate, val, divider->table, + divider->flags, divider->width); +} + +static const struct clk_ops sandbox_clk_composite_divider_ops = { + .get_rate = sandbox_clk_composite_divider_recalc_rate, +}; + +struct clk *sandbox_clk_composite(const char *name, + const char * const *parent_names, + int num_parents, void __iomem *reg, + unsigned long flags) +{ + struct clk *clk = ERR_PTR(-ENOMEM); + struct clk_divider *div = NULL; + struct clk_gate *gate = NULL; + struct clk_mux *mux = NULL; + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + goto fail; + + mux->reg = reg; + mux->shift = 24; + mux->mask = 0x7; + mux->num_parents = num_parents; + mux->flags = flags; + mux->parent_names = parent_names; + + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + goto fail; + + div->reg = reg; + div->shift = 16; + div->width = 3; + div->flags = CLK_DIVIDER_ROUND_CLOSEST | flags; + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + goto fail; + + gate->reg = reg; + gate->bit_idx = 28; + gate->flags = flags; + + clk = clk_register_composite(NULL, name, + parent_names, num_parents, + &mux->clk, &clk_mux_ops, &div->clk, + &sandbox_clk_composite_divider_ops, + &gate->clk, &clk_gate_ops, flags); + if (IS_ERR(clk)) + goto fail; + + return clk; + +fail: + kfree(gate); + kfree(div); + kfree(mux); + return ERR_CAST(clk); +} + /* --- Sandbox Gate --- */ /* The CCF core driver itself */ static const struct udevice_id sandbox_clk_ccf_test_ids[] = { @@ -138,6 +212,7 @@ static const struct udevice_id sandbox_clk_ccf_test_ids[] = { }; static const char *const usdhc_sels[] = { "pll3_60m", "pll3_80m", }; +static const char *const i2c_sels[] = { "pll3_60m", "pll3_80m", }; static int sandbox_clk_ccf_probe(struct udevice *dev) { @@ -174,6 +249,11 @@ static int sandbox_clk_ccf_probe(struct udevice *dev) sandbox_clk_mux("usdhc2_sel", ®, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels))); + reg = BIT(28) | BIT(24) | BIT(16); + clk_dm(SANDBOX_CLK_I2C, + sandbox_clk_composite("i2c", i2c_sels, ARRAY_SIZE(i2c_sels), + ®, 0)); + return 0; } diff --git a/include/sandbox-clk.h b/include/sandbox-clk.h index 37c9838f765..f449de13649 100644 --- a/include/sandbox-clk.h +++ b/include/sandbox-clk.h @@ -19,6 +19,7 @@ enum { SANDBOX_CLK_ECSPI1, SANDBOX_CLK_USDHC1_SEL, SANDBOX_CLK_USDHC2_SEL, + SANDBOX_CLK_I2C, }; enum sandbox_pllv3_type { From 4f895988adc021d96c02cbcbb7b899c57ecbae4a Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 31 Jul 2019 07:02:05 +0000 Subject: [PATCH 16/16] test: dm: clk_ccf: test composite clk Test composite clk with dm ccf Signed-off-by: Peng Fan --- test/dm/clk_ccf.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/dm/clk_ccf.c b/test/dm/clk_ccf.c index 8d397593a32..bbc4b500e83 100644 --- a/test/dm/clk_ccf.c +++ b/test/dm/clk_ccf.c @@ -56,6 +56,14 @@ static int dm_test_clk_ccf(struct unit_test_state *uts) pclk = clk_get_parent(clk); ut_asserteq_str("pll3_80m", pclk->dev->name); + /* Test the composite of CCF */ + ret = clk_get_by_id(SANDBOX_CLK_I2C, &clk); + ut_assertok(ret); + ut_asserteq_str("i2c", clk->dev->name); + + rate = clk_get_rate(clk); + ut_asserteq(rate, 60000000); + return 1; }