lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <4F5E5A0C.6080604@gmail.com>
Date:	Mon, 12 Mar 2012 15:18:20 -0500
From:	Rob Herring <robherring2@...il.com>
To:	Mike Turquette <mturquette@...aro.org>
CC:	Russell King <linux@....linux.org.uk>,
	Andrew Lunn <andrew@...n.ch>, linaro-dev@...ts.linaro.org,
	Saravana Kannan <skannan@...eaurora.org>,
	Jamie Iles <jamie@...ieiles.com>,
	Jeremy Kerr <jeremy.kerr@...onical.com>,
	Magnus Damm <magnus.damm@...il.com>,
	linux-arm-kernel@...ts.infradead.org,
	Arnd Bergman <arnd.bergmann@...aro.org>, patches@...aro.org,
	Sascha Hauer <s.hauer@...gutronix.de>,
	Rob Herring <rob.herring@...xeda.com>,
	Thomas Gleixner <tglx@...utronix.de>,
	Paul Walmsley <paul@...an.com>,
	Linus Walleij <linus.walleij@...ricsson.com>,
	Mark Brown <broonie@...nsource.wolfsonmicro.com>,
	Stephen Boyd <sboyd@...eaurora.org>,
	linux-kernel@...r.kernel.org
Subject: Re: [PATCH v6 3/3] clk: basic clock hardware types

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.

> +
> +	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

--
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

Powered by Openwall GNU/*/Linux Powered by OpenVZ