#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/clk.h>
-#include <asm/bitops.h>
-
-#include <asm/io.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
#include <mach/clock.h>
+#include <mach/clockdomain.h>
#include <mach/sram.h>
#include <mach/cpu.h>
+#include <mach/prcm.h>
#include <asm/div64.h>
-#include "memory.h"
+#include <mach/sdrc.h>
#include "sdrc.h"
#include "clock.h"
#include "prm.h"
u8 cpu_mask;
/*-------------------------------------------------------------------------
- * Omap2 specific clock functions
+ * OMAP2/3 specific clock functions
*-------------------------------------------------------------------------*/
+/**
+ * omap2_init_clk_clkdm - look up a clockdomain name, store pointer in clk
+ * @clk: OMAP clock struct ptr to use
+ *
+ * Convert a clockdomain name stored in a struct clk 'clk' into a
+ * clockdomain pointer, and save it into the struct clk. Intended to be
+ * called during clk_register(). No return value.
+ */
+void omap2_init_clk_clkdm(struct clk *clk)
+{
+ struct clockdomain *clkdm;
+
+ if (!clk->clkdm.name)
+ return;
+
+ clkdm = clkdm_lookup(clk->clkdm.name);
+ if (clkdm) {
+ pr_debug("clock: associated clk %s to clkdm %s\n",
+ clk->name, clk->clkdm.name);
+ clk->clkdm.ptr = clkdm;
+ } else {
+ pr_debug("clock: could not associate clk %s to "
+ "clkdm %s\n", clk->name, clk->clkdm.name);
+ }
+}
+
/**
* omap2_init_clksel_parent - set a clksel clk's parent field from the hardware
* @clk: OMAP clock struct ptr to use
* 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) &&
* Note: We don't need special code here for INVERT_ENABLE
* for the time being since INVERT_ENABLE only applies to clocks enabled by
* CM_CLKEN_PLL
+ *
+ * REVISIT: This code is ugly and does not belong here.
*/
static void omap2_clk_wait_ready(struct clk *clk)
{
- void __iomem *reg, *other_reg, *st_reg;
- u32 bit;
+ u32 other_bit, idlest_bit;
+ unsigned long reg, other_reg, idlest_reg, prcm_mod, prcm_regid;
- /*
- * REVISIT: This code is pretty ugly. It would be nice to generalize
- * 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* */
+ reg = (unsigned long)clk->enable_reg;
+ prcm_mod = reg & ~0xff;
+ prcm_regid = reg & 0xff;
+
+ if (prcm_regid >= CM_FCLKEN1 && prcm_regid <= OMAP24XX_CM_FCLKEN2)
+ other_reg = ((reg & ~0xf0) | 0x10); /* CM_ICLKEN* */
+ else if (prcm_regid >= CM_ICLKEN1 && prcm_regid <= OMAP24XX_CM_ICLKEN4)
+ other_reg = ((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 */
+ /* Covers most of the cases - a few exceptions are below */
+ other_bit = 1 << clk->enable_bit;
+ idlest_bit = other_bit;
+
+ /* 24xx: DSS and CAM have no idlest bits for their target agents */
+ if (cpu_is_omap24xx() &&
+ (prcm_mod == OMAP2420_CM_REGADDR(CORE_MOD, 0) ||
+ prcm_mod == OMAP2430_CM_REGADDR(CORE_MOD, 0)) &&
+ (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;
+ if (cpu_is_omap34xx()) {
+
+ /* SSI */
+ if (prcm_mod == OMAP34XX_CM_REGADDR(CORE_MOD, 0) &&
+ (reg & 0x0f) == 0 &&
+ clk->enable_bit == OMAP3430_EN_SSI_SHIFT) {
+
+ if (is_sil_rev_equal_to(OMAP3430_REV_ES1_0))
+ return;
+
+ idlest_bit = OMAP3430ES2_ST_SSI_IDLE;
+ }
+
+ /* DSS */
+ if (prcm_mod == OMAP34XX_CM_REGADDR(OMAP3430_DSS_MOD, 0)) {
+
+ /* 3430ES1 DSS has no target idlest bits */
+ if (is_sil_rev_equal_to(OMAP3430_REV_ES1_0))
+ return;
+
+ /*
+ * For 3430ES2+ DSS, only wait once (dss1_alwon_fclk,
+ * dss_l3_iclk, dss_l4_iclk) are enabled
+ */
+ if (clk->enable_bit != OMAP3430_EN_DSS1_SHIFT)
+ return;
+
+ idlest_bit = OMAP3430ES2_ST_DSS_IDLE;
+ }
+
+ /* USBHOST */
+ if (is_sil_rev_greater_than(OMAP3430_REV_ES1_0) &&
+ prcm_mod == OMAP34XX_CM_REGADDR(OMAP3430ES2_USBHOST_MOD, 0)) {
+
+ /*
+ * The 120MHz clock apparently has nothing to do with
+ * USBHOST module accessibility
+ */
+ if (clk->enable_bit == OMAP3430ES2_EN_USBHOST2_SHIFT)
+ return;
+
+ idlest_bit = OMAP3430ES2_ST_USBHOST_IDLE;
+
+ }
+ }
/* Check if both functional and interface clocks
* are running. */
- bit = 1 << clk->enable_bit;
- if (!(__raw_readl(other_reg) & bit))
+ if (!(__raw_readl((void __iomem *)other_reg) & other_bit))
return;
- st_reg = (void __iomem *)(((u32)other_reg & ~0xf0) | 0x20); /* CM_IDLEST* */
- omap2_wait_clock_ready(st_reg, bit, clk->name);
+ idlest_reg = ((other_reg & ~0xf0) | 0x20); /* CM_IDLEST* */
+
+ omap2_wait_clock_ready((void __iomem *)idlest_reg, idlest_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_clk_enable(struct clk *clk)
{
u32 regval32;
if (clk->enable)
return clk->enable(clk);
- if (unlikely(clk->enable_reg == 0)) {
+ if (!clk->enable_reg) {
printk(KERN_ERR "clock.c: Enable for %s without enable code\n",
clk->name);
return 0; /* REVISIT: -EINVAL */
}
/* Disables clock without considering parent dependencies or use count */
-void _omap2_clk_disable(struct clk *clk)
+static void _omap2_clk_disable(struct clk *clk)
{
u32 regval32;
return;
}
- if (clk->enable_reg == 0) {
+ if (!clk->enable_reg) {
/*
* 'Independent' here refers to a clock which is not
* controlled by its parent.
{
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.ptr)
+ omap2_clkdm_clk_disable(clk->clkdm.ptr, clk);
+
}
}
int ret = 0;
if (clk->usecount++ == 0) {
- if (likely((u32)clk->parent))
+ if (clk->parent)
ret = omap2_clk_enable(clk->parent);
- if (unlikely(ret != 0)) {
+ if (ret != 0) {
clk->usecount--;
return ret;
}
+ if (clk->clkdm.ptr)
+ omap2_clkdm_clk_enable(clk->clkdm.ptr, clk);
+
ret = _omap2_clk_enable(clk);
- if (unlikely(ret != 0) && clk->parent) {
- omap2_clk_disable(clk->parent);
- clk->usecount--;
+ if (ret != 0) {
+ if (clk->clkdm.ptr)
+ omap2_clkdm_clk_disable(clk->clkdm.ptr, clk);
+
+ if (clk->parent) {
+ omap2_clk_disable(clk->parent);
+ clk->usecount--;
+ }
}
}
if (div == 0)
return;
- if (unlikely(clk->rate == clk->parent->rate / div))
+ if (clk->rate == (clk->parent->rate / div))
return;
clk->rate = clk->parent->rate / div;
pr_debug("clock: new clock rate is %ld (div %d)\n", clk->rate, div);
- if (unlikely(clk->flags & RATE_PROPAGATES))
+ if (clk->flags & RATE_PROPAGATES)
propagate_rate(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;
*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++) {
/* 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 != 0)
+ if (clk->round_rate)
return clk->round_rate(clk, rate);
if (clk->flags & RATE_FIXED)
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++) {
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++) {
*
* Returns the address of the clksel register upon success or NULL on error.
*/
-void __iomem *omap2_get_clksel(struct clk *clk, u32 *field_mask)
+static void __iomem *omap2_get_clksel(struct clk *clk, u32 *field_mask)
{
- if (unlikely((clk->clksel_reg == 0) || (clk->clksel_mask == 0)))
+ if (!clk->clksel_reg || (clk->clksel_mask == 0))
return NULL;
*field_mask = clk->clksel_mask;
void __iomem *div_addr;
div_addr = omap2_get_clksel(clk, &field_mask);
- if (div_addr == 0)
+ if (!div_addr)
return 0;
field_val = __raw_readl(div_addr) & field_mask;
int omap2_clksel_set_rate(struct clk *clk, unsigned long rate)
{
- u32 field_mask, field_val, reg_val, validrate, new_div = 0;
+ u32 field_mask, field_val, validrate, new_div = 0;
void __iomem *div_addr;
+ u32 v;
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 == 0)
+ if (!div_addr)
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);
+ v = __raw_readl(div_addr);
+ v &= ~field_mask;
+ v |= field_val << __ffs(field_mask);
+ __raw_writel(v, div_addr);
+
wmb();
clk->rate = clk->parent->rate / new_div;
return -EINVAL;
/* dpll_ck, core_ck, virt_prcm_set; plus all clksel clocks */
- if (clk->set_rate != 0)
+ if (clk->set_rate)
ret = clk->set_rate(clk, rate);
- if (unlikely(ret == 0 && (clk->flags & RATE_PROPAGATES)))
+ if (ret == 0 && (clk->flags & RATE_PROPAGATES))
propagate_rate(clk);
return ret;
const struct clksel_rate *clkr;
*parent_div = 0;
- *src_addr = 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++) {
void __iomem *src_addr;
u32 field_val, field_mask, reg_val, parent_div;
- if (unlikely(clk->flags & CONFIG_PARTICIPANT))
+ if (clk->flags & CONFIG_PARTICIPANT)
return -EINVAL;
if (!clk->clksel)
field_val = omap2_clksel_get_src_field(&src_addr, new_parent,
&field_mask, clk, &parent_div);
- if (src_addr == 0)
+ if (!src_addr)
return -EINVAL;
if (clk->usecount > 0)
wmb();
if (clk->flags & DELAYED_APP && cpu_is_omap24xx()) {
- __raw_writel(OMAP24XX_VALID_CONFIG, OMAP24XX_PRCM_CLKCFG_CTRL);
+ prm_write_mod_reg(OMAP24XX_VALID_CONFIG,
+ OMAP24XX_GR_MOD, OMAP24XX_PRCM_CLKCFG_CTRL_OFFSET);
wmb();
}
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))
+ if (clk->flags & RATE_PROPAGATES)
propagate_rate(clk);
return 0;
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;