summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2/clkt_clksel.c
diff options
context:
space:
mode:
authorMike Turquette <mturquette@linaro.org>2012-11-11 00:58:41 +0100
committerPaul Walmsley <paul@pwsan.com>2012-11-12 21:55:50 +0100
commit32cc002116b866151ca24c6e9110ba8a93754753 (patch)
tree4a7d3f08bf774ed72d37bd0de541d9dac5e3083e /arch/arm/mach-omap2/clkt_clksel.c
parentARM: OMAP: hwmod: Fix up hwmod based clkdm accesses (diff)
downloadlinux-32cc002116b866151ca24c6e9110ba8a93754753.tar.xz
linux-32cc002116b866151ca24c6e9110ba8a93754753.zip
ARM: OMAP4: clock: Convert to common clk
Convert all OMAP4 specific platform files to use COMMON clk and keep all the changes under the CONFIG_COMMON_CLK macro check so it does not break any existing code. At a later point switch to COMMON clk and get rid of all old/legacy code. This converts all apis which will be called directly from COMMON clk to take a struct clk_hw parameter, and all the internal platform apis to take a struct clk_hw_omap parameter. Changes are based off the original patch from Mike Turquette. Signed-off-by: Rajendra Nayak <rnayak@ti.com> [paul@pwsan.com: created new omap2_clksel_find_parent_index() rather than modifying omap2_init_clksel_parent(); moved clkhwops_iclk_wait to clkt_iclk.c to fix OMAP4-only builds; added clk-provider.h include to clock.h to try to fix some 3430-builds] [mturquette@ti.com: squash patch for omap2_clkops_{en,dis}able_clkdm; omap2_dflt_clk_is_enabled should not enable clocks] Signed-off-by: Mike Turquette <mturquette@ti.com> [paul@pwsan.com: fix compiler warning; update to apply; added kerneldoc on non-trivial new functions; added the dpll3xxx clockdomain modifications] Signed-off-by: Paul Walmsley <paul@pwsan.com>
Diffstat (limited to 'arch/arm/mach-omap2/clkt_clksel.c')
-rw-r--r--arch/arm/mach-omap2/clkt_clksel.c184
1 files changed, 179 insertions, 5 deletions
diff --git a/arch/arm/mach-omap2/clkt_clksel.c b/arch/arm/mach-omap2/clkt_clksel.c
index 53646facda45..03ceb2efd532 100644
--- a/arch/arm/mach-omap2/clkt_clksel.c
+++ b/arch/arm/mach-omap2/clkt_clksel.c
@@ -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>
@@ -58,11 +62,18 @@
* 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 */
@@ -70,7 +81,11 @@ static const struct clksel *_get_clksel_by_parent(struct clk *clk,
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;
}
@@ -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)
{
@@ -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
@@ -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;
@@ -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;
@@ -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;
}
@@ -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;
@@ -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;
@@ -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;
}
@@ -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;
@@ -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;
@@ -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;
@@ -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
@@ -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 *
@@ -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;
@@ -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
@@ -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);
@@ -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)
@@ -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;
}
@@ -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;
@@ -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);
@@ -520,7 +694,6 @@ 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)),
@@ -528,3 +701,4 @@ int omap2_clksel_set_parent(struct clk *clk, struct clk *new_parent)
return 0;
}
+#endif