On 03/10/2012 01:54 AM, Mike Turquette wrote: > Many platforms support simple gateable clocks, fixed-rate clocks, > adjustable divider clocks and multi-parent multiplexer clocks. > > This patch introduces basic clock types for the above-mentioned hardware > which share some common characteristics. > > Based on original work by Jeremy Kerr and contribution by Jamie Iles. > Dividers and multiplexor clocks originally contributed by Richard Zhao & > Sascha Hauer. > > Signed-off-by: Mike Turquette <mturque...@linaro.org> > Signed-off-by: Mike Turquette <mturque...@ti.com> > Cc: Russell King <li...@arm.linux.org.uk> > Cc: Jeremy Kerr <jeremy.k...@canonical.com> > Cc: Thomas Gleixner <t...@linutronix.de> > Cc: Arnd Bergman <arnd.bergm...@linaro.org> > Cc: Paul Walmsley <p...@pwsan.com> > Cc: Shawn Guo <shawn....@freescale.com> > Cc: Sascha Hauer <s.ha...@pengutronix.de> > Cc: Jamie Iles <ja...@jamieiles.com> > Cc: Richard Zhao <richard.z...@linaro.org> > Cc: Saravana Kannan <skan...@codeaurora.org> > Cc: Magnus Damm <magnus.d...@gmail.com> > Cc: Rob Herring <rob.herr...@calxeda.com> > Cc: Mark Brown <broo...@opensource.wolfsonmicro.com> > Cc: Linus Walleij <linus.wall...@stericsson.com> > Cc: Stephen Boyd <sb...@codeaurora.org> > Cc: Amit Kucheria <amit.kuche...@linaro.org> > Cc: Deepak Saxena <dsax...@linaro.org> > Cc: Grant Likely <grant.lik...@secretlab.ca> > Cc: Andrew Lunn <and...@lunn.ch>
One issue with spinlocks below, but otherwise: Reviewed-by: Rob Herring <rob.herr...@calxeda.com> > --- > Changes since v5: > * standardized the hw-specific locking in the basic clock types > * export the individual ops for each basic clock type > * improve registration for single-parent basic clocks (thanks Sascha) > * fixed bugs in gate clock's static initializers (thanks Andrew) > > drivers/clk/Makefile | 3 +- > drivers/clk/clk-divider.c | 204 > ++++++++++++++++++++++++++++++++++++++++++ > drivers/clk/clk-fixed-rate.c | 82 +++++++++++++++++ > drivers/clk/clk-gate.c | 157 ++++++++++++++++++++++++++++++++ > drivers/clk/clk-mux.c | 123 +++++++++++++++++++++++++ > include/linux/clk-private.h | 124 +++++++++++++++++++++++++ > include/linux/clk-provider.h | 127 ++++++++++++++++++++++++++ > 7 files changed, 819 insertions(+), 1 deletions(-) > create mode 100644 drivers/clk/clk-divider.c > create mode 100644 drivers/clk/clk-fixed-rate.c > create mode 100644 drivers/clk/clk-gate.c > create mode 100644 drivers/clk/clk-mux.c > > diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile > index ff362c4..1f736bc 100644 > --- a/drivers/clk/Makefile > +++ b/drivers/clk/Makefile > @@ -1,3 +1,4 @@ > > obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o > -obj-$(CONFIG_COMMON_CLK) += clk.o > +obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed-rate.o clk-gate.o \ > + clk-mux.o clk-divider.o > diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c > new file mode 100644 > index 0000000..c0c4e0b > --- /dev/null > +++ b/drivers/clk/clk-divider.c > @@ -0,0 +1,204 @@ > +/* > + * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.ha...@pengutronix.de> > + * Copyright (C) 2011 Richard Zhao, Linaro <richard.z...@linaro.org> > + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturque...@linaro.org> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + * > + * Adjustable divider clock implementation > + */ > + > +#include <linux/clk-provider.h> > +#include <linux/module.h> > +#include <linux/slab.h> > +#include <linux/io.h> > +#include <linux/err.h> > +#include <linux/string.h> > + > +/* > + * DOC: basic adjustable divider clock that cannot gate > + * > + * Traits of this clock: > + * prepare - clk_prepare only ensures that parents are prepared > + * enable - clk_enable only ensures that parents are enabled > + * rate - rate is adjustable. clk->rate = parent->rate / divisor > + * parent - fixed parent. No clk_set_parent support > + */ > + > +#define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw) > + > +static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, > + unsigned long parent_rate) > +{ > + struct clk_divider *divider = to_clk_divider(hw); > + unsigned int div; > + unsigned long flags = 0; > + > + if (divider->lock) > + spin_lock_irqsave(divider->lock, flags); > + > + div = readl(divider->reg) >> divider->shift; > + div &= (1 << divider->width) - 1; > + > + if (divider->lock) > + spin_unlock_irqrestore(divider->lock, flags); What are you locking against? You are only reading the register. > + > + if (!(divider->flags & CLK_DIVIDER_ONE_BASED)) > + div++; > + > + return parent_rate / div; > +} > +EXPORT_SYMBOL_GPL(clk_divider_recalc_rate); > + > +static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, > + unsigned long *best_parent_rate) > +{ > + struct clk_divider *divider = to_clk_divider(hw); > + int i, bestdiv = 0; > + unsigned long parent_rate, best = 0, now, maxdiv; > + > + maxdiv = (1 << divider->width); > + > + if (divider->flags & CLK_DIVIDER_ONE_BASED) > + maxdiv--; > + > + if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) { > + parent_rate = __clk_get_rate(__clk_get_parent(hw->clk)); > + bestdiv = parent_rate / rate; > + bestdiv = bestdiv == 0 ? 1 : bestdiv; > + bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; > + goto out; > + } > + > + /* > + * The maximum divider we can use without overflowing > + * unsigned long in rate * i below > + */ > + maxdiv = min(ULONG_MAX / rate, maxdiv); > + > + for (i = 1; i <= maxdiv; i++) { > + int div; > + parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), > + rate * i); > + div = parent_rate / rate; > + div = div > maxdiv ? maxdiv : div; > + div = div < 1 ? 1 : div; > + now = parent_rate / div; > + > + if (now <= rate && now >= best) { > + bestdiv = div; > + best = now; > + *best_parent_rate = parent_rate; > + } > + } > + > + if (!bestdiv) { > + bestdiv = (1 << divider->width); > + parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), 1); > + } else { > + parent_rate = best * bestdiv; > + } > + > +out: > + if (best_parent_rate) > + *best_parent_rate = parent_rate; > + > + return bestdiv; > +} > + > +static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, > + unsigned long *prate) > +{ > + unsigned long best_parent_rate; > + int div = clk_divider_bestdiv(hw, rate, &best_parent_rate); > + if (prate) { > + if (best_parent_rate == __clk_get_rate( > + __clk_get_parent(hw->clk))) > + *prate = 0; > + else > + *prate = best_parent_rate; > + } > + > + return best_parent_rate / div; > +} > +EXPORT_SYMBOL_GPL(clk_divider_round_rate); > + > +static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate) > +{ > + unsigned long best_parent_rate; > + struct clk_divider *divider = to_clk_divider(hw); > + unsigned int div; > + unsigned long flags = 0; > + u32 val; > + > + div = clk_divider_bestdiv(hw, rate, &best_parent_rate); > + > + if (divider->lock) > + spin_lock_irqsave(divider->lock, flags); > + > + if (!(divider->flags & CLK_DIVIDER_ONE_BASED)) > + div--; > + > + val = readl(divider->reg); > + val &= ~(((1 << divider->width) - 1) << divider->shift); > + val |= div << divider->shift; > + writel(val, divider->reg); > + > + if (divider->lock) > + spin_unlock_irqrestore(divider->lock, flags); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(clk_divider_set_rate); > + > +struct clk_ops clk_divider_ops = { > + .recalc_rate = clk_divider_recalc_rate, > + .round_rate = clk_divider_round_rate, > + .set_rate = clk_divider_set_rate, > +}; > +EXPORT_SYMBOL_GPL(clk_divider_ops); > + > +struct clk *clk_register_divider(struct device *dev, const char *name, > + const char *parent_name, unsigned long flags, > + void __iomem *reg, u8 shift, u8 width, > + u8 clk_divider_flags, spinlock_t *lock) > +{ > + struct clk_divider *div; > + struct clk *clk; > + > + div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL); > + > + if (!div) { > + pr_err("%s: could not allocate divider clk\n", __func__); > + return NULL; > + } > + > + /* struct clk_divider assignments */ > + div->reg = reg; > + div->shift = shift; > + div->width = width; > + div->flags = clk_divider_flags; > + div->lock = lock; > + > + if (parent_name) { > + div->parent[0] = kstrdup(parent_name, GFP_KERNEL); > + if (!div->parent[0]) > + goto out; > + } > + > + clk = clk_register(dev, name, > + &clk_divider_ops, &div->hw, > + div->parent, > + (parent_name ? 1 : 0), > + flags); > + if (clk) > + return clk; > + > +out: > + kfree(div->parent[0]); > + kfree(div); > + > + return NULL; > +} > diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c > new file mode 100644 > index 0000000..90c79fb > --- /dev/null > +++ b/drivers/clk/clk-fixed-rate.c > @@ -0,0 +1,82 @@ > +/* > + * Copyright (C) 2010-2011 Canonical Ltd <jeremy.k...@canonical.com> > + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturque...@linaro.org> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + * > + * Fixed rate clock implementation > + */ > + > +#include <linux/clk-provider.h> > +#include <linux/module.h> > +#include <linux/slab.h> > +#include <linux/io.h> > +#include <linux/err.h> > + > +/* > + * DOC: basic fixed-rate clock that cannot gate > + * > + * Traits of this clock: > + * prepare - clk_(un)prepare only ensures parents are prepared > + * enable - clk_enable only ensures parents are enabled > + * rate - rate is always a fixed value. No clk_set_rate support > + * parent - fixed parent. No clk_set_parent support > + */ > + > +#define to_clk_fixed_rate(_hw) container_of(_hw, struct clk_fixed_rate, hw) > + > +static unsigned long clk_fixed_rate_recalc_rate(struct clk_hw *hw, > + unsigned long parent_rate) > +{ > + return to_clk_fixed_rate(hw)->fixed_rate; > +} > +EXPORT_SYMBOL_GPL(clk_fixed_rate_recalc_rate); > + > +struct clk_ops clk_fixed_rate_ops = { > + .recalc_rate = clk_fixed_rate_recalc_rate, > +}; > +EXPORT_SYMBOL_GPL(clk_fixed_rate_ops); > + > +struct clk *clk_register_fixed_rate(struct device *dev, const char *name, > + const char *parent_name, unsigned long flags, > + unsigned long fixed_rate) > +{ > + struct clk_fixed_rate *fixed; > + char **parent_names = NULL; > + u8 len; > + > + fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL); > + > + if (!fixed) { > + pr_err("%s: could not allocate fixed clk\n", __func__); > + return ERR_PTR(-ENOMEM); > + } > + > + /* struct clk_fixed_rate assignments */ > + fixed->fixed_rate = fixed_rate; > + > + if (parent_name) { > + parent_names = kmalloc(sizeof(char *), GFP_KERNEL); > + > + if (! parent_names) > + goto out; > + > + len = sizeof(char) * strlen(parent_name); > + > + parent_names[0] = kmalloc(len, GFP_KERNEL); > + > + if (!parent_names[0]) > + goto out; > + > + strncpy(parent_names[0], parent_name, len); > + } > + > +out: > + return clk_register(dev, name, > + &clk_fixed_rate_ops, &fixed->hw, > + parent_names, > + (parent_name ? 1 : 0), > + flags); > +} > diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c > new file mode 100644 > index 0000000..5d50efa > --- /dev/null > +++ b/drivers/clk/clk-gate.c > @@ -0,0 +1,157 @@ > +/* > + * Copyright (C) 2010-2011 Canonical Ltd <jeremy.k...@canonical.com> > + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturque...@linaro.org> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + * > + * Gated clock implementation > + */ > + > +#include <linux/clk-provider.h> > +#include <linux/module.h> > +#include <linux/slab.h> > +#include <linux/io.h> > +#include <linux/err.h> > +#include <linux/string.h> > + > +/** > + * DOC: basic gatable clock which can gate and ungate it's ouput > + * > + * 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 > + */ > + > +#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) > + > +static void clk_gate_set_bit(struct clk_gate *gate) > +{ > + u32 reg; > + unsigned long flags = 0; > + > + if (gate->lock) > + spin_lock_irqsave(gate->lock, flags); > + > + reg = readl(gate->reg); > + reg |= BIT(gate->bit_idx); > + writel(reg, gate->reg); > + > + if (gate->lock) > + spin_unlock_irqrestore(gate->lock, flags); > +} > + > +static void clk_gate_clear_bit(struct clk_gate *gate) > +{ > + u32 reg; > + unsigned long flags = 0; > + > + if (gate->lock) > + spin_lock_irqsave(gate->lock, flags); > + > + reg = readl(gate->reg); > + reg &= ~BIT(gate->bit_idx); > + writel(reg, gate->reg); > + > + if (gate->lock) > + spin_unlock_irqrestore(gate->lock, flags); > +} > + > +static int clk_gate_enable(struct clk_hw *hw) > +{ > + struct clk_gate *gate = to_clk_gate(hw); > + > + if (gate->flags & CLK_GATE_SET_TO_DISABLE) > + clk_gate_clear_bit(gate); > + else > + clk_gate_set_bit(gate); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(clk_gate_enable); > + > +static void clk_gate_disable(struct clk_hw *hw) > +{ > + struct clk_gate *gate = to_clk_gate(hw); > + > + if (gate->flags & CLK_GATE_SET_TO_DISABLE) > + clk_gate_set_bit(gate); > + else > + clk_gate_clear_bit(gate); > +} > +EXPORT_SYMBOL_GPL(clk_gate_disable); > + > +static int clk_gate_is_enabled(struct clk_hw *hw) > +{ > + u32 reg; > + unsigned long flags = 0; > + struct clk_gate *gate = to_clk_gate(hw); > + > + if (gate->lock) > + spin_lock_irqsave(gate->lock, flags); > + > + reg = readl(gate->reg); > + > + if (gate->lock) > + spin_unlock_irqrestore(gate->lock, flags); > + ditto. > + /* 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; > +} > +EXPORT_SYMBOL_GPL(clk_gate_is_enabled); > + > +struct clk_ops clk_gate_ops = { > + .enable = clk_gate_enable, > + .disable = clk_gate_disable, > + .is_enabled = clk_gate_is_enabled, > +}; > +EXPORT_SYMBOL_GPL(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_gate *gate; > + struct clk *clk; > + > + gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); > + > + if (!gate) { > + pr_err("%s: could not allocate gated clk\n", __func__); > + return NULL; > + } > + > + /* struct clk_gate assignments */ > + gate->reg = reg; > + gate->bit_idx = bit_idx; > + gate->flags = clk_gate_flags; > + gate->lock = lock; > + > + if (parent_name) { > + gate->parent[0] = kstrdup(parent_name, GFP_KERNEL); > + if (!gate->parent[0]) > + goto out; > + } > + > + clk = clk_register(dev, name, > + &clk_gate_ops, &gate->hw, > + gate->parent, > + (parent_name ? 1 : 0), > + flags); > + if (clk) > + return clk; > +out: > + kfree(gate->parent[0]); > + kfree(gate); > + > + return NULL; > +} > diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c > new file mode 100644 > index 0000000..6fc0878 > --- /dev/null > +++ b/drivers/clk/clk-mux.c > @@ -0,0 +1,123 @@ > +/* > + * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.ha...@pengutronix.de> > + * Copyright (C) 2011 Richard Zhao, Linaro <richard.z...@linaro.org> > + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturque...@linaro.org> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + * > + * Simple multiplexer clock implementation > + */ > + > +#include <linux/clk.h> > +#include <linux/clk-provider.h> > +#include <linux/module.h> > +#include <linux/slab.h> > +#include <linux/io.h> > +#include <linux/err.h> > + > +/* > + * DOC: basic adjustable multiplexer clock that cannot gate > + * > + * Traits of this clock: > + * prepare - clk_prepare only ensures that parents are prepared > + * enable - clk_enable only ensures that parents are enabled > + * rate - rate is only affected by parent switching. No clk_set_rate support > + * parent - parent is adjustable through clk_set_parent > + */ > + > +#define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw) > + > +static u8 clk_mux_get_parent(struct clk_hw *hw) > +{ > + struct clk_mux *mux = to_clk_mux(hw); > + u32 val; > + unsigned long flags = 0; > + > + if (mux->lock) > + spin_lock_irqsave(mux->lock, flags); > + > + /* > + * FIXME need a mux-specific flag to determine if val is bitwise or > numeric > + * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1 > + * to 0x7 (index starts at one) > + * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so > + * val = 0x4 really means "bit 2, index starts at bit 0" > + */ > + val = readl(mux->reg) >> mux->shift; > + val &= (1 << mux->width) - 1; > + > + if (mux->lock) > + spin_unlock_irqrestore(mux->lock, flags); ditto > + > + if (val && (mux->flags & CLK_MUX_INDEX_BIT)) > + val = ffs(val) - 1; > + > + if (val && (mux->flags & CLK_MUX_INDEX_ONE)) > + val--; > + > + if (val >= __clk_get_num_parents(hw->clk)) > + return -EINVAL; > + > + return val; > +} > +EXPORT_SYMBOL_GPL(clk_mux_get_parent); > + > +static int clk_mux_set_parent(struct clk_hw *hw, u8 index) > +{ > + struct clk_mux *mux = to_clk_mux(hw); > + u32 val; > + unsigned long flags = 0; > + > + if (mux->flags & CLK_MUX_INDEX_BIT) > + index = (1 << ffs(index)); > + > + if (mux->flags & CLK_MUX_INDEX_ONE) > + index++; > + > + if (mux->lock) > + spin_lock_irqsave(mux->lock, flags); > + > + val = readl(mux->reg); > + val &= ~(((1 << mux->width) - 1) << mux->shift); > + val |= index << mux->shift; > + writel(val, mux->reg); > + > + if (mux->lock) > + spin_unlock_irqrestore(mux->lock, flags); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(clk_mux_set_parent); > + > +struct clk_ops clk_mux_ops = { > + .get_parent = clk_mux_get_parent, > + .set_parent = clk_mux_set_parent, > +}; > +EXPORT_SYMBOL_GPL(clk_mux_ops); > + > +struct clk *clk_register_mux(struct device *dev, const char *name, > + char **parent_names, u8 num_parents, unsigned long flags, > + void __iomem *reg, u8 shift, u8 width, > + u8 clk_mux_flags, spinlock_t *lock) > +{ > + struct clk_mux *mux; > + > + mux = kmalloc(sizeof(struct clk_mux), GFP_KERNEL); > + > + if (!mux) { > + pr_err("%s: could not allocate mux clk\n", __func__); > + return ERR_PTR(-ENOMEM); > + } > + > + /* struct clk_mux assignments */ > + mux->reg = reg; > + mux->shift = shift; > + mux->width = width; > + mux->flags = clk_mux_flags; > + mux->lock = lock; > + > + return clk_register(dev, name, &clk_mux_ops, &mux->hw, > + parent_names, num_parents, flags); > +} > diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h > index 33bf6a7..9d5c4b1 100644 > --- a/include/linux/clk-private.h > +++ b/include/linux/clk-private.h > @@ -45,6 +45,130 @@ struct clk { > #endif > }; > > +/* > + * DOC: Basic clock implementations common to many platforms > + * > + * Each basic clock hardware type is comprised of a structure describing the > + * clock hardware, implementations of the relevant callbacks in struct > clk_ops, > + * unique flags for that hardware type, a registration function and an > + * alternative macro for static initialization > + */ > + > +extern struct clk_ops clk_fixed_rate_ops; > + > +#define DEFINE_CLK_FIXED_RATE(_name, _flags, _rate, \ > + _fixed_rate_flags) \ > + static struct clk _name; \ > + static char *_name##_parent_names[] = {}; \ > + static struct clk_fixed_rate _name##_hw = { \ > + .hw = { \ > + .clk = &_name, \ > + }, \ > + .fixed_rate = _rate, \ > + .flags = _fixed_rate_flags, \ > + }; \ > + static struct clk _name = { \ > + .name = #_name, \ > + .ops = &clk_fixed_rate_ops, \ > + .hw = &_name##_hw.hw, \ > + .parent_names = _name##_parent_names, \ > + .num_parents = \ > + ARRAY_SIZE(_name##_parent_names), \ > + .flags = _flags, \ > + }; > + > +extern struct clk_ops clk_gate_ops; > + > +#define DEFINE_CLK_GATE(_name, _parent_name, _parent_ptr, \ > + _flags, _reg, _bit_idx, \ > + _gate_flags, _lock) \ > + static struct clk _name; \ > + static char *_name##_parent_names[] = { \ > + _parent_name, \ > + }; \ > + static struct clk *_name##_parents[] = { \ > + _parent_ptr, \ > + }; \ > + static struct clk_gate _name##_hw = { \ > + .hw = { \ > + .clk = &_name, \ > + }, \ > + .reg = _reg, \ > + .bit_idx = _bit_idx, \ > + .flags = _gate_flags, \ > + .lock = _lock, \ > + }; \ > + static struct clk _name = { \ > + .name = #_name, \ > + .ops = &clk_gate_ops, \ > + .hw = &_name##_hw.hw, \ > + .parent_names = _name##_parent_names, \ > + .num_parents = \ > + ARRAY_SIZE(_name##_parent_names), \ > + .parents = _name##_parents, \ > + .flags = _flags, \ > + }; > + > +extern struct clk_ops clk_divider_ops; > + > +#define DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \ > + _flags, _reg, _shift, _width, \ > + _divider_flags, _lock) \ > + static struct clk _name; \ > + static char *_name##_parent_names[] = { \ > + _parent_name, \ > + }; \ > + static struct clk *_name##_parents[] = { \ > + _parent_ptr, \ > + }; \ > + static struct clk_divider _name##_hw = { \ > + .hw = { \ > + .clk = &_name, \ > + }, \ > + .reg = _reg, \ > + .shift = _shift, \ > + .width = _width, \ > + .flags = _divider_flags, \ > + .lock = _lock, \ > + }; \ > + static struct clk _name = { \ > + .name = #_name, \ > + .ops = &clk_divider_ops, \ > + .hw = &_name##_hw.hw, \ > + .parent_names = _name##_parent_names, \ > + .num_parents = \ > + ARRAY_SIZE(_name##_parent_names), \ > + .parents = _name##_parents, \ > + .flags = _flags, \ > + }; > + > +extern struct clk_ops clk_mux_ops; > + > +#define DEFINE_CLK_MUX(_name, _parent_names, _parents, _flags, \ > + _reg, _shift, _width, \ > + _mux_flags, _lock) \ > + static struct clk _name; \ > + static struct clk_mux _name##_hw = { \ > + .hw = { \ > + .clk = &_name, \ > + }, \ > + .reg = _reg, \ > + .shift = _shift, \ > + .width = _width, \ > + .flags = _mux_flags, \ > + .lock = _lock, \ > + }; \ > + static struct clk _name = { \ > + .name = #_name, \ > + .ops = &clk_mux_ops, \ > + .hw = &_name##_hw.hw, \ > + .parent_names = _parent_names, \ > + .num_parents = \ > + ARRAY_SIZE(_parent_names), \ > + .parents = _parents, \ > + .flags = _flags, \ > + }; > + > /** > * __clk_init - initialize the data structures in a struct clk > * @dev: device initializing this clk, placeholder for now > diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h > index 09dea1f..853d526 100644 > --- a/include/linux/clk-provider.h > +++ b/include/linux/clk-provider.h > @@ -129,6 +129,133 @@ struct clk_ops { > void (*init)(struct clk_hw *hw); > }; > > +/* > + * DOC: Basic clock implementations common to many platforms > + * > + * Each basic clock hardware type is comprised of a structure describing the > + * clock hardware, implementations of the relevant callbacks in struct > clk_ops, > + * unique flags for that hardware type, a registration function and an > + * alternative macro for static initialization > + */ > + > +/** > + * struct clk_fixed_rate - fixed-rate clock > + * @hw: handle between common and hardware-specific interfaces > + * @fixed_rate: constant frequency of clock > + */ > +struct clk_fixed_rate { > + struct clk_hw hw; > + unsigned long fixed_rate; > + u8 flags; > +}; > + > +struct clk *clk_register_fixed_rate(struct device *dev, const char *name, > + const char *parent_name, unsigned long flags, > + unsigned long fixed_rate); > + > +/** > + * struct clk_gate - gating clock > + * > + * @hw: handle between common and hardware-specific interfaces > + * @reg: register controlling gate > + * @bit_idx: single bit controlling gate > + * @flags: hardware-specific flags > + * @lock: register lock > + * > + * Clock which can gate its output. Implements .enable & .disable > + * > + * Flags: > + * CLK_GATE_SET_DISABLE - by default this clock sets the bit at bit_idx to > + * enable the clock. Setting this flag does the opposite: setting the bit > + * disable the clock and clearing it enables the clock > + */ > +struct clk_gate { > + struct clk_hw hw; > + void __iomem *reg; > + u8 bit_idx; > + u8 flags; > + spinlock_t *lock; > + char *parent[1]; > +}; > + > +#define CLK_GATE_SET_TO_DISABLE BIT(0) > + > +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_divider - adjustable divider clock > + * > + * @hw: handle between common and hardware-specific interfaces > + * @reg: register containing the divider > + * @shift: shift to the divider bit field > + * @width: width of the divider bit field > + * @lock: register lock > + * > + * Clock with an adjustable divider affecting its output frequency. > Implements > + * .recalc_rate, .set_rate and .round_rate > + * > + * Flags: > + * CLK_DIVIDER_ONE_BASED - by default the divisor is the value read from the > + * register plus one. If CLK_DIVIDER_ONE_BASED is set then the divider is > + * the raw value read from the register, with the value of zero considered > + * invalid > + * CLK_DIVIDER_POWER_OF_TWO - clock divisor is 2 raised to the value read > from > + * the hardware register > + */ > +struct clk_divider { > + struct clk_hw hw; > + void __iomem *reg; > + u8 shift; > + u8 width; > + u8 flags; > + spinlock_t *lock; > + char *parent[1]; > +}; > + > +#define CLK_DIVIDER_ONE_BASED BIT(0) > +#define CLK_DIVIDER_POWER_OF_TWO BIT(1) > + > +struct clk *clk_register_divider(struct device *dev, const char *name, > + const char *parent_name, unsigned long flags, > + void __iomem *reg, u8 shift, u8 width, > + u8 clk_divider_flags, spinlock_t *lock); > + > +/** > + * struct clk_mux - multiplexer clock > + * > + * @hw: handle between common and hardware-specific interfaces > + * @reg: register controlling multiplexer > + * @shift: shift to multiplexer bit field > + * @width: width of mutliplexer bit field > + * @num_clks: number of parent clocks > + * @lock: register lock > + * > + * Clock with multiple selectable parents. Implements .get_parent, > .set_parent > + * and .recalc_rate > + * > + * Flags: > + * CLK_MUX_INDEX_ONE - register index starts at 1, not 0 > + * CLK_MUX_INDEX_BITWISE - register index is a single bit (power of two) > + */ > +struct clk_mux { > + struct clk_hw hw; > + void __iomem *reg; > + u8 shift; > + u8 width; > + u8 flags; > + spinlock_t *lock; > +}; > + > +#define CLK_MUX_INDEX_ONE BIT(0) > +#define CLK_MUX_INDEX_BIT BIT(1) > + > +struct clk *clk_register_mux(struct device *dev, const char *name, > + char **parent_names, u8 num_parents, unsigned long flags, > + void __iomem *reg, u8 shift, u8 width, > + u8 clk_mux_flags, spinlock_t *lock); > > /** > * clk_register - allocate a new clock, register it and return an opaque > cookie _______________________________________________ linaro-dev mailing list linaro-dev@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-dev