[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAJOA=zOS44YRfg3A7D8G+jWoQ_gzRQ937A7tYDU+DxWce_khzQ@mail.gmail.com>
Date: Mon, 12 Mar 2012 13:58:11 -0700
From: "Turquette, Mike" <mturquette@...com>
To: Rob Herring <robherring2@...il.com>
Cc: Andrew Lunn <andrew@...n.ch>, Paul Walmsley <paul@...an.com>,
linaro-dev@...ts.linaro.org,
Linus Walleij <linus.walleij@...ricsson.com>,
patches@...aro.org, Stephen Boyd <sboyd@...eaurora.org>,
Sascha Hauer <s.hauer@...gutronix.de>,
Mark Brown <broonie@...nsource.wolfsonmicro.com>,
Magnus Damm <magnus.damm@...il.com>,
linux-kernel@...r.kernel.org,
Rob Herring <rob.herring@...xeda.com>,
Saravana Kannan <skannan@...eaurora.org>,
Thomas Gleixner <tglx@...utronix.de>,
Jamie Iles <jamie@...ieiles.com>,
Russell King <linux@....linux.org.uk>,
Jeremy Kerr <jeremy.kerr@...onical.com>,
Arnd Bergman <arnd.bergmann@...aro.org>,
linux-arm-kernel@...ts.infradead.org
Subject: Re: [PATCH v6 3/3] clk: basic clock hardware types
On Mon, Mar 12, 2012 at 1:18 PM, Rob Herring <robherring2@...il.com> wrote:
> 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 <mturquette@...aro.org>
>> Signed-off-by: Mike Turquette <mturquette@...com>
>> Cc: Russell King <linux@....linux.org.uk>
>> Cc: Jeremy Kerr <jeremy.kerr@...onical.com>
>> Cc: Thomas Gleixner <tglx@...utronix.de>
>> Cc: Arnd Bergman <arnd.bergmann@...aro.org>
>> Cc: Paul Walmsley <paul@...an.com>
>> Cc: Shawn Guo <shawn.guo@...escale.com>
>> Cc: Sascha Hauer <s.hauer@...gutronix.de>
>> Cc: Jamie Iles <jamie@...ieiles.com>
>> Cc: Richard Zhao <richard.zhao@...aro.org>
>> Cc: Saravana Kannan <skannan@...eaurora.org>
>> Cc: Magnus Damm <magnus.damm@...il.com>
>> Cc: Rob Herring <rob.herring@...xeda.com>
>> Cc: Mark Brown <broonie@...nsource.wolfsonmicro.com>
>> Cc: Linus Walleij <linus.walleij@...ricsson.com>
>> Cc: Stephen Boyd <sboyd@...eaurora.org>
>> Cc: Amit Kucheria <amit.kucheria@...aro.org>
>> Cc: Deepak Saxena <dsaxena@...aro.org>
>> Cc: Grant Likely <grant.likely@...retlab.ca>
>> Cc: Andrew Lunn <andrew@...n.ch>
>
> One issue with spinlocks below, but otherwise:
>
> Reviewed-by: Rob Herring <rob.herring@...xeda.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.hauer@...gutronix.de>
>> + * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@...aro.org>
>> + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@...aro.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.
Hi Rob,
These register-level locks originally came in from the divider &
multiplexer patches from Richard and Sascha and I'm sure they can give
you more details than I.
Basically on their platform they have some 32-bits regs that have a
lot of overlapping clock functions in them, such as enable/disable and
adjusting a divider all in one reg. Those operations are protected by
different locks (enable spinlock and prepare mutex, respectively) so
some synchronization mechanism is necessary. On OMAP we don't use
this since we have a billion registers that typically only map to one
clock each. I also wonder if having device type memory for the
affected regions makes a this irrelevant on ARM... but that wouldn't
matter for non-ARM architectures. Just a thought.
Regards,
Mike
>
>> +
>> + 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.kerr@...onical.com>
>> + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@...aro.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.kerr@...onical.com>
>> + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@...aro.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.hauer@...gutronix.de>
>> + * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@...aro.org>
>> + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@...aro.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@...ts.linaro.org
> http://lists.linaro.org/mailman/listinfo/linaro-dev
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists