#include <linux/bitops.h>
#include <linux/io.h>
-#include <asm/arch/clock.h>
-#include <asm/arch/clockdomain.h>
-#include <asm/arch/sram.h>
-#include <asm/arch/cpu.h>
-#include <asm/arch/prcm.h>
+#include <mach/clock.h>
+#include <mach/clockdomain.h>
+#include <mach/sram.h>
+#include <mach/cpu.h>
+#include <mach/prcm.h>
+#include <mach/control.h>
#include <asm/div64.h>
-#include <asm/arch/sdrc.h>
+#include <mach/sdrc.h>
#include "sdrc.h"
#include "clock.h"
#include "prm.h"
#define DPLL_ROUNDING_VAL ((DPLL_SCALE_BASE / 2) * \
(DPLL_SCALE_FACTOR / DPLL_SCALE_BASE))
+/* Some OMAP2xxx CM_CLKSEL_PLL.ST_CORE_CLK bits - for omap2_get_dpll_rate() */
+#define ST_CORE_CLK_REF 0x1
+#define ST_CORE_CLK_32K 0x3
+
+/* Bitmask to isolate the register type of clk.enable_reg */
+#define PRCM_REGTYPE_MASK 0xf0
+/* various CM register type options */
+#define CM_FCLKEN_REGTYPE 0x00
+#define CM_ICLKEN_REGTYPE 0x10
+#define CM_IDLEST_REGTYPE 0x20
+
u8 cpu_mask;
/*-------------------------------------------------------------------------
* OMAP2/3 specific clock functions
*-------------------------------------------------------------------------*/
+/*
+ * _omap2_clk_read_reg - read a clock register
+ * @clk: struct clk *
+ *
+ * Given a struct clk *, returns the value of the clock's register.
+ */
+static u32 _omap2_clk_read_reg(u16 reg_offset, struct clk *clk)
+{
+ if (clk->prcm_mod & CLK_REG_IN_SCM)
+ return omap_ctrl_readl(reg_offset);
+ else if (clk->prcm_mod & CLK_REG_IN_PRM)
+ return prm_read_mod_reg(clk->prcm_mod & PRCM_MOD_ADDR_MASK,
+ reg_offset);
+ else
+ return cm_read_mod_reg(clk->prcm_mod, reg_offset);
+}
+
+/*
+ * _omap2_clk_write_reg - write a clock's register
+ * @v: value to write to the clock's enable_reg
+ * @clk: struct clk *
+ *
+ * Given a register value @v and struct clk * @clk, writes the value of @v to
+ * the clock's enable register. No return value.
+ */
+static void _omap2_clk_write_reg(u32 v, u16 reg_offset, struct clk *clk)
+{
+ if (clk->prcm_mod & CLK_REG_IN_SCM)
+ omap_ctrl_writel(v, reg_offset);
+ else if (clk->prcm_mod & CLK_REG_IN_PRM)
+ prm_write_mod_reg(v, clk->prcm_mod & PRCM_MOD_ADDR_MASK,
+ reg_offset);
+ else
+ cm_write_mod_reg(v, clk->prcm_mod, reg_offset);
+}
+
+
/**
* omap2_init_clk_clkdm - look up a clockdomain name, store pointer in clk
* @clk: OMAP clock struct ptr to use
{
struct clockdomain *clkdm;
- if (!clk->clkdm.name)
+ if (!clk->clkdm.name) {
+ pr_err("clock: %s: missing clockdomain", clk->name);
return;
+ }
clkdm = clkdm_lookup(clk->clkdm.name);
if (clkdm) {
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);
+ pr_err("clock: %s: could not associate to clkdm %s\n",
+ clk->name, clk->clkdm.name);
}
}
if (!clk->clksel)
return;
- r = __raw_readl(clk->clksel_reg) & clk->clksel_mask;
+ r = _omap2_clk_read_reg(clk->clksel_reg, clk);
+ r &= clk->clksel_mask;
r >>= __ffs(clk->clksel_mask);
for (clks = clk->clksel; clks->parent && !found; clks++) {
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 = cm_read_mod_reg(clk->prcm_mod, dd->idlest_reg);
+ v &= dd->idlest_mask;
+ v >>= __ffs(dd->idlest_mask);
+ if (cpu_is_omap24xx()) {
+
+ if (v == ST_CORE_CLK_REF)
+ return clk->parent->rate; /* sys_clk */
+ else if (v == ST_CORE_CLK_32K)
+ return 32768;
+
+ } else if (cpu_is_omap34xx()) {
+
+ if (!v)
+ return dd->bypass_clk->rate;
+
+ }
+
+ v = cm_read_mod_reg(clk->prcm_mod, 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;
/**
* omap2_wait_clock_ready - wait for clock to enable
- * @reg: physical address of clock IDLEST register
+ * @prcm_mod: CM submodule offset from CM_BASE (e.g., "MPU_MOD")
+ * @reg_index: offset of CM register address from prcm_mod
* @mask: value to mask against to determine if the clock is active
* @name: name of the clock (for printk)
*
* Returns 1 if the clock enabled in time, or 0 if it failed to enable
* in roughly MAX_CLOCK_ENABLE_WAIT microseconds.
*/
-int omap2_wait_clock_ready(void __iomem *reg, u32 mask, const char *name)
+int omap2_wait_clock_ready(s16 prcm_mod, u16 reg_index, u32 mask,
+ const char *name)
{
- int i = 0;
- int ena = 0;
+ int i = 0, ena = 0;
/*
* 24xx uses 0 to indicate not ready, and 1 to indicate ready.
ena = 0;
/* Wait for lock */
- while (((__raw_readl(reg) & mask) != ena) &&
+ while (((cm_read_mod_reg(prcm_mod, reg_index) & mask) != ena) &&
(i++ < MAX_CLOCK_ENABLE_WAIT)) {
udelay(1);
}
printk(KERN_ERR "Clock %s didn't enable in %d tries\n",
name, MAX_CLOCK_ENABLE_WAIT);
-
return (i < MAX_CLOCK_ENABLE_WAIT) ? 1 : 0;
};
*/
static void omap2_clk_wait_ready(struct clk *clk)
{
+ u16 other_reg, idlest_reg;
u32 other_bit, idlest_bit;
- unsigned long reg, other_reg, idlest_reg, prcm_mod, prcm_regid;
- reg = (unsigned long)clk->enable_reg;
- prcm_mod = reg & ~0xff;
- prcm_regid = reg & 0xff;
+ /* Only CM-controlled clocks affect module IDLEST */
+ if (clk->prcm_mod & ~PRCM_MOD_ADDR_MASK)
+ return;
+
+ other_reg = clk->enable_reg & ~PRCM_REGTYPE_MASK;
- 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* */
+ /* If we are enabling an iclk, also test the fclk; and vice versa */
+ if (clk->enable_reg & CM_ICLKEN_REGTYPE)
+ other_reg |= CM_FCLKEN_REGTYPE;
else
- return;
+ other_reg |= CM_ICLKEN_REGTYPE;
/* 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 (cpu_is_omap24xx() && clk->prcm_mod == CORE_MOD &&
+ (clk->enable_reg == CM_FCLKEN1 || clk->enable_reg == CM_ICLKEN1)) {
if (clk->enable_bit == OMAP24XX_EN_DSS2_SHIFT ||
clk->enable_bit == OMAP24XX_EN_DSS1_SHIFT ||
if (cpu_is_omap34xx()) {
/* SSI */
- if (prcm_mod == OMAP34XX_CM_REGADDR(CORE_MOD, 0) &&
- (reg & 0x0f) == 0 &&
+ if (clk->prcm_mod == CORE_MOD &&
+ (clk->enable_reg == CM_FCLKEN1 ||
+ clk->enable_reg == CM_ICLKEN1) &&
clk->enable_bit == OMAP3430_EN_SSI_SHIFT) {
- if (is_sil_rev_equal_to(OMAP3430_REV_ES1_0))
+ if (system_rev == OMAP3430_REV_ES1_0)
return;
- idlest_bit = OMAP3430ES2_ST_SSI_IDLE;
+ idlest_bit = OMAP3430ES2_ST_SSI_IDLE_SHIFT;
}
/* DSS */
- if (prcm_mod == OMAP34XX_CM_REGADDR(OMAP3430_DSS_MOD, 0)) {
+ if (clk->prcm_mod == OMAP3430_DSS_MOD) {
/* 3430ES1 DSS has no target idlest bits */
- if (is_sil_rev_equal_to(OMAP3430_REV_ES1_0))
+ if (system_rev == OMAP3430_REV_ES1_0)
return;
/*
if (clk->enable_bit != OMAP3430_EN_DSS1_SHIFT)
return;
- idlest_bit = OMAP3430ES2_ST_DSS_IDLE;
+ idlest_bit = OMAP3430ES2_ST_DSS_IDLE_SHIFT;
}
/* USBHOST */
- if (is_sil_rev_greater_than(OMAP3430_REV_ES1_0) &&
- prcm_mod == OMAP34XX_CM_REGADDR(OMAP3430ES2_USBHOST_MOD, 0)) {
+ if (system_rev > OMAP3430_REV_ES1_0 &&
+ clk->prcm_mod == OMAP3430ES2_USBHOST_MOD) {
/*
* The 120MHz clock apparently has nothing to do with
if (clk->enable_bit == OMAP3430ES2_EN_USBHOST2_SHIFT)
return;
- idlest_bit = OMAP3430ES2_ST_USBHOST_IDLE;
+ idlest_bit = OMAP3430ES2_ST_USBHOST_IDLE_SHIFT;
}
}
- /* Check if both functional and interface clocks
- * are running. */
- if (!(__raw_readl((void __iomem *)other_reg) & other_bit))
+ /* Check if both functional and interface clocks are running. */
+ if (!(cm_read_mod_reg(clk->prcm_mod, other_reg) & other_bit))
return;
- idlest_reg = ((other_reg & ~0xf0) | 0x20); /* CM_IDLEST* */
+ idlest_reg = other_reg & ~PRCM_REGTYPE_MASK;
+ idlest_reg |= CM_IDLEST_REGTYPE;
- omap2_wait_clock_ready((void __iomem *)idlest_reg, idlest_bit,
+ omap2_wait_clock_ready(clk->prcm_mod, idlest_reg, idlest_bit,
clk->name);
}
*/
static int _omap2_clk_enable(struct clk *clk)
{
- u32 regval32;
+ u32 v;
if (clk->flags & (ALWAYS_ENABLED | PARENT_CONTROLS_CLOCK))
return 0;
if (clk->enable)
return clk->enable(clk);
- if (!clk->enable_reg) {
- printk(KERN_ERR "clock.c: Enable for %s without enable code\n",
- clk->name);
- return 0; /* REVISIT: -EINVAL */
- }
-
- regval32 = __raw_readl(clk->enable_reg);
+ v = _omap2_clk_read_reg(clk->enable_reg, clk);
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);
+ v |= (1 << clk->enable_bit);
+ _omap2_clk_write_reg(v, clk->enable_reg, clk);
wmb();
omap2_clk_wait_ready(clk);
/* Disables clock without considering parent dependencies or use count */
static void _omap2_clk_disable(struct clk *clk)
{
- u32 regval32;
+ u32 v;
if (clk->flags & (ALWAYS_ENABLED | PARENT_CONTROLS_CLOCK))
return;
return;
}
- if (!clk->enable_reg) {
- /*
- * 'Independent' here refers to a clock which is not
- * controlled by its parent.
- */
- printk(KERN_ERR "clock: clk_disable called on independent "
- "clock %s which has no enable_reg\n", clk->name);
- return;
- }
-
- regval32 = __raw_readl(clk->enable_reg);
+ v = _omap2_clk_read_reg(clk->enable_reg, clk);
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);
+ v &= ~(1 << clk->enable_bit);
+ _omap2_clk_write_reg(v, clk->enable_reg, clk);
wmb();
}
/* 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)
+ if (clk->round_rate != NULL)
return clk->round_rate(clk, rate);
if (clk->flags & RATE_FIXED)
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.
- */
-static void __iomem *omap2_get_clksel(struct clk *clk, u32 *field_mask)
-{
- if (!clk->clksel_reg || (clk->clksel_mask == 0))
- 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.
*/
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)
+ if (!clk->clksel_mask)
return 0;
- field_val = __raw_readl(div_addr) & field_mask;
- field_val >>= __ffs(field_mask);
+ v = _omap2_clk_read_reg(clk->clksel_reg, clk);
+ v &= 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, validrate, new_div = 0;
- void __iomem *div_addr;
- u32 v;
+ u32 v, field_val, validrate, new_div = 0;
- validrate = omap2_clksel_round_rate_div(clk, rate, &new_div);
- if (validrate != rate)
+ if (!clk->clksel_mask)
return -EINVAL;
- div_addr = omap2_get_clksel(clk, &field_mask);
- if (!div_addr)
- return -EINVAL;
+ validrate = omap2_clksel_round_rate_div(clk, rate, &new_div);
+ if (validrate != rate)
+ return -EINVAL;
field_val = omap2_divisor_to_clksel(clk, new_div);
if (field_val == ~0)
return -EINVAL;
- v = __raw_readl(div_addr);
- v &= ~field_mask;
- v |= field_val << __ffs(field_mask);
- __raw_writel(v, div_addr);
+ v = _omap2_clk_read_reg(clk->clksel_reg, clk);
+ v &= ~clk->clksel_mask;
+ v |= field_val << __ffs(clk->clksel_mask);
+ _omap2_clk_write_reg(v, clk->clksel_reg, clk);
wmb();
return -EINVAL;
/* dpll_ck, core_ck, virt_prcm_set; plus all clksel clocks */
- if (clk->set_rate)
+ if (clk->set_rate != NULL)
ret = clk->set_rate(clk, rate);
if (ret == 0 && (clk->flags & RATE_PROPAGATES))
/*
* 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)
return 0;
/* 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 (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)
+ 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);
+ v = _omap2_clk_read_reg(clk->clksel_reg, clk);
+ v &= ~clk->clksel_mask;
+ v |= field_val << __ffs(clk->clksel_mask);
+ _omap2_clk_write_reg(v, clk->clksel_reg, clk);
wmb();
if (clk->flags & DELAYED_APP && cpu_is_omap24xx()) {
v = (clk->flags & INVERT_ENABLE) ? (1 << clk->enable_bit) : 0;
- regval32 = __raw_readl(clk->enable_reg);
+ regval32 = _omap2_clk_read_reg(clk->enable_reg, clk);
if ((regval32 & (1 << clk->enable_bit)) == v)
return;