Merge branch 'omap-clks3' into devel
Conflicts: arch/arm/mach-omap2/clock.c
This commit is contained in:
@@ -26,11 +26,10 @@
|
||||
|
||||
#include <mach/clock.h>
|
||||
#include <mach/clockdomain.h>
|
||||
#include <mach/sram.h>
|
||||
#include <mach/cpu.h>
|
||||
#include <asm/div64.h>
|
||||
|
||||
#include "memory.h"
|
||||
#include <mach/sdrc.h>
|
||||
#include "sdrc.h"
|
||||
#include "clock.h"
|
||||
#include "prm.h"
|
||||
@@ -46,7 +45,7 @@
|
||||
#define DPLL_MIN_DIVIDER 1
|
||||
|
||||
/* Possible error results from _dpll_test_mult */
|
||||
#define DPLL_MULT_UNDERFLOW (1 << 0)
|
||||
#define DPLL_MULT_UNDERFLOW -1
|
||||
|
||||
/*
|
||||
* Scale factor to mitigate roundoff errors in DPLL rate rounding.
|
||||
@@ -59,12 +58,90 @@
|
||||
#define DPLL_ROUNDING_VAL ((DPLL_SCALE_BASE / 2) * \
|
||||
(DPLL_SCALE_FACTOR / DPLL_SCALE_BASE))
|
||||
|
||||
/* DPLL valid Fint frequency band limits - from 34xx TRM Section 4.7.6.2 */
|
||||
#define DPLL_FINT_BAND1_MIN 750000
|
||||
#define DPLL_FINT_BAND1_MAX 2100000
|
||||
#define DPLL_FINT_BAND2_MIN 7500000
|
||||
#define DPLL_FINT_BAND2_MAX 21000000
|
||||
|
||||
/* _dpll_test_fint() return codes */
|
||||
#define DPLL_FINT_UNDERFLOW -1
|
||||
#define DPLL_FINT_INVALID -2
|
||||
|
||||
u8 cpu_mask;
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* OMAP2/3 specific clock functions
|
||||
*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* _omap2xxx_clk_commit - commit clock parent/rate changes in hardware
|
||||
* @clk: struct clk *
|
||||
*
|
||||
* If @clk has the DELAYED_APP flag set, meaning that parent/rate changes
|
||||
* don't take effect until the VALID_CONFIG bit is written, write the
|
||||
* VALID_CONFIG bit and wait for the write to complete. No return value.
|
||||
*/
|
||||
static void _omap2xxx_clk_commit(struct clk *clk)
|
||||
{
|
||||
if (!cpu_is_omap24xx())
|
||||
return;
|
||||
|
||||
if (!(clk->flags & DELAYED_APP))
|
||||
return;
|
||||
|
||||
prm_write_mod_reg(OMAP24XX_VALID_CONFIG, OMAP24XX_GR_MOD,
|
||||
OMAP24XX_PRCM_CLKCFG_CTRL_OFFSET);
|
||||
/* OCP barrier */
|
||||
prm_read_mod_reg(OMAP24XX_GR_MOD, OMAP24XX_PRCM_CLKCFG_CTRL_OFFSET);
|
||||
}
|
||||
|
||||
/*
|
||||
* _dpll_test_fint - test whether an Fint value is valid for the DPLL
|
||||
* @clk: DPLL struct clk to test
|
||||
* @n: divider value (N) to test
|
||||
*
|
||||
* Tests whether a particular divider @n will result in a valid DPLL
|
||||
* internal clock frequency Fint. See the 34xx TRM 4.7.6.2 "DPLL Jitter
|
||||
* Correction". Returns 0 if OK, -1 if the enclosing loop can terminate
|
||||
* (assuming that it is counting N upwards), or -2 if the enclosing loop
|
||||
* should skip to the next iteration (again assuming N is increasing).
|
||||
*/
|
||||
static int _dpll_test_fint(struct clk *clk, u8 n)
|
||||
{
|
||||
struct dpll_data *dd;
|
||||
long fint;
|
||||
int ret = 0;
|
||||
|
||||
dd = clk->dpll_data;
|
||||
|
||||
/* DPLL divider must result in a valid jitter correction val */
|
||||
fint = clk->parent->rate / (n + 1);
|
||||
if (fint < DPLL_FINT_BAND1_MIN) {
|
||||
|
||||
pr_debug("rejecting n=%d due to Fint failure, "
|
||||
"lowering max_divider\n", n);
|
||||
dd->max_divider = n;
|
||||
ret = DPLL_FINT_UNDERFLOW;
|
||||
|
||||
} else if (fint > DPLL_FINT_BAND1_MAX &&
|
||||
fint < DPLL_FINT_BAND2_MIN) {
|
||||
|
||||
pr_debug("rejecting n=%d due to Fint failure\n", n);
|
||||
ret = DPLL_FINT_INVALID;
|
||||
|
||||
} else if (fint > DPLL_FINT_BAND2_MAX) {
|
||||
|
||||
pr_debug("rejecting n=%d due to Fint failure, "
|
||||
"boosting min_divider\n", n);
|
||||
dd->min_divider = n;
|
||||
ret = DPLL_FINT_INVALID;
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap2_init_clk_clkdm - look up a clockdomain name, store pointer in clk
|
||||
* @clk: OMAP clock struct ptr to use
|
||||
@@ -120,7 +197,7 @@ void omap2_init_clksel_parent(struct clk *clk)
|
||||
clk->name, clks->parent->name,
|
||||
((clk->parent) ?
|
||||
clk->parent->name : "NULL"));
|
||||
clk->parent = clks->parent;
|
||||
clk_reparent(clk, clks->parent);
|
||||
};
|
||||
found = 1;
|
||||
}
|
||||
@@ -134,25 +211,52 @@ void omap2_init_clksel_parent(struct clk *clk)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Returns the DPLL rate */
|
||||
/**
|
||||
* omap2_get_dpll_rate - returns the current DPLL CLKOUT rate
|
||||
* @clk: struct clk * of a DPLL
|
||||
*
|
||||
* DPLLs can be locked or bypassed - basically, enabled or disabled.
|
||||
* When locked, the DPLL output depends on the M and N values. When
|
||||
* bypassed, on OMAP2xxx, the output rate is either the 32KiHz clock
|
||||
* or sys_clk. Bypass rates on OMAP3 depend on the DPLL: DPLLs 1 and
|
||||
* 2 are bypassed with dpll1_fclk and dpll2_fclk respectively
|
||||
* (generated by DPLL3), while DPLL 3, 4, and 5 bypass rates are sys_clk.
|
||||
* Returns the current DPLL CLKOUT rate (*not* CLKOUTX2) if the DPLL is
|
||||
* locked, or the appropriate bypass rate if the DPLL is bypassed, or 0
|
||||
* if the clock @clk is not a DPLL.
|
||||
*/
|
||||
u32 omap2_get_dpll_rate(struct clk *clk)
|
||||
{
|
||||
long long dpll_clk;
|
||||
u32 dpll_mult, dpll_div, dpll;
|
||||
u32 dpll_mult, dpll_div, v;
|
||||
struct dpll_data *dd;
|
||||
|
||||
dd = clk->dpll_data;
|
||||
/* REVISIT: What do we return on error? */
|
||||
if (!dd)
|
||||
return 0;
|
||||
|
||||
dpll = __raw_readl(dd->mult_div1_reg);
|
||||
dpll_mult = dpll & dd->mult_mask;
|
||||
/* Return bypass rate if DPLL is bypassed */
|
||||
v = __raw_readl(dd->control_reg);
|
||||
v &= dd->enable_mask;
|
||||
v >>= __ffs(dd->enable_mask);
|
||||
|
||||
if (cpu_is_omap24xx()) {
|
||||
if (v == OMAP2XXX_EN_DPLL_LPBYPASS ||
|
||||
v == OMAP2XXX_EN_DPLL_FRBYPASS)
|
||||
return dd->clk_bypass->rate;
|
||||
} else if (cpu_is_omap34xx()) {
|
||||
if (v == OMAP3XXX_EN_DPLL_LPBYPASS ||
|
||||
v == OMAP3XXX_EN_DPLL_FRBYPASS)
|
||||
return dd->clk_bypass->rate;
|
||||
}
|
||||
|
||||
v = __raw_readl(dd->mult_div1_reg);
|
||||
dpll_mult = v & dd->mult_mask;
|
||||
dpll_mult >>= __ffs(dd->mult_mask);
|
||||
dpll_div = dpll & dd->div1_mask;
|
||||
dpll_div = v & dd->div1_mask;
|
||||
dpll_div >>= __ffs(dd->div1_mask);
|
||||
|
||||
dpll_clk = (long long)clk->parent->rate * dpll_mult;
|
||||
dpll_clk = (long long)dd->clk_ref->rate * dpll_mult;
|
||||
do_div(dpll_clk, dpll_div + 1);
|
||||
|
||||
return dpll_clk;
|
||||
@@ -162,14 +266,11 @@ u32 omap2_get_dpll_rate(struct clk *clk)
|
||||
* Used for clocks that have the same value as the parent clock,
|
||||
* divided by some factor
|
||||
*/
|
||||
void omap2_fixed_divisor_recalc(struct clk *clk)
|
||||
unsigned long omap2_fixed_divisor_recalc(struct clk *clk)
|
||||
{
|
||||
WARN_ON(!clk->fixed_div);
|
||||
|
||||
clk->rate = clk->parent->rate / clk->fixed_div;
|
||||
|
||||
if (clk->flags & RATE_PROPAGATES)
|
||||
propagate_rate(clk);
|
||||
return clk->parent->rate / clk->fixed_div;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,11 +291,10 @@ int omap2_wait_clock_ready(void __iomem *reg, u32 mask, const char *name)
|
||||
* 24xx uses 0 to indicate not ready, and 1 to indicate ready.
|
||||
* 34xx reverses this, just to keep us on our toes
|
||||
*/
|
||||
if (cpu_mask & (RATE_IN_242X | RATE_IN_243X)) {
|
||||
if (cpu_mask & (RATE_IN_242X | RATE_IN_243X))
|
||||
ena = mask;
|
||||
} else if (cpu_mask & RATE_IN_343X) {
|
||||
else if (cpu_mask & RATE_IN_343X)
|
||||
ena = 0;
|
||||
}
|
||||
|
||||
/* Wait for lock */
|
||||
while (((__raw_readl(reg) & mask) != ena) &&
|
||||
@@ -228,31 +328,12 @@ static void omap2_clk_wait_ready(struct clk *clk)
|
||||
* it and pull it into struct clk itself somehow.
|
||||
*/
|
||||
reg = clk->enable_reg;
|
||||
if ((((u32)reg & 0xff) >= CM_FCLKEN1) &&
|
||||
(((u32)reg & 0xff) <= OMAP24XX_CM_FCLKEN2))
|
||||
other_reg = (void __iomem *)(((u32)reg & ~0xf0) | 0x10); /* CM_ICLKEN* */
|
||||
else if ((((u32)reg & 0xff) >= CM_ICLKEN1) &&
|
||||
(((u32)reg & 0xff) <= OMAP24XX_CM_ICLKEN4))
|
||||
other_reg = (void __iomem *)(((u32)reg & ~0xf0) | 0x00); /* CM_FCLKEN* */
|
||||
else
|
||||
return;
|
||||
|
||||
/* REVISIT: What are the appropriate exclusions for 34XX? */
|
||||
/* No check for DSS or cam clocks */
|
||||
if (cpu_is_omap24xx() && ((u32)reg & 0x0f) == 0) { /* CM_{F,I}CLKEN1 */
|
||||
if (clk->enable_bit == OMAP24XX_EN_DSS2_SHIFT ||
|
||||
clk->enable_bit == OMAP24XX_EN_DSS1_SHIFT ||
|
||||
clk->enable_bit == OMAP24XX_EN_CAM_SHIFT)
|
||||
return;
|
||||
}
|
||||
|
||||
/* REVISIT: What are the appropriate exclusions for 34XX? */
|
||||
/* OMAP3: ignore DSS-mod clocks */
|
||||
if (cpu_is_omap34xx() &&
|
||||
(((u32)reg & ~0xff) == (u32)OMAP_CM_REGADDR(OMAP3430_DSS_MOD, 0) ||
|
||||
((((u32)reg & ~0xff) == (u32)OMAP_CM_REGADDR(CORE_MOD, 0)) &&
|
||||
clk->enable_bit == OMAP3430_EN_SSI_SHIFT)))
|
||||
return;
|
||||
/*
|
||||
* Convert CM_ICLKEN* <-> CM_FCLKEN*. This conversion assumes
|
||||
* it's just a matter of XORing the bits.
|
||||
*/
|
||||
other_reg = (void __iomem *)((u32)reg ^ (CM_FCLKEN ^ CM_ICLKEN));
|
||||
|
||||
/* Check if both functional and interface clocks
|
||||
* are running. */
|
||||
@@ -264,18 +345,9 @@ static void omap2_clk_wait_ready(struct clk *clk)
|
||||
omap2_wait_clock_ready(st_reg, bit, clk->name);
|
||||
}
|
||||
|
||||
/* Enables clock without considering parent dependencies or use count
|
||||
* REVISIT: Maybe change this to use clk->enable like on omap1?
|
||||
*/
|
||||
int _omap2_clk_enable(struct clk *clk)
|
||||
static int omap2_dflt_clk_enable(struct clk *clk)
|
||||
{
|
||||
u32 regval32;
|
||||
|
||||
if (clk->flags & (ALWAYS_ENABLED | PARENT_CONTROLS_CLOCK))
|
||||
return 0;
|
||||
|
||||
if (clk->enable)
|
||||
return clk->enable(clk);
|
||||
u32 v;
|
||||
|
||||
if (unlikely(clk->enable_reg == NULL)) {
|
||||
printk(KERN_ERR "clock.c: Enable for %s without enable code\n",
|
||||
@@ -283,33 +355,38 @@ int _omap2_clk_enable(struct clk *clk)
|
||||
return 0; /* REVISIT: -EINVAL */
|
||||
}
|
||||
|
||||
regval32 = __raw_readl(clk->enable_reg);
|
||||
v = __raw_readl(clk->enable_reg);
|
||||
if (clk->flags & INVERT_ENABLE)
|
||||
regval32 &= ~(1 << clk->enable_bit);
|
||||
v &= ~(1 << clk->enable_bit);
|
||||
else
|
||||
regval32 |= (1 << clk->enable_bit);
|
||||
__raw_writel(regval32, clk->enable_reg);
|
||||
wmb();
|
||||
|
||||
omap2_clk_wait_ready(clk);
|
||||
v |= (1 << clk->enable_bit);
|
||||
__raw_writel(v, clk->enable_reg);
|
||||
v = __raw_readl(clk->enable_reg); /* OCP barrier */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Disables clock without considering parent dependencies or use count */
|
||||
void _omap2_clk_disable(struct clk *clk)
|
||||
static int omap2_dflt_clk_enable_wait(struct clk *clk)
|
||||
{
|
||||
u32 regval32;
|
||||
int ret;
|
||||
|
||||
if (clk->flags & (ALWAYS_ENABLED | PARENT_CONTROLS_CLOCK))
|
||||
return;
|
||||
|
||||
if (clk->disable) {
|
||||
clk->disable(clk);
|
||||
return;
|
||||
if (!clk->enable_reg) {
|
||||
printk(KERN_ERR "clock.c: Enable for %s without enable code\n",
|
||||
clk->name);
|
||||
return 0; /* REVISIT: -EINVAL */
|
||||
}
|
||||
|
||||
if (clk->enable_reg == NULL) {
|
||||
ret = omap2_dflt_clk_enable(clk);
|
||||
if (ret == 0)
|
||||
omap2_clk_wait_ready(clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void omap2_dflt_clk_disable(struct clk *clk)
|
||||
{
|
||||
u32 v;
|
||||
|
||||
if (!clk->enable_reg) {
|
||||
/*
|
||||
* 'Independent' here refers to a clock which is not
|
||||
* controlled by its parent.
|
||||
@@ -319,20 +396,44 @@ void _omap2_clk_disable(struct clk *clk)
|
||||
return;
|
||||
}
|
||||
|
||||
regval32 = __raw_readl(clk->enable_reg);
|
||||
v = __raw_readl(clk->enable_reg);
|
||||
if (clk->flags & INVERT_ENABLE)
|
||||
regval32 |= (1 << clk->enable_bit);
|
||||
v |= (1 << clk->enable_bit);
|
||||
else
|
||||
regval32 &= ~(1 << clk->enable_bit);
|
||||
__raw_writel(regval32, clk->enable_reg);
|
||||
wmb();
|
||||
v &= ~(1 << clk->enable_bit);
|
||||
__raw_writel(v, clk->enable_reg);
|
||||
/* No OCP barrier needed here since it is a disable operation */
|
||||
}
|
||||
|
||||
const struct clkops clkops_omap2_dflt_wait = {
|
||||
.enable = omap2_dflt_clk_enable_wait,
|
||||
.disable = omap2_dflt_clk_disable,
|
||||
};
|
||||
|
||||
const struct clkops clkops_omap2_dflt = {
|
||||
.enable = omap2_dflt_clk_enable,
|
||||
.disable = omap2_dflt_clk_disable,
|
||||
};
|
||||
|
||||
/* Enables clock without considering parent dependencies or use count
|
||||
* REVISIT: Maybe change this to use clk->enable like on omap1?
|
||||
*/
|
||||
static int _omap2_clk_enable(struct clk *clk)
|
||||
{
|
||||
return clk->ops->enable(clk);
|
||||
}
|
||||
|
||||
/* Disables clock without considering parent dependencies or use count */
|
||||
static void _omap2_clk_disable(struct clk *clk)
|
||||
{
|
||||
clk->ops->disable(clk);
|
||||
}
|
||||
|
||||
void omap2_clk_disable(struct clk *clk)
|
||||
{
|
||||
if (clk->usecount > 0 && !(--clk->usecount)) {
|
||||
_omap2_clk_disable(clk);
|
||||
if (likely((u32)clk->parent))
|
||||
if (clk->parent)
|
||||
omap2_clk_disable(clk->parent);
|
||||
if (clk->clkdm)
|
||||
omap2_clkdm_clk_disable(clk->clkdm, clk);
|
||||
@@ -345,30 +446,29 @@ int omap2_clk_enable(struct clk *clk)
|
||||
int ret = 0;
|
||||
|
||||
if (clk->usecount++ == 0) {
|
||||
if (likely((u32)clk->parent))
|
||||
ret = omap2_clk_enable(clk->parent);
|
||||
|
||||
if (unlikely(ret != 0)) {
|
||||
clk->usecount--;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (clk->clkdm)
|
||||
omap2_clkdm_clk_enable(clk->clkdm, clk);
|
||||
|
||||
if (clk->parent) {
|
||||
ret = omap2_clk_enable(clk->parent);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = _omap2_clk_enable(clk);
|
||||
|
||||
if (unlikely(ret != 0)) {
|
||||
if (clk->clkdm)
|
||||
omap2_clkdm_clk_disable(clk->clkdm, clk);
|
||||
|
||||
if (clk->parent) {
|
||||
if (ret) {
|
||||
if (clk->parent)
|
||||
omap2_clk_disable(clk->parent);
|
||||
clk->usecount--;
|
||||
}
|
||||
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
||||
err:
|
||||
if (clk->clkdm)
|
||||
omap2_clkdm_clk_disable(clk->clkdm, clk);
|
||||
clk->usecount--;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -376,24 +476,22 @@ int omap2_clk_enable(struct clk *clk)
|
||||
* Used for clocks that are part of CLKSEL_xyz governed clocks.
|
||||
* REVISIT: Maybe change to use clk->enable() functions like on omap1?
|
||||
*/
|
||||
void omap2_clksel_recalc(struct clk *clk)
|
||||
unsigned long omap2_clksel_recalc(struct clk *clk)
|
||||
{
|
||||
unsigned long rate;
|
||||
u32 div = 0;
|
||||
|
||||
pr_debug("clock: recalc'ing clksel clk %s\n", clk->name);
|
||||
|
||||
div = omap2_clksel_get_divisor(clk);
|
||||
if (div == 0)
|
||||
return;
|
||||
return clk->rate;
|
||||
|
||||
if (unlikely(clk->rate == clk->parent->rate / div))
|
||||
return;
|
||||
clk->rate = clk->parent->rate / div;
|
||||
rate = clk->parent->rate / div;
|
||||
|
||||
pr_debug("clock: new clock rate is %ld (div %d)\n", clk->rate, div);
|
||||
pr_debug("clock: new clock rate is %ld (div %d)\n", rate, div);
|
||||
|
||||
if (unlikely(clk->flags & RATE_PROPAGATES))
|
||||
propagate_rate(clk);
|
||||
return rate;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -405,8 +503,8 @@ void omap2_clksel_recalc(struct clk *clk)
|
||||
* the element associated with the supplied parent clock address.
|
||||
* Returns a pointer to the struct clksel on success or NULL on error.
|
||||
*/
|
||||
const struct clksel *omap2_get_clksel_by_parent(struct clk *clk,
|
||||
struct clk *src_clk)
|
||||
static const struct clksel *omap2_get_clksel_by_parent(struct clk *clk,
|
||||
struct clk *src_clk)
|
||||
{
|
||||
const struct clksel *clks;
|
||||
|
||||
@@ -455,7 +553,7 @@ u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate,
|
||||
*new_div = 1;
|
||||
|
||||
clks = omap2_get_clksel_by_parent(clk, clk->parent);
|
||||
if (clks == NULL)
|
||||
if (!clks)
|
||||
return ~0;
|
||||
|
||||
for (clkr = clks->rates; clkr->div; clkr++) {
|
||||
@@ -514,7 +612,7 @@ long omap2_clksel_round_rate(struct clk *clk, unsigned long target_rate)
|
||||
/* Given a clock and a rate apply a clock specific rounding function */
|
||||
long omap2_clk_round_rate(struct clk *clk, unsigned long rate)
|
||||
{
|
||||
if (clk->round_rate != NULL)
|
||||
if (clk->round_rate)
|
||||
return clk->round_rate(clk, rate);
|
||||
|
||||
if (clk->flags & RATE_FIXED)
|
||||
@@ -540,7 +638,7 @@ u32 omap2_clksel_to_divisor(struct clk *clk, u32 field_val)
|
||||
const struct clksel_rate *clkr;
|
||||
|
||||
clks = omap2_get_clksel_by_parent(clk, clk->parent);
|
||||
if (clks == NULL)
|
||||
if (!clks)
|
||||
return 0;
|
||||
|
||||
for (clkr = clks->rates; clkr->div; clkr++) {
|
||||
@@ -576,7 +674,7 @@ u32 omap2_divisor_to_clksel(struct clk *clk, u32 div)
|
||||
WARN_ON(div == 0);
|
||||
|
||||
clks = omap2_get_clksel_by_parent(clk, clk->parent);
|
||||
if (clks == NULL)
|
||||
if (!clks)
|
||||
return ~0;
|
||||
|
||||
for (clkr = clks->rates; clkr->div; clkr++) {
|
||||
@@ -594,23 +692,6 @@ u32 omap2_divisor_to_clksel(struct clk *clk, u32 div)
|
||||
return clkr->val;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap2_get_clksel - find clksel register addr & field mask for a clk
|
||||
* @clk: struct clk to use
|
||||
* @field_mask: ptr to u32 to store the register field mask
|
||||
*
|
||||
* Returns the address of the clksel register upon success or NULL on error.
|
||||
*/
|
||||
void __iomem *omap2_get_clksel(struct clk *clk, u32 *field_mask)
|
||||
{
|
||||
if (unlikely((clk->clksel_reg == NULL) || (clk->clksel_mask == NULL)))
|
||||
return NULL;
|
||||
|
||||
*field_mask = clk->clksel_mask;
|
||||
|
||||
return clk->clksel_reg;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap2_clksel_get_divisor - get current divider applied to parent clock.
|
||||
* @clk: OMAP struct clk to use.
|
||||
@@ -619,49 +700,41 @@ void __iomem *omap2_get_clksel(struct clk *clk, u32 *field_mask)
|
||||
*/
|
||||
u32 omap2_clksel_get_divisor(struct clk *clk)
|
||||
{
|
||||
u32 field_mask, field_val;
|
||||
void __iomem *div_addr;
|
||||
u32 v;
|
||||
|
||||
div_addr = omap2_get_clksel(clk, &field_mask);
|
||||
if (div_addr == NULL)
|
||||
if (!clk->clksel_mask)
|
||||
return 0;
|
||||
|
||||
field_val = __raw_readl(div_addr) & field_mask;
|
||||
field_val >>= __ffs(field_mask);
|
||||
v = __raw_readl(clk->clksel_reg) & clk->clksel_mask;
|
||||
v >>= __ffs(clk->clksel_mask);
|
||||
|
||||
return omap2_clksel_to_divisor(clk, field_val);
|
||||
return omap2_clksel_to_divisor(clk, v);
|
||||
}
|
||||
|
||||
int omap2_clksel_set_rate(struct clk *clk, unsigned long rate)
|
||||
{
|
||||
u32 field_mask, field_val, reg_val, validrate, new_div = 0;
|
||||
void __iomem *div_addr;
|
||||
u32 v, field_val, validrate, new_div = 0;
|
||||
|
||||
if (!clk->clksel_mask)
|
||||
return -EINVAL;
|
||||
|
||||
validrate = omap2_clksel_round_rate_div(clk, rate, &new_div);
|
||||
if (validrate != rate)
|
||||
return -EINVAL;
|
||||
|
||||
div_addr = omap2_get_clksel(clk, &field_mask);
|
||||
if (div_addr == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
field_val = omap2_divisor_to_clksel(clk, new_div);
|
||||
if (field_val == ~0)
|
||||
return -EINVAL;
|
||||
|
||||
reg_val = __raw_readl(div_addr);
|
||||
reg_val &= ~field_mask;
|
||||
reg_val |= (field_val << __ffs(field_mask));
|
||||
__raw_writel(reg_val, div_addr);
|
||||
wmb();
|
||||
v = __raw_readl(clk->clksel_reg);
|
||||
v &= ~clk->clksel_mask;
|
||||
v |= field_val << __ffs(clk->clksel_mask);
|
||||
__raw_writel(v, clk->clksel_reg);
|
||||
v = __raw_readl(clk->clksel_reg); /* OCP barrier */
|
||||
|
||||
clk->rate = clk->parent->rate / new_div;
|
||||
|
||||
if (clk->flags & DELAYED_APP && cpu_is_omap24xx()) {
|
||||
prm_write_mod_reg(OMAP24XX_VALID_CONFIG,
|
||||
OMAP24XX_GR_MOD, OMAP24XX_PRCM_CLKCFG_CTRL_OFFSET);
|
||||
wmb();
|
||||
}
|
||||
_omap2xxx_clk_commit(clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -680,31 +753,24 @@ int omap2_clk_set_rate(struct clk *clk, unsigned long rate)
|
||||
return -EINVAL;
|
||||
|
||||
/* dpll_ck, core_ck, virt_prcm_set; plus all clksel clocks */
|
||||
if (clk->set_rate != NULL)
|
||||
if (clk->set_rate)
|
||||
ret = clk->set_rate(clk, rate);
|
||||
|
||||
if (unlikely(ret == 0 && (clk->flags & RATE_PROPAGATES)))
|
||||
propagate_rate(clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts encoded control register address into a full address
|
||||
* On error, *src_addr will be returned as 0.
|
||||
* On error, the return value (parent_div) will be 0.
|
||||
*/
|
||||
static u32 omap2_clksel_get_src_field(void __iomem **src_addr,
|
||||
struct clk *src_clk, u32 *field_mask,
|
||||
struct clk *clk, u32 *parent_div)
|
||||
static u32 _omap2_clksel_get_src_field(struct clk *src_clk, struct clk *clk,
|
||||
u32 *field_val)
|
||||
{
|
||||
const struct clksel *clks;
|
||||
const struct clksel_rate *clkr;
|
||||
|
||||
*parent_div = 0;
|
||||
*src_addr = NULL;
|
||||
|
||||
clks = omap2_get_clksel_by_parent(clk, src_clk);
|
||||
if (clks == NULL)
|
||||
if (!clks)
|
||||
return 0;
|
||||
|
||||
for (clkr = clks->rates; clkr->div; clkr++) {
|
||||
@@ -722,47 +788,35 @@ static u32 omap2_clksel_get_src_field(void __iomem **src_addr,
|
||||
/* Should never happen. Add a clksel mask to the struct clk. */
|
||||
WARN_ON(clk->clksel_mask == 0);
|
||||
|
||||
*field_mask = clk->clksel_mask;
|
||||
*src_addr = clk->clksel_reg;
|
||||
*parent_div = clkr->div;
|
||||
*field_val = clkr->val;
|
||||
|
||||
return clkr->val;
|
||||
return clkr->div;
|
||||
}
|
||||
|
||||
int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent)
|
||||
{
|
||||
void __iomem *src_addr;
|
||||
u32 field_val, field_mask, reg_val, parent_div;
|
||||
u32 field_val, v, parent_div;
|
||||
|
||||
if (unlikely(clk->flags & CONFIG_PARTICIPANT))
|
||||
if (clk->flags & CONFIG_PARTICIPANT)
|
||||
return -EINVAL;
|
||||
|
||||
if (!clk->clksel)
|
||||
return -EINVAL;
|
||||
|
||||
field_val = omap2_clksel_get_src_field(&src_addr, new_parent,
|
||||
&field_mask, clk, &parent_div);
|
||||
if (src_addr == NULL)
|
||||
parent_div = _omap2_clksel_get_src_field(new_parent, clk, &field_val);
|
||||
if (!parent_div)
|
||||
return -EINVAL;
|
||||
|
||||
if (clk->usecount > 0)
|
||||
omap2_clk_disable(clk);
|
||||
|
||||
/* Set new source value (previous dividers if any in effect) */
|
||||
reg_val = __raw_readl(src_addr) & ~field_mask;
|
||||
reg_val |= (field_val << __ffs(field_mask));
|
||||
__raw_writel(reg_val, src_addr);
|
||||
wmb();
|
||||
v = __raw_readl(clk->clksel_reg);
|
||||
v &= ~clk->clksel_mask;
|
||||
v |= field_val << __ffs(clk->clksel_mask);
|
||||
__raw_writel(v, clk->clksel_reg);
|
||||
v = __raw_readl(clk->clksel_reg); /* OCP barrier */
|
||||
|
||||
if (clk->flags & DELAYED_APP && cpu_is_omap24xx()) {
|
||||
__raw_writel(OMAP24XX_VALID_CONFIG, OMAP24XX_PRCM_CLKCFG_CTRL);
|
||||
wmb();
|
||||
}
|
||||
_omap2xxx_clk_commit(clk);
|
||||
|
||||
clk->parent = new_parent;
|
||||
|
||||
if (clk->usecount > 0)
|
||||
omap2_clk_enable(clk);
|
||||
clk_reparent(clk, new_parent);
|
||||
|
||||
/* CLKSEL clocks follow their parents' rates, divided by a divisor */
|
||||
clk->rate = new_parent->rate;
|
||||
@@ -773,9 +827,6 @@ int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent)
|
||||
pr_debug("clock: set parent of %s to %s (new rate %ld)\n",
|
||||
clk->name, clk->parent->name, clk->rate);
|
||||
|
||||
if (unlikely(clk->flags & RATE_PROPAGATES))
|
||||
propagate_rate(clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -805,7 +856,8 @@ int omap2_dpll_set_rate_tolerance(struct clk *clk, unsigned int tolerance)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long _dpll_compute_new_rate(unsigned long parent_rate, unsigned int m, unsigned int n)
|
||||
static unsigned long _dpll_compute_new_rate(unsigned long parent_rate,
|
||||
unsigned int m, unsigned int n)
|
||||
{
|
||||
unsigned long long num;
|
||||
|
||||
@@ -838,7 +890,7 @@ static int _dpll_test_mult(int *m, int n, unsigned long *new_rate,
|
||||
unsigned long target_rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
int flags = 0, carry = 0;
|
||||
int r = 0, carry = 0;
|
||||
|
||||
/* Unscale m and round if necessary */
|
||||
if (*m % DPLL_SCALE_FACTOR >= DPLL_ROUNDING_VAL)
|
||||
@@ -859,13 +911,13 @@ static int _dpll_test_mult(int *m, int n, unsigned long *new_rate,
|
||||
if (*m < DPLL_MIN_MULTIPLIER) {
|
||||
*m = DPLL_MIN_MULTIPLIER;
|
||||
*new_rate = 0;
|
||||
flags = DPLL_MULT_UNDERFLOW;
|
||||
r = DPLL_MULT_UNDERFLOW;
|
||||
}
|
||||
|
||||
if (*new_rate == 0)
|
||||
*new_rate = _dpll_compute_new_rate(parent_rate, *m, n);
|
||||
|
||||
return flags;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -889,54 +941,65 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)
|
||||
int m, n, r, e, scaled_max_m;
|
||||
unsigned long scaled_rt_rp, new_rate;
|
||||
int min_e = -1, min_e_m = -1, min_e_n = -1;
|
||||
struct dpll_data *dd;
|
||||
|
||||
if (!clk || !clk->dpll_data)
|
||||
return ~0;
|
||||
|
||||
dd = clk->dpll_data;
|
||||
|
||||
pr_debug("clock: starting DPLL round_rate for clock %s, target rate "
|
||||
"%ld\n", clk->name, target_rate);
|
||||
|
||||
scaled_rt_rp = target_rate / (clk->parent->rate / DPLL_SCALE_FACTOR);
|
||||
scaled_max_m = clk->dpll_data->max_multiplier * DPLL_SCALE_FACTOR;
|
||||
scaled_rt_rp = target_rate / (dd->clk_ref->rate / DPLL_SCALE_FACTOR);
|
||||
scaled_max_m = dd->max_multiplier * DPLL_SCALE_FACTOR;
|
||||
|
||||
clk->dpll_data->last_rounded_rate = 0;
|
||||
dd->last_rounded_rate = 0;
|
||||
|
||||
for (n = clk->dpll_data->max_divider; n >= DPLL_MIN_DIVIDER; n--) {
|
||||
for (n = dd->min_divider; n <= dd->max_divider; n++) {
|
||||
|
||||
/* Is the (input clk, divider) pair valid for the DPLL? */
|
||||
r = _dpll_test_fint(clk, n);
|
||||
if (r == DPLL_FINT_UNDERFLOW)
|
||||
break;
|
||||
else if (r == DPLL_FINT_INVALID)
|
||||
continue;
|
||||
|
||||
/* Compute the scaled DPLL multiplier, based on the divider */
|
||||
m = scaled_rt_rp * n;
|
||||
|
||||
/*
|
||||
* Since we're counting n down, a m overflow means we can
|
||||
* can immediately skip to the next n
|
||||
* Since we're counting n up, a m overflow means we
|
||||
* can bail out completely (since as n increases in
|
||||
* the next iteration, there's no way that m can
|
||||
* increase beyond the current m)
|
||||
*/
|
||||
if (m > scaled_max_m)
|
||||
continue;
|
||||
break;
|
||||
|
||||
r = _dpll_test_mult(&m, n, &new_rate, target_rate,
|
||||
clk->parent->rate);
|
||||
dd->clk_ref->rate);
|
||||
|
||||
/* m can't be set low enough for this n - try with a larger n */
|
||||
if (r == DPLL_MULT_UNDERFLOW)
|
||||
continue;
|
||||
|
||||
e = target_rate - new_rate;
|
||||
pr_debug("clock: n = %d: m = %d: rate error is %d "
|
||||
"(new_rate = %ld)\n", n, m, e, new_rate);
|
||||
|
||||
if (min_e == -1 ||
|
||||
min_e >= (int)(abs(e) - clk->dpll_data->rate_tolerance)) {
|
||||
min_e >= (int)(abs(e) - dd->rate_tolerance)) {
|
||||
min_e = e;
|
||||
min_e_m = m;
|
||||
min_e_n = n;
|
||||
|
||||
pr_debug("clock: found new least error %d\n", min_e);
|
||||
}
|
||||
|
||||
/*
|
||||
* Since we're counting n down, a m underflow means we
|
||||
* can bail out completely (since as n decreases in
|
||||
* the next iteration, there's no way that m can
|
||||
* increase beyond the current m)
|
||||
*/
|
||||
if (r & DPLL_MULT_UNDERFLOW)
|
||||
break;
|
||||
/* We found good settings -- bail out now */
|
||||
if (min_e <= dd->rate_tolerance)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (min_e < 0) {
|
||||
@@ -944,17 +1007,17 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)
|
||||
return ~0;
|
||||
}
|
||||
|
||||
clk->dpll_data->last_rounded_m = min_e_m;
|
||||
clk->dpll_data->last_rounded_n = min_e_n;
|
||||
clk->dpll_data->last_rounded_rate =
|
||||
_dpll_compute_new_rate(clk->parent->rate, min_e_m, min_e_n);
|
||||
dd->last_rounded_m = min_e_m;
|
||||
dd->last_rounded_n = min_e_n;
|
||||
dd->last_rounded_rate = _dpll_compute_new_rate(dd->clk_ref->rate,
|
||||
min_e_m, min_e_n);
|
||||
|
||||
pr_debug("clock: final least error: e = %d, m = %d, n = %d\n",
|
||||
min_e, min_e_m, min_e_n);
|
||||
pr_debug("clock: final rate: %ld (target rate: %ld)\n",
|
||||
clk->dpll_data->last_rounded_rate, target_rate);
|
||||
dd->last_rounded_rate, target_rate);
|
||||
|
||||
return clk->dpll_data->last_rounded_rate;
|
||||
return dd->last_rounded_rate;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
@@ -973,6 +1036,10 @@ void omap2_clk_disable_unused(struct clk *clk)
|
||||
return;
|
||||
|
||||
printk(KERN_INFO "Disabling unused clock \"%s\"\n", clk->name);
|
||||
_omap2_clk_disable(clk);
|
||||
if (cpu_is_omap34xx()) {
|
||||
omap2_clk_enable(clk);
|
||||
omap2_clk_disable(clk);
|
||||
} else
|
||||
_omap2_clk_disable(clk);
|
||||
}
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user