From bea07a688163c806a1001028f9eda715ca96976c Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 1 Feb 2017 15:58:58 +0100 Subject: clk: implement CLK_OPS_PARENT_ENABLE Some clocks may only be modified when their parent clocks are enabled. The kernel has the CLK_OPS_PARENT_ENABLE flag for this purpose. Implement it for barebox aswell. Signed-off-by: Sascha Hauer --- include/linux/clk.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux/clk.h') diff --git a/include/linux/clk.h b/include/linux/clk.h index 8cb9731f12..697ab4ff4a 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -198,6 +198,8 @@ static inline int clk_set_rate(struct clk *clk, unsigned long rate) #ifdef CONFIG_COMMON_CLK #define CLK_SET_RATE_PARENT (1 << 0) /* propagate rate change up one level */ +/* parents need enable during gate/ungate, set rate and re-parent */ +#define CLK_OPS_PARENT_ENABLE (1 << 12) #define CLK_GATE_INVERTED (1 << 0) #define CLK_GATE_HIWORD_MASK (1 << 1) -- cgit v1.2.3 From 2225c194edc1e2f71c0d8dc1c0c3b936d241060a Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 2 Feb 2017 10:34:21 +0100 Subject: clk: Add support for shared gates Sometimes a single software control knob controls multiple gates in hardware. This patch adds support for shared gates which help coping this situation. The first gate is registered with the hardware gate as usual, the others are registered as shared gates which does not have hardware control itself, but only switches the real hardware gate. Signed-off-by: Sascha Hauer --- drivers/clk/Makefile | 3 +- drivers/clk/clk-gate-shared.c | 123 ++++++++++++++++++++++++++++++++++++++++++ include/linux/clk.h | 3 ++ 3 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/clk-gate-shared.c (limited to 'include/linux/clk.h') diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index a4e4ed0241..5811d28b88 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed.o clk-divider.o clk-fixed-factor.o \ clk-mux.o clk-gate.o clk-composite.o \ - clk-fractional-divider.o clk-conf.o + clk-fractional-divider.o clk-conf.o \ + clk-gate-shared.o obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o obj-$(CONFIG_ARCH_MVEBU) += mvebu/ diff --git a/drivers/clk/clk-gate-shared.c b/drivers/clk/clk-gate-shared.c new file mode 100644 index 0000000000..a95f940dd2 --- /dev/null +++ b/drivers/clk/clk-gate-shared.c @@ -0,0 +1,123 @@ +/* + * clk-gate-shared.c - generic barebox clock support. Based on Linux clk support + * + * Copyright (c) 2017 Sascha Hauer , Pengutronix + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include + +struct clk_gate_shared { + struct clk clk; + const char *parent; + const char *companion_gate; + struct clk *companion_clk; +}; + +#define to_clk_gate_shared(_clk) container_of(_clk, struct clk_gate_shared, clk) + +static struct clk *lookup_companion(struct clk_gate_shared *g) +{ + if (IS_ERR(g->companion_clk)) + g->companion_clk = clk_lookup(g->companion_gate); + + if (IS_ERR(g->companion_clk)) + return NULL; + + return g->companion_clk; +} + +static int clk_gate_shared_enable(struct clk *clk) +{ + struct clk_gate_shared *g = to_clk_gate_shared(clk); + + return clk_enable(lookup_companion(g)); +} + +static void clk_gate_shared_disable(struct clk *clk) +{ + struct clk_gate_shared *g = to_clk_gate_shared(clk); + + clk_disable(lookup_companion(g)); +} + +static int clk_gate_shared_is_enabled(struct clk *clk) +{ + struct clk_gate_shared *g = to_clk_gate_shared(clk); + + return clk_is_enabled(lookup_companion(g)); +} + +static struct clk_ops clk_gate_shared_ops = { + .set_rate = clk_parent_set_rate, + .round_rate = clk_parent_round_rate, + .enable = clk_gate_shared_enable, + .disable = clk_gate_shared_disable, + .is_enabled = clk_gate_shared_is_enabled, +}; + +struct clk *clk_gate_shared_alloc(const char *name, const char *parent, const char *companion, + unsigned flags) +{ + struct clk_gate_shared *g = xzalloc(sizeof(*g)); + + g->parent = parent; + g->companion_gate = companion; + g->companion_clk = ERR_PTR(-EINVAL); + g->clk.ops = &clk_gate_shared_ops; + g->clk.name = name; + g->clk.flags = flags; + g->clk.parent_names = &g->parent; + g->clk.num_parents = 1; + + return &g->clk; +} + +void clk_gate_shared_free(struct clk *clk) +{ + struct clk_gate_shared *g = to_clk_gate_shared(clk); + + free(g); +} + +/* + * clk_gate_shared - register a gate controlled by another gate + * @name: The name of the new clock gate + * @parent: The parent name of the new clock + * companion: The hardware gate this clock is controlled by + * @flags: common CLK_* flags + * + * This gate clock is used when a single software control knob controls multiple + * gates in hardware. The first gate is then registered as the real hardware gate, + * the others are registered with this function. This gate has no hardware control + * itself, but only enables/disabled its companion hardware gate. + */ +struct clk *clk_gate_shared(const char *name, const char *parent, const char *companion, + unsigned flags) +{ + struct clk *clk; + int ret; + + clk = clk_gate_shared_alloc(name , parent, companion, flags); + + ret = clk_register(clk); + if (ret) { + clk_gate_shared_free(clk); + return ERR_PTR(ret); + } + + return clk; +} diff --git a/include/linux/clk.h b/include/linux/clk.h index 697ab4ff4a..7dd52388c5 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -296,6 +296,9 @@ struct clk *clk_gate(const char *name, const char *parent, void __iomem *reg, u8 shift, unsigned flags, u8 clk_gate_flags); struct clk *clk_gate_inverted(const char *name, const char *parent, void __iomem *reg, u8 shift, unsigned flags); +struct clk *clk_gate_shared(const char *name, const char *parent, const char *shared, + unsigned flags); + int clk_is_enabled(struct clk *clk); int clk_is_enabled_always(struct clk *clk); -- cgit v1.2.3