-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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@linaro.org> Signed-off-by: Mike Turquette <mturquette@ti.com> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Tested-by: Andrew Lunn <andrew@lunn.ch> Reviewed-by: Rob Herring <rob.herring@calxeda.com> Cc: Russell King <linux@arm.linux.org.uk> Cc: Jeremy Kerr <jeremy.kerr@canonical.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Arnd Bergman <arnd.bergmann@linaro.org> Cc: Paul Walmsley <paul@pwsan.com> Cc: Shawn Guo <shawn.guo@freescale.com> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Jamie Iles <jamie@jamieiles.com> Cc: Richard Zhao <richard.zhao@linaro.org> Cc: Saravana Kannan <skannan@codeaurora.org> Cc: Magnus Damm <magnus.damm@gmail.com> Cc: Mark Brown <broonie@opensource.wolfsonmicro.com> Cc: Linus Walleij <linus.walleij@stericsson.com> Cc: Stephen Boyd <sboyd@codeaurora.org> Cc: Amit Kucheria <amit.kucheria@linaro.org> Cc: Deepak Saxena <dsaxena@linaro.org> Cc: Grant Likely <grant.likely@secretlab.ca> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
- Loading branch information
Mike Turquette
authored and
Arnd Bergmann
committed
Mar 16, 2012
1 parent
b247649
commit 9d9f78e
Showing
7 changed files
with
801 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
/* | ||
* Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> | ||
* Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org> | ||
* Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@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) | ||
|
||
#define div_mask(d) ((1 << (d->width)) - 1) | ||
|
||
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; | ||
|
||
div = readl(divider->reg) >> divider->shift; | ||
div &= div_mask(divider); | ||
|
||
if (!(divider->flags & CLK_DIVIDER_ONE_BASED)) | ||
div++; | ||
|
||
return parent_rate / div; | ||
} | ||
EXPORT_SYMBOL_GPL(clk_divider_recalc_rate); | ||
|
||
/* | ||
* The reverse of DIV_ROUND_UP: The maximum number which | ||
* divided by m is r | ||
*/ | ||
#define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 1) | ||
|
||
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; | ||
|
||
if (!rate) | ||
rate = 1; | ||
|
||
maxdiv = (1 << divider->width); | ||
|
||
if (divider->flags & CLK_DIVIDER_ONE_BASED) | ||
maxdiv--; | ||
|
||
if (!best_parent_rate) { | ||
parent_rate = __clk_get_rate(__clk_get_parent(hw->clk)); | ||
bestdiv = DIV_ROUND_UP(parent_rate, rate); | ||
bestdiv = bestdiv == 0 ? 1 : bestdiv; | ||
bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; | ||
return bestdiv; | ||
} | ||
|
||
/* | ||
* 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++) { | ||
parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), | ||
MULT_ROUND_UP(rate, i)); | ||
now = parent_rate / i; | ||
if (now <= rate && now > best) { | ||
bestdiv = i; | ||
best = now; | ||
*best_parent_rate = parent_rate; | ||
} | ||
} | ||
|
||
if (!bestdiv) { | ||
bestdiv = (1 << divider->width); | ||
if (divider->flags & CLK_DIVIDER_ONE_BASED) | ||
bestdiv--; | ||
*best_parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), 1); | ||
} | ||
|
||
return bestdiv; | ||
} | ||
|
||
static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, | ||
unsigned long *prate) | ||
{ | ||
int div; | ||
div = clk_divider_bestdiv(hw, rate, prate); | ||
|
||
if (prate) | ||
return *prate / div; | ||
else { | ||
unsigned long r; | ||
r = __clk_get_rate(__clk_get_parent(hw->clk)); | ||
return r / div; | ||
} | ||
} | ||
EXPORT_SYMBOL_GPL(clk_divider_round_rate); | ||
|
||
static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate) | ||
{ | ||
struct clk_divider *divider = to_clk_divider(hw); | ||
unsigned int div; | ||
unsigned long flags = 0; | ||
u32 val; | ||
|
||
div = __clk_get_rate(__clk_get_parent(hw->clk)) / rate; | ||
|
||
if (!(divider->flags & CLK_DIVIDER_ONE_BASED)) | ||
div--; | ||
|
||
if (div > div_mask(divider)) | ||
div = div_mask(divider); | ||
|
||
if (divider->lock) | ||
spin_lock_irqsave(divider->lock, flags); | ||
|
||
val = readl(divider->reg); | ||
val &= ~(div_mask(divider) << 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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
/* | ||
* Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com> | ||
* Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@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); | ||
} |
Oops, something went wrong.