Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 342450
b: refs/heads/master
c: 32cc002
h: refs/heads/master
v: v3
  • Loading branch information
Mike Turquette authored and Paul Walmsley committed Nov 12, 2012
1 parent bb3f883 commit 01a1bbd
Show file tree
Hide file tree
Showing 8 changed files with 869 additions and 20 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: f5dd3bb53ca45f3b47c6889e5920c562f5a37359
refs/heads/master: 32cc002116b866151ca24c6e9110ba8a93754753
184 changes: 179 additions & 5 deletions trunk/arch/arm/mach-omap2/clkt_clksel.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@

#include <linux/kernel.h>
#include <linux/errno.h>
#ifdef CONFIG_COMMON_CLK
#include <linux/clk-provider.h>
#else
#include <linux/clk.h>
#endif
#include <linux/io.h>
#include <linux/bug.h>

Expand All @@ -58,19 +62,30 @@
* the element associated with the supplied parent clock address.
* Returns a pointer to the struct clksel on success or NULL on error.
*/
#ifdef CONFIG_COMMON_CLK
static const struct clksel *_get_clksel_by_parent(struct clk_hw_omap *clk,
#else
static const struct clksel *_get_clksel_by_parent(struct clk *clk,
#endif
struct clk *src_clk)
{
const struct clksel *clks;

if (!src_clk)
return NULL;

for (clks = clk->clksel; clks->parent; clks++)
if (clks->parent == src_clk)
break; /* Found the requested parent */

if (!clks->parent) {
/* This indicates a data problem */
WARN(1, "clock: %s: could not find parent clock %s in clksel array\n",
#ifdef CONFIG_COMMON_CLK
__clk_get_name(clk->hw.clk), __clk_get_name(src_clk));
#else
__clk_get_name(clk), __clk_get_name(src_clk));
#endif
return NULL;
}

Expand All @@ -92,6 +107,7 @@ static const struct clksel *_get_clksel_by_parent(struct clk *clk,
* success (in this latter case, the corresponding register bitfield
* value is passed back in the variable pointed to by @field_val)
*/
#ifndef CONFIG_COMMON_CLK
static u8 _get_div_and_fieldval(struct clk *src_clk, struct clk *clk,
u32 *field_val)
{
Expand Down Expand Up @@ -134,6 +150,7 @@ static u8 _get_div_and_fieldval(struct clk *src_clk, struct clk *clk,

return max_div;
}
#endif

/**
* _write_clksel_reg() - program a clock's clksel register in hardware
Expand All @@ -148,7 +165,11 @@ static u8 _get_div_and_fieldval(struct clk *src_clk, struct clk *clk,
* take into account any time the hardware might take to switch the
* clock source.
*/
#ifdef CONFIG_COMMON_CLK
static void _write_clksel_reg(struct clk_hw_omap *clk, u32 field_val)
#else
static void _write_clksel_reg(struct clk *clk, u32 field_val)
#endif
{
u32 v;

Expand All @@ -171,13 +192,22 @@ static void _write_clksel_reg(struct clk *clk, u32 field_val)
* before calling. Returns 0 on error or returns the actual integer divisor
* upon success.
*/
#ifdef CONFIG_COMMON_CLK
static u32 _clksel_to_divisor(struct clk_hw_omap *clk, u32 field_val)
#else
static u32 _clksel_to_divisor(struct clk *clk, u32 field_val)
#endif
{
const struct clksel *clks;
const struct clksel_rate *clkr;
struct clk *parent;

#ifdef CONFIG_COMMON_CLK
parent = __clk_get_parent(clk->hw.clk);
#else
parent = __clk_get_parent(clk);
#endif

clks = _get_clksel_by_parent(clk, parent);
if (!clks)
return 0;
Expand All @@ -193,7 +223,12 @@ static u32 _clksel_to_divisor(struct clk *clk, u32 field_val)
if (!clkr->div) {
/* This indicates a data error */
WARN(1, "clock: %s: could not find fieldval %d for parent %s\n",
#ifdef CONFIG_COMMON_CLK
__clk_get_name(clk->hw.clk), field_val,
__clk_get_name(parent));
#else
__clk_get_name(clk), field_val, __clk_get_name(parent));
#endif
return 0;
}

Expand All @@ -210,7 +245,11 @@ static u32 _clksel_to_divisor(struct clk *clk, u32 field_val)
* register field value _before_ left-shifting (i.e., LSB is at bit
* 0); or returns 0xFFFFFFFF (~0) upon error.
*/
#ifdef CONFIG_COMMON_CLK
static u32 _divisor_to_clksel(struct clk_hw_omap *clk, u32 div)
#else
static u32 _divisor_to_clksel(struct clk *clk, u32 div)
#endif
{
const struct clksel *clks;
const struct clksel_rate *clkr;
Expand All @@ -219,7 +258,11 @@ static u32 _divisor_to_clksel(struct clk *clk, u32 div)
/* should never happen */
WARN_ON(div == 0);

#ifdef CONFIG_COMMON_CLK
parent = __clk_get_parent(clk->hw.clk);
#else
parent = __clk_get_parent(clk);
#endif
clks = _get_clksel_by_parent(clk, parent);
if (!clks)
return ~0;
Expand All @@ -234,7 +277,12 @@ static u32 _divisor_to_clksel(struct clk *clk, u32 div)

if (!clkr->div) {
pr_err("clock: %s: could not find divisor %d for parent %s\n",
#ifdef CONFIG_COMMON_CLK
__clk_get_name(clk->hw.clk), div,
__clk_get_name(parent));
#else
__clk_get_name(clk), div, __clk_get_name(parent));
#endif
return ~0;
}

Expand All @@ -249,7 +297,11 @@ static u32 _divisor_to_clksel(struct clk *clk, u32 div)
* into the hardware, convert it into the actual divisor value, and
* return it; or return 0 on error.
*/
#ifdef CONFIG_COMMON_CLK
static u32 _read_divisor(struct clk_hw_omap *clk)
#else
static u32 _read_divisor(struct clk *clk)
#endif
{
u32 v;

Expand Down Expand Up @@ -277,7 +329,12 @@ static u32 _read_divisor(struct clk *clk)
*
* Returns the rounded clock rate or returns 0xffffffff on error.
*/
#ifdef CONFIG_COMMON_CLK
u32 omap2_clksel_round_rate_div(struct clk_hw_omap *clk,
unsigned long target_rate,
#else
u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate,
#endif
u32 *new_div)
{
unsigned long test_rate;
Expand All @@ -288,9 +345,14 @@ u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate,
unsigned long parent_rate;
const char *clk_name;

#ifdef CONFIG_COMMON_CLK
parent = __clk_get_parent(clk->hw.clk);
clk_name = __clk_get_name(clk->hw.clk);
#else
parent = __clk_get_parent(clk);
parent_rate = __clk_get_rate(parent);
clk_name = __clk_get_name(clk);
#endif
parent_rate = __clk_get_rate(parent);

if (!clk->clksel || !clk->clksel_mask)
return ~0;
Expand Down Expand Up @@ -340,6 +402,63 @@ u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate,
* (i.e., those used in struct clk field function pointers, etc.)
*/

#ifdef CONFIG_COMMON_CLK
/**
* omap2_clksel_find_parent_index() - return the array index of the current
* hardware parent of @hw
* @hw: struct clk_hw * to find the current hardware parent of
*
* Given a struct clk_hw pointer @hw to the 'hw' member of a struct
* clk_hw_omap record representing a source-selectable hardware clock,
* read the hardware register and determine what its parent is
* currently set to. Intended to be called only by the common clock
* framework struct clk_hw_ops.get_parent function pointer. Return
* the array index of this parent clock upon success -- there is no
* way to return an error, so if we encounter an error, just WARN()
* and pretend that we know that we're doing.
*/
u8 omap2_clksel_find_parent_index(struct clk_hw *hw)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
const struct clksel *clks;
const struct clksel_rate *clkr;
u32 r, found = 0;
struct clk *parent;
const char *clk_name;
int ret = 0, f = 0;

parent = __clk_get_parent(hw->clk);
clk_name = __clk_get_name(hw->clk);

/* XXX should be able to return an error */
WARN((!clk->clksel || !clk->clksel_mask),
"clock: %s: attempt to call on a non-clksel clock", clk_name);

r = __raw_readl(clk->clksel_reg) & clk->clksel_mask;
r >>= __ffs(clk->clksel_mask);

for (clks = clk->clksel; clks->parent && !found; clks++) {
for (clkr = clks->rates; clkr->div && !found; clkr++) {
if (!(clkr->flags & cpu_mask))
continue;

if (clkr->val == r) {
found = 1;
ret = f;
}
}
f++;
}

/* This indicates a data error */
WARN(!found, "clock: %s: init parent: could not find regval %0x\n",
clk_name, r);

return ret;
}

#else

/**
* omap2_init_clksel_parent() - set a clksel clk's parent field from the hdwr
* @clk: OMAP clock struct ptr to use
Expand Down Expand Up @@ -393,6 +512,8 @@ void omap2_init_clksel_parent(struct clk *clk)
return;
}

#endif

/**
* omap2_clksel_recalc() - function ptr to pass via struct clk .recalc field
* @clk: struct clk *
Expand All @@ -402,6 +523,28 @@ void omap2_init_clksel_parent(struct clk *clk)
* function. Returns the clock's current rate, based on its parent's rate
* and its current divisor setting in the hardware.
*/
#ifdef CONFIG_COMMON_CLK
unsigned long omap2_clksel_recalc(struct clk_hw *hw, unsigned long parent_rate)
{
unsigned long rate;
u32 div = 0;
struct clk_hw_omap *clk = to_clk_hw_omap(hw);

if (!parent_rate)
return 0;

div = _read_divisor(clk);
if (!div)
rate = parent_rate;
else
rate = parent_rate / div;

pr_debug("%s: recalc'd %s's rate to %lu (div %d)\n", __func__,
__clk_get_name(hw->clk), rate, div);

return rate;
}
#else
unsigned long omap2_clksel_recalc(struct clk *clk)
{
unsigned long rate;
Expand All @@ -420,6 +563,7 @@ unsigned long omap2_clksel_recalc(struct clk *clk)

return rate;
}
#endif

/**
* omap2_clksel_round_rate() - find rounded rate for the given clock and rate
Expand All @@ -432,8 +576,15 @@ unsigned long omap2_clksel_recalc(struct clk *clk)
*
* Returns the rounded clock rate or returns 0xffffffff on error.
*/
#ifdef CONFIG_COMMON_CLK
long omap2_clksel_round_rate(struct clk_hw *hw, unsigned long target_rate,
unsigned long *parent_rate)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
#else
long omap2_clksel_round_rate(struct clk *clk, unsigned long target_rate)
{
#endif
u32 new_div;

return omap2_clksel_round_rate_div(clk, target_rate, &new_div);
Expand All @@ -454,8 +605,15 @@ long omap2_clksel_round_rate(struct clk *clk, unsigned long target_rate)
* is changed, they will all be affected without any notification.
* Returns -EINVAL upon error, or 0 upon success.
*/
#ifdef CONFIG_COMMON_CLK
int omap2_clksel_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
#else
int omap2_clksel_set_rate(struct clk *clk, unsigned long rate)
{
#endif
u32 field_val, validrate, new_div = 0;

if (!clk->clksel || !clk->clksel_mask)
Expand All @@ -471,11 +629,16 @@ int omap2_clksel_set_rate(struct clk *clk, unsigned long rate)

_write_clksel_reg(clk, field_val);

clk->rate = __clk_get_rate(__clk_get_parent(clk)) / new_div;

#ifdef CONFIG_COMMON_CLK
pr_debug("clock: %s: set rate to %ld\n", __clk_get_name(hw->clk),
__clk_get_rate(hw->clk));
#else
pr_debug("clock: %s: set rate to %ld\n", __clk_get_name(clk),
__clk_get_rate(clk));

clk->rate = __clk_get_rate(__clk_get_parent(clk)) / new_div;
#endif

return 0;
}

Expand All @@ -499,6 +662,18 @@ int omap2_clksel_set_rate(struct clk *clk, unsigned long rate)
* affected without any notification. Returns -EINVAL upon error, or
* 0 upon success.
*/
#ifdef CONFIG_COMMON_CLK
int omap2_clksel_set_parent(struct clk_hw *hw, u8 field_val)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);

if (!clk->clksel || !clk->clksel_mask)
return -EINVAL;

_write_clksel_reg(clk, field_val);
return 0;
}
#else
int omap2_clksel_set_parent(struct clk *clk, struct clk *new_parent)
{
u32 field_val = 0;
Expand All @@ -510,7 +685,6 @@ int omap2_clksel_set_parent(struct clk *clk, struct clk *new_parent)
parent_div = _get_div_and_fieldval(new_parent, clk, &field_val);
if (!parent_div)
return -EINVAL;

_write_clksel_reg(clk, field_val);

clk_reparent(clk, new_parent);
Expand All @@ -520,11 +694,11 @@ int omap2_clksel_set_parent(struct clk *clk, struct clk *new_parent)

if (parent_div > 0)
__clk_get_rate(clk) /= parent_div;

pr_debug("clock: %s: set parent to %s (new rate %ld)\n",
__clk_get_name(clk),
__clk_get_name(__clk_get_parent(clk)),
__clk_get_rate(clk));

return 0;
}
#endif
Loading

0 comments on commit 01a1bbd

Please sign in to comment.