X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=arch%2Farm%2Fmach-omap2%2Fclock.c;h=b57ffb5a22a521d44aa91bdf368f7433f9ca3591;hb=8a3227268877b81096d7b7a841aaf51099ad2068;hp=588adb5ab47fa2b4bd796fc9c2c42a4529eb308e;hpb=080e89270a7bfb7d01fac9a67050f8ac6d6cdd11;p=linux-2.6 diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c index 588adb5ab4..b57ffb5a22 100644 --- a/arch/arm/mach-omap2/clock.c +++ b/arch/arm/mach-omap2/clock.c @@ -1,20 +1,19 @@ /* * linux/arch/arm/mach-omap2/clock.c * - * Copyright (C) 2005 Texas Instruments Inc. - * Richard Woodruff - * Created for OMAP2. - * - * Cleaned up and modified to use omap shared clock framework by - * Tony Lindgren + * Copyright (C) 2005-2008 Texas Instruments, Inc. + * Copyright (C) 2004-2008 Nokia Corporation * - * Based on omap1 clock.c, Copyright (C) 2004 - 2005 Nokia corporation - * Written by Tuukka Tikkanen + * Contacts: + * Richard Woodruff + * Paul Walmsley * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#undef DEBUG + #include #include #include @@ -22,176 +21,227 @@ #include #include #include +#include #include #include #include +#include #include -#include "prcm-regs.h" #include "memory.h" +#include "sdrc.h" #include "clock.h" +#include "prm.h" +#include "prm-regbits-24xx.h" +#include "cm.h" +#include "cm-regbits-24xx.h" +#include "cm-regbits-34xx.h" -#undef DEBUG - -//#define DOWN_VARIABLE_DPLL 1 /* Experimental */ +#define MAX_CLOCK_ENABLE_WAIT 100000 -static struct prcm_config *curr_prcm_set; -static u32 curr_perf_level = PRCM_FULL_SPEED; -static struct clk *vclk; -static struct clk *sclk; +u8 cpu_mask; /*------------------------------------------------------------------------- * Omap2 specific clock functions *-------------------------------------------------------------------------*/ -/* Recalculate SYST_CLK */ -static void omap2_sys_clk_recalc(struct clk * clk) +/** + * omap2_init_clksel_parent - set a clksel clk's parent field from the hardware + * @clk: OMAP clock struct ptr to use + * + * Given a pointer to a source-selectable struct clk, read the hardware + * register and determine what its parent is currently set to. Update the + * clk->parent field with the appropriate clk ptr. + */ +void omap2_init_clksel_parent(struct clk *clk) { - u32 div = PRCM_CLKSRC_CTRL; - div &= (1 << 7) | (1 << 6); /* Test if ext clk divided by 1 or 2 */ - div >>= clk->rate_offset; - clk->rate = (clk->parent->rate / div); - propagate_rate(clk); + const struct clksel *clks; + const struct clksel_rate *clkr; + u32 r, found = 0; + + if (!clk->clksel) + return; + + 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) && (clkr->val == r)) { + if (clk->parent != clks->parent) { + pr_debug("clock: inited %s parent " + "to %s (was %s)\n", + clk->name, clks->parent->name, + ((clk->parent) ? + clk->parent->name : "NULL")); + clk->parent = clks->parent; + }; + found = 1; + } + } + } + + if (!found) + printk(KERN_ERR "clock: init parent: could not find " + "regval %0x for clock %s\n", r, clk->name); + + return; } -static u32 omap2_get_dpll_rate(struct clk * tclk) +/* Returns the DPLL rate */ +u32 omap2_get_dpll_rate(struct clk *clk) { long long dpll_clk; - int dpll_mult, dpll_div, amult; + u32 dpll_mult, dpll_div, dpll; + const 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; + dpll_mult >>= __ffs(dd->mult_mask); + dpll_div = dpll & dd->div1_mask; + dpll_div >>= __ffs(dd->div1_mask); - dpll_mult = (CM_CLKSEL1_PLL >> 12) & 0x03ff; /* 10 bits */ - dpll_div = (CM_CLKSEL1_PLL >> 8) & 0x0f; /* 4 bits */ - dpll_clk = (long long)tclk->parent->rate * dpll_mult; + dpll_clk = (long long)clk->parent->rate * dpll_mult; do_div(dpll_clk, dpll_div + 1); - amult = CM_CLKSEL2_PLL & 0x3; - dpll_clk *= amult; return dpll_clk; } -static void omap2_followparent_recalc(struct clk *clk) -{ - followparent_recalc(clk); -} - -static void omap2_propagate_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) { - if (!(clk->flags & RATE_FIXED)) - clk->rate = clk->parent->rate; + WARN_ON(!clk->fixed_div); - propagate_rate(clk); -} + clk->rate = clk->parent->rate / clk->fixed_div; -static void omap2_set_osc_ck(int enable) -{ - if (enable) - PRCM_CLKSRC_CTRL &= ~(0x3 << 3); - else - PRCM_CLKSRC_CTRL |= 0x3 << 3; + if (clk->flags & RATE_PROPAGATES) + propagate_rate(clk); } -/* Enable an APLL if off */ -static void omap2_clk_fixed_enable(struct clk *clk) +/** + * omap2_wait_clock_ready - wait for clock to enable + * @reg: physical address of clock IDLEST register + * @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) { - u32 cval, i=0; + int i = 0; + int ena = 0; - if (clk->enable_bit == 0xff) /* Parent will do it */ - return; + /* + * 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)) { + ena = mask; + } else if (cpu_mask & RATE_IN_343X) { + ena = 0; + } - cval = CM_CLKEN_PLL; + /* Wait for lock */ + while (((__raw_readl(reg) & mask) != ena) && + (i++ < MAX_CLOCK_ENABLE_WAIT)) { + udelay(1); + } - if ((cval & (0x3 << clk->enable_bit)) == (0x3 << clk->enable_bit)) - return; + if (i < MAX_CLOCK_ENABLE_WAIT) + pr_debug("Clock %s stable after %d loops\n", name, i); + else + printk(KERN_ERR "Clock %s didn't enable in %d tries\n", + name, MAX_CLOCK_ENABLE_WAIT); - cval &= ~(0x3 << clk->enable_bit); - cval |= (0x3 << clk->enable_bit); - CM_CLKEN_PLL = cval; - if (clk == &apll96_ck) - cval = (1 << 8); - else if (clk == &apll54_ck) - cval = (1 << 6); + return (i < MAX_CLOCK_ENABLE_WAIT) ? 1 : 0; +}; - while (!(CM_IDLEST_CKGEN & cval)) { /* Wait for lock */ - ++i; - udelay(1); - if (i == 100000) { - printk(KERN_ERR "Clock %s didn't lock\n", clk->name); - break; - } - } -} +/* + * 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 + */ static void omap2_clk_wait_ready(struct clk *clk) { - unsigned long reg, other_reg, st_reg; + void __iomem *reg, *other_reg, *st_reg; u32 bit; - int i; - - reg = (unsigned long) clk->enable_reg; - if (reg == (unsigned long) &CM_FCLKEN1_CORE || - reg == (unsigned long) &CM_FCLKEN2_CORE) - other_reg = (reg & ~0xf0) | 0x10; - else if (reg == (unsigned long) &CM_ICLKEN1_CORE || - reg == (unsigned long) &CM_ICLKEN2_CORE) - other_reg = (reg & ~0xf0) | 0x00; + + /* + * 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* */ else return; + /* REVISIT: What are the appropriate exclusions for 34XX? */ /* No check for DSS or cam clocks */ - if ((reg & 0x0f) == 0) { - if (clk->enable_bit <= 1 || clk->enable_bit == 31) + 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))) + return; + /* Check if both functional and interface clocks * are running. */ bit = 1 << clk->enable_bit; if (!(__raw_readl(other_reg) & bit)) return; - st_reg = (other_reg & ~0xf0) | 0x20; - i = 0; - while (!(__raw_readl(st_reg) & bit)) { - i++; - if (i == 100000) { - printk(KERN_ERR "Timeout enabling clock %s\n", clk->name); - break; - } - } - if (i) - pr_debug("Clock %s stable after %d loops\n", clk->name, i); + st_reg = (void __iomem *)(((u32)other_reg & ~0xf0) | 0x20); /* CM_IDLEST* */ + + 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? */ -static int _omap2_clk_enable(struct clk * clk) +int _omap2_clk_enable(struct clk *clk) { u32 regval32; - if (clk->flags & ALWAYS_ENABLED) + if (clk->flags & (ALWAYS_ENABLED | PARENT_CONTROLS_CLOCK)) return 0; - if (unlikely(clk == &osc_ck)) { - omap2_set_osc_ck(1); - return 0; - } + if (clk->enable) + return clk->enable(clk); if (unlikely(clk->enable_reg == 0)) { printk(KERN_ERR "clock.c: Enable for %s without enable code\n", clk->name); - return 0; - } - - if (clk->enable_reg == (void __iomem *)&CM_CLKEN_PLL) { - omap2_clk_fixed_enable(clk); - return 0; + return 0; /* REVISIT: -EINVAL */ } regval32 = __raw_readl(clk->enable_reg); - regval32 |= (1 << clk->enable_bit); + if (clk->flags & INVERT_ENABLE) + regval32 &= ~(1 << clk->enable_bit); + else + regval32 |= (1 << clk->enable_bit); __raw_writel(regval32, clk->enable_reg); wmb(); @@ -200,44 +250,48 @@ static int _omap2_clk_enable(struct clk * clk) return 0; } -/* Stop APLL */ -static void omap2_clk_fixed_disable(struct clk *clk) -{ - u32 cval; - - if(clk->enable_bit == 0xff) /* let parent off do it */ - return; - - cval = CM_CLKEN_PLL; - cval &= ~(0x3 << clk->enable_bit); - CM_CLKEN_PLL = cval; -} - /* Disables clock without considering parent dependencies or use count */ -static void _omap2_clk_disable(struct clk *clk) +void _omap2_clk_disable(struct clk *clk) { u32 regval32; - if (unlikely(clk == &osc_ck)) { - omap2_set_osc_ck(0); + if (clk->flags & (ALWAYS_ENABLED | PARENT_CONTROLS_CLOCK)) return; - } - if (clk->enable_reg == 0) + if (clk->disable) { + clk->disable(clk); return; + } - if (clk->enable_reg == (void __iomem *)&CM_CLKEN_PLL) { - omap2_clk_fixed_disable(clk); + if (clk->enable_reg == 0) { + /* + * '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); - regval32 &= ~(1 << clk->enable_bit); + if (clk->flags & INVERT_ENABLE) + regval32 |= (1 << clk->enable_bit); + else + regval32 &= ~(1 << clk->enable_bit); __raw_writel(regval32, clk->enable_reg); wmb(); } -static int omap2_clk_enable(struct clk *clk) +void omap2_clk_disable(struct clk *clk) +{ + if (clk->usecount > 0 && !(--clk->usecount)) { + _omap2_clk_disable(clk); + if (likely((u32)clk->parent)) + omap2_clk_disable(clk->parent); + } +} + +int omap2_clk_enable(struct clk *clk) { int ret = 0; @@ -261,519 +315,314 @@ static int omap2_clk_enable(struct clk *clk) return ret; } -static void omap2_clk_disable(struct clk *clk) -{ - if (clk->usecount > 0 && !(--clk->usecount)) { - _omap2_clk_disable(clk); - if (likely((u32)clk->parent)) - omap2_clk_disable(clk->parent); - } -} - -/* - * Uses the current prcm set to tell if a rate is valid. - * You can go slower, but not faster within a given rate set. - */ -static u32 omap2_dpll_round_rate(unsigned long target_rate) -{ - u32 high, low; - - if ((CM_CLKSEL2_PLL & 0x3) == 1) { /* DPLL clockout */ - high = curr_prcm_set->dpll_speed * 2; - low = curr_prcm_set->dpll_speed; - } else { /* DPLL clockout x 2 */ - high = curr_prcm_set->dpll_speed; - low = curr_prcm_set->dpll_speed / 2; - } - -#ifdef DOWN_VARIABLE_DPLL - if (target_rate > high) - return high; - else - return target_rate; -#else - if (target_rate > low) - return high; - else - return low; -#endif - -} - /* * Used for clocks that are part of CLKSEL_xyz governed clocks. * REVISIT: Maybe change to use clk->enable() functions like on omap1? */ -static void omap2_clksel_recalc(struct clk * clk) +void omap2_clksel_recalc(struct clk *clk) { - u32 fixed = 0, div = 0; - - if (clk == &dpll_ck) { - clk->rate = omap2_get_dpll_rate(clk); - fixed = 1; - div = 0; - } + u32 div = 0; - if (clk == &iva1_mpu_int_ifck) { - div = 2; - fixed = 1; - } + pr_debug("clock: recalc'ing clksel clk %s\n", clk->name); - if ((clk == &dss1_fck) && ((CM_CLKSEL1_CORE & (0x1f << 8)) == 0)) { - clk->rate = sys_ck.rate; + div = omap2_clksel_get_divisor(clk); + if (div == 0) return; - } - if (!fixed) { - div = omap2_clksel_get_divisor(clk); - if (div == 0) - return; - } + if (unlikely(clk->rate == clk->parent->rate / div)) + return; + clk->rate = clk->parent->rate / div; - if (div != 0) { - if (unlikely(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)) propagate_rate(clk); } -/* - * Finds best divider value in an array based on the source and target - * rates. The divider array must be sorted with smallest divider first. +/** + * omap2_get_clksel_by_parent - return clksel struct for a given clk & parent + * @clk: OMAP struct clk ptr to inspect + * @src_clk: OMAP struct clk ptr of the parent clk to search for + * + * Scan the struct clksel array associated with the clock to find + * the element associated with the supplied parent clock address. + * Returns a pointer to the struct clksel on success or NULL on error. */ -static inline u32 omap2_divider_from_table(u32 size, u32 *div_array, - u32 src_rate, u32 tgt_rate) +const struct clksel *omap2_get_clksel_by_parent(struct clk *clk, + struct clk *src_clk) { - int i, test_rate; + const struct clksel *clks; - if (div_array == NULL) - return ~1; + if (!clk->clksel) + return NULL; - for (i=0; i < size; i++) { - test_rate = src_rate / *div_array; - if (test_rate <= tgt_rate) - return *div_array; - ++div_array; + for (clks = clk->clksel; clks->parent; clks++) { + if (clks->parent == src_clk) + break; /* Found the requested parent */ } - return ~0; /* No acceptable divider */ + if (!clks->parent) { + printk(KERN_ERR "clock: Could not find parent clock %s in " + "clksel array of clock %s\n", src_clk->name, + clk->name); + return NULL; + } + + return clks; } -/* - * Find divisor for the given clock and target rate. +/** + * omap2_clksel_round_rate_div - find divisor for the given clock and rate + * @clk: OMAP struct clk to use + * @target_rate: desired clock rate + * @new_div: ptr to where we should store the divisor * + * Finds 'best' divider value in an array based on the source and target + * rates. The divider array must be sorted with smallest divider first. * Note that this will not work for clocks which are part of CONFIG_PARTICIPANT, * they are only settable as part of virtual_prcm set. + * + * Returns the rounded clock rate or returns 0xffffffff on error. */ -static u32 omap2_clksel_round_rate(struct clk *tclk, u32 target_rate, - u32 *new_div) +u32 omap2_clksel_round_rate_div(struct clk *clk, unsigned long target_rate, + u32 *new_div) { - u32 gfx_div[] = {2, 3, 4}; - u32 sysclkout_div[] = {1, 2, 4, 8, 16}; - u32 dss1_div[] = {1, 2, 3, 4, 5, 6, 8, 9, 12, 16}; - u32 vylnq_div[] = {1, 2, 3, 4, 6, 8, 9, 12, 16, 18}; - u32 best_div = ~0, asize = 0; - u32 *div_array = NULL; - - switch (tclk->flags & SRC_RATE_SEL_MASK) { - case CM_GFX_SEL1: - asize = 3; - div_array = gfx_div; - break; - case CM_PLL_SEL1: - return omap2_dpll_round_rate(target_rate); - case CM_SYSCLKOUT_SEL1: - asize = 5; - div_array = sysclkout_div; - break; - case CM_CORE_SEL1: - if(tclk == &dss1_fck){ - if(tclk->parent == &core_ck){ - asize = 10; - div_array = dss1_div; - } else { - *new_div = 0; /* fixed clk */ - return(tclk->parent->rate); - } - } else if((tclk == &vlynq_fck) && cpu_is_omap2420()){ - if(tclk->parent == &core_ck){ - asize = 10; - div_array = vylnq_div; - } else { - *new_div = 0; /* fixed clk */ - return(tclk->parent->rate); - } - } - break; + unsigned long test_rate; + const struct clksel *clks; + const struct clksel_rate *clkr; + u32 last_div = 0; + + printk(KERN_INFO "clock: clksel_round_rate_div: %s target_rate %ld\n", + clk->name, target_rate); + + *new_div = 1; + + clks = omap2_get_clksel_by_parent(clk, clk->parent); + if (clks == NULL) + return ~0; + + for (clkr = clks->rates; clkr->div; clkr++) { + if (!(clkr->flags & cpu_mask)) + continue; + + /* Sanity check */ + if (clkr->div <= last_div) + printk(KERN_ERR "clock: clksel_rate table not sorted " + "for clock %s", clk->name); + + last_div = clkr->div; + + test_rate = clk->parent->rate / clkr->div; + + if (test_rate <= target_rate) + break; /* found it */ } - best_div = omap2_divider_from_table(asize, div_array, - tclk->parent->rate, target_rate); - if (best_div == ~0){ - *new_div = 1; - return best_div; /* signal error */ + if (!clkr->div) { + printk(KERN_ERR "clock: Could not find divisor for target " + "rate %ld for clock %s parent %s\n", target_rate, + clk->name, clk->parent->name); + return ~0; } - *new_div = best_div; - return (tclk->parent->rate / best_div); + *new_div = clkr->div; + + printk(KERN_INFO "clock: new_div = %d, new_rate = %ld\n", *new_div, + (clk->parent->rate / clkr->div)); + + return (clk->parent->rate / clkr->div); } -/* Given a clock and a rate apply a clock specific rounding function */ -static long omap2_clk_round_rate(struct clk *clk, unsigned long rate) +/** + * omap2_clksel_round_rate - find rounded rate for the given clock and rate + * @clk: OMAP struct clk to use + * @target_rate: desired clock rate + * + * Compatibility wrapper for OMAP clock framework + * Finds best target rate based on the source clock and possible dividers. + * rates. The divider array must be sorted with smallest divider first. + * Note that this will not work for clocks which are part of CONFIG_PARTICIPANT, + * they are only settable as part of virtual_prcm set. + * + * Returns the rounded clock rate or returns 0xffffffff on error. + */ +long omap2_clksel_round_rate(struct clk *clk, unsigned long target_rate) { - u32 new_div = 0; - int valid_rate; + u32 new_div; - if (clk->flags & RATE_FIXED) - return clk->rate; + return omap2_clksel_round_rate_div(clk, target_rate, &new_div); +} - if (clk->flags & RATE_CKCTL) { - valid_rate = omap2_clksel_round_rate(clk, rate, &new_div); - return valid_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 != 0) return clk->round_rate(clk, rate); + if (clk->flags & RATE_FIXED) + printk(KERN_ERR "clock: generic omap2_clk_round_rate called " + "on fixed-rate clock %s\n", clk->name); + return clk->rate; } -/* - * Check the DLL lock state, and return tue if running in unlock mode. - * This is needed to compensate for the shifted DLL value in unlock mode. +/** + * omap2_clksel_to_divisor() - turn clksel field value into integer divider + * @clk: OMAP struct clk to use + * @field_val: register field value to find + * + * Given a struct clk of a rate-selectable clksel clock, and a register field + * value to search for, find the corresponding clock divisor. The register + * field value should be pre-masked and shifted down so the LSB is at bit 0 + * before calling. Returns 0 on error */ -static u32 omap2_dll_force_needed(void) +u32 omap2_clksel_to_divisor(struct clk *clk, u32 field_val) { - u32 dll_state = SDRC_DLLA_CTRL; /* dlla and dllb are a set */ + const struct clksel *clks; + const struct clksel_rate *clkr; - if ((dll_state & (1 << 2)) == (1 << 2)) - return 1; - else + clks = omap2_get_clksel_by_parent(clk, clk->parent); + if (clks == NULL) return 0; -} -static u32 omap2_reprogram_sdrc(u32 level, u32 force) -{ - u32 slow_dll_ctrl, fast_dll_ctrl, m_type; - u32 prev = curr_perf_level, flags; - - if ((curr_perf_level == level) && !force) - return prev; - - m_type = omap2_memory_get_type(); - slow_dll_ctrl = omap2_memory_get_slow_dll_ctrl(); - fast_dll_ctrl = omap2_memory_get_fast_dll_ctrl(); - - if (level == PRCM_HALF_SPEED) { - local_irq_save(flags); - PRCM_VOLTSETUP = 0xffff; - omap2_sram_reprogram_sdrc(PRCM_HALF_SPEED, - slow_dll_ctrl, m_type); - curr_perf_level = PRCM_HALF_SPEED; - local_irq_restore(flags); + for (clkr = clks->rates; clkr->div; clkr++) { + if ((clkr->flags & cpu_mask) && (clkr->val == field_val)) + break; } - if (level == PRCM_FULL_SPEED) { - local_irq_save(flags); - PRCM_VOLTSETUP = 0xffff; - omap2_sram_reprogram_sdrc(PRCM_FULL_SPEED, - fast_dll_ctrl, m_type); - curr_perf_level = PRCM_FULL_SPEED; - local_irq_restore(flags); + + if (!clkr->div) { + printk(KERN_ERR "clock: Could not find fieldval %d for " + "clock %s parent %s\n", field_val, clk->name, + clk->parent->name); + return 0; } - return prev; + return clkr->div; } -static int omap2_reprogram_dpll(struct clk * clk, unsigned long rate) +/** + * omap2_divisor_to_clksel() - turn clksel integer divisor into a field value + * @clk: OMAP struct clk to use + * @div: integer divisor to search for + * + * Given a struct clk of a rate-selectable clksel clock, and a clock divisor, + * find the corresponding register field value. The return register value is + * the value before left-shifting. Returns 0xffffffff on error + */ +u32 omap2_divisor_to_clksel(struct clk *clk, u32 div) { - u32 flags, cur_rate, low, mult, div, valid_rate, done_rate; - u32 bypass = 0; - struct prcm_config tmpset; - int ret = -EINVAL; - - local_irq_save(flags); - cur_rate = omap2_get_dpll_rate(&dpll_ck); - mult = CM_CLKSEL2_PLL & 0x3; - - if ((rate == (cur_rate / 2)) && (mult == 2)) { - omap2_reprogram_sdrc(PRCM_HALF_SPEED, 1); - } else if ((rate == (cur_rate * 2)) && (mult == 1)) { - omap2_reprogram_sdrc(PRCM_FULL_SPEED, 1); - } else if (rate != cur_rate) { - valid_rate = omap2_dpll_round_rate(rate); - if (valid_rate != rate) - goto dpll_exit; - - if ((CM_CLKSEL2_PLL & 0x3) == 1) - low = curr_prcm_set->dpll_speed; - else - low = curr_prcm_set->dpll_speed / 2; - - tmpset.cm_clksel1_pll = CM_CLKSEL1_PLL; - tmpset.cm_clksel1_pll &= ~(0x3FFF << 8); - div = ((curr_prcm_set->xtal_speed / 1000000) - 1); - tmpset.cm_clksel2_pll = CM_CLKSEL2_PLL; - tmpset.cm_clksel2_pll &= ~0x3; - if (rate > low) { - tmpset.cm_clksel2_pll |= 0x2; - mult = ((rate / 2) / 1000000); - done_rate = PRCM_FULL_SPEED; - } else { - tmpset.cm_clksel2_pll |= 0x1; - mult = (rate / 1000000); - done_rate = PRCM_HALF_SPEED; - } - tmpset.cm_clksel1_pll |= ((div << 8) | (mult << 12)); - - /* Worst case */ - tmpset.base_sdrc_rfr = V24XX_SDRC_RFR_CTRL_BYPASS; + const struct clksel *clks; + const struct clksel_rate *clkr; - if (rate == curr_prcm_set->xtal_speed) /* If asking for 1-1 */ - bypass = 1; + /* should never happen */ + WARN_ON(div == 0); - omap2_reprogram_sdrc(PRCM_FULL_SPEED, 1); /* For init_mem */ - - /* Force dll lock mode */ - omap2_set_prcm(tmpset.cm_clksel1_pll, tmpset.base_sdrc_rfr, - bypass); + clks = omap2_get_clksel_by_parent(clk, clk->parent); + if (clks == NULL) + return 0; - /* Errata: ret dll entry state */ - omap2_init_memory_params(omap2_dll_force_needed()); - omap2_reprogram_sdrc(done_rate, 0); + for (clkr = clks->rates; clkr->div; clkr++) { + if ((clkr->flags & cpu_mask) && (clkr->div == div)) + break; } - omap2_clksel_recalc(&dpll_ck); - ret = 0; -dpll_exit: - local_irq_restore(flags); - return(ret); -} + if (!clkr->div) { + printk(KERN_ERR "clock: Could not find divisor %d for " + "clock %s parent %s\n", div, clk->name, + clk->parent->name); + return 0; + } -/* Just return the MPU speed */ -static void omap2_mpu_recalc(struct clk * clk) -{ - clk->rate = curr_prcm_set->mpu_speed; + return clkr->val; } -/* - * Look for a rate equal or less than the target rate given a configuration set. +/** + * 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 * - * What's not entirely clear is "which" field represents the key field. - * Some might argue L3-DDR, others ARM, others IVA. This code is simple and - * just uses the ARM rates. + * Returns the address of the clksel register upon success or NULL on error. */ -static long omap2_round_to_table_rate(struct clk * clk, unsigned long rate) +void __iomem *omap2_get_clksel(struct clk *clk, u32 *field_mask) { - struct prcm_config * ptr; - long highest_rate; - - if (clk != &virt_prcm_set) - return -EINVAL; + if (unlikely((clk->clksel_reg == 0) || (clk->clksel_mask == 0))) + return NULL; - highest_rate = -EINVAL; + *field_mask = clk->clksel_mask; - for (ptr = rate_table; ptr->mpu_speed; ptr++) { - if (ptr->xtal_speed != sys_ck.rate) - continue; - - highest_rate = ptr->mpu_speed; - - /* Can check only after xtal frequency check */ - if (ptr->mpu_speed <= rate) - break; - } - return highest_rate; + return clk->clksel_reg; } -/* - * omap2_convert_field_to_div() - turn field value into integer divider +/** + * omap2_clksel_get_divisor - get current divider applied to parent clock. + * @clk: OMAP struct clk to use. + * + * Returns the integer divisor upon success or 0 on error. */ -static u32 omap2_clksel_to_divisor(u32 div_sel, u32 field_val) +u32 omap2_clksel_get_divisor(struct clk *clk) { - u32 i; - u32 clkout_array[] = {1, 2, 4, 8, 16}; + u32 field_mask, field_val; + void __iomem *div_addr; - if ((div_sel & SRC_RATE_SEL_MASK) == CM_SYSCLKOUT_SEL1) { - for (i = 0; i < 5; i++) { - if (field_val == i) - return clkout_array[i]; - } - return ~0; - } else - return field_val; + div_addr = omap2_get_clksel(clk, &field_mask); + if (div_addr == 0) + return 0; + + field_val = __raw_readl(div_addr) & field_mask; + field_val >>= __ffs(field_mask); + + return omap2_clksel_to_divisor(clk, field_val); } -/* - * Returns the CLKSEL divider register value - * REVISIT: This should be cleaned up to work nicely with void __iomem * - */ -static u32 omap2_get_clksel(u32 *div_sel, u32 *field_mask, - struct clk *clk) +int omap2_clksel_set_rate(struct clk *clk, unsigned long rate) { - int ret = ~0; - u32 reg_val, div_off; - u32 div_addr = 0; - u32 mask = ~0; - - div_off = clk->rate_offset; - - switch ((*div_sel & SRC_RATE_SEL_MASK)) { - case CM_MPU_SEL1: - div_addr = (u32)&CM_CLKSEL_MPU; - mask = 0x1f; - break; - case CM_DSP_SEL1: - div_addr = (u32)&CM_CLKSEL_DSP; - if (cpu_is_omap2420()) { - if ((div_off == 0) || (div_off == 8)) - mask = 0x1f; - else if (div_off == 5) - mask = 0x3; - } else if (cpu_is_omap2430()) { - if (div_off == 0) - mask = 0x1f; - else if (div_off == 5) - mask = 0x3; - } - break; - case CM_GFX_SEL1: - div_addr = (u32)&CM_CLKSEL_GFX; - if (div_off == 0) - mask = 0x7; - break; - case CM_MODEM_SEL1: - div_addr = (u32)&CM_CLKSEL_MDM; - if (div_off == 0) - mask = 0xf; - break; - case CM_SYSCLKOUT_SEL1: - div_addr = (u32)&PRCM_CLKOUT_CTRL; - if ((div_off == 3) || (div_off = 11)) - mask= 0x3; - break; - case CM_CORE_SEL1: - div_addr = (u32)&CM_CLKSEL1_CORE; - switch (div_off) { - case 0: /* l3 */ - case 8: /* dss1 */ - case 15: /* vylnc-2420 */ - case 20: /* ssi */ - mask = 0x1f; break; - case 5: /* l4 */ - mask = 0x3; break; - case 13: /* dss2 */ - mask = 0x1; break; - case 25: /* usb */ - mask = 0x7; break; - } - } + u32 field_mask, field_val, reg_val, validrate, new_div = 0; + void __iomem *div_addr; - *field_mask = mask; + validrate = omap2_clksel_round_rate_div(clk, rate, &new_div); + if (validrate != rate) + return -EINVAL; - if (unlikely(mask == ~0)) - div_addr = 0; + div_addr = omap2_get_clksel(clk, &field_mask); + if (div_addr == 0) + return -EINVAL; - *div_sel = div_addr; + field_val = omap2_divisor_to_clksel(clk, new_div); + if (field_val == ~0) + return -EINVAL; - if (unlikely(div_addr == 0)) - return ret; + reg_val = __raw_readl(div_addr); + reg_val &= ~field_mask; + reg_val |= (field_val << __ffs(field_mask)); + __raw_writel(reg_val, div_addr); + wmb(); - /* Isolate field */ - reg_val = __raw_readl((void __iomem *)div_addr) & (mask << div_off); + clk->rate = clk->parent->rate / new_div; - /* Normalize back to divider value */ - reg_val >>= div_off; + if (clk->flags & DELAYED_APP && cpu_is_omap24xx()) { + __raw_writel(OMAP24XX_VALID_CONFIG, OMAP24XX_PRCM_CLKCFG_CTRL); + wmb(); + } - return reg_val; + return 0; } -/* - * Return divider to be applied to parent clock. - * Return 0 on error. - */ -static u32 omap2_clksel_get_divisor(struct clk *clk) -{ - int ret = 0; - u32 div, div_sel, div_off, field_mask, field_val; - - /* isolate control register */ - div_sel = (SRC_RATE_SEL_MASK & clk->flags); - - div_off = clk->rate_offset; - field_val = omap2_get_clksel(&div_sel, &field_mask, clk); - if (div_sel == 0) - return ret; - - div_sel = (SRC_RATE_SEL_MASK & clk->flags); - div = omap2_clksel_to_divisor(div_sel, field_val); - - return div; -} /* Set the clock rate for a clock source */ -static int omap2_clk_set_rate(struct clk *clk, unsigned long rate) - +int omap2_clk_set_rate(struct clk *clk, unsigned long rate) { int ret = -EINVAL; - void __iomem * reg; - u32 div_sel, div_off, field_mask, field_val, reg_val, validrate; - u32 new_div = 0; - - if (!(clk->flags & CONFIG_PARTICIPANT) && (clk->flags & RATE_CKCTL)) { - if (clk == &dpll_ck) - return omap2_reprogram_dpll(clk, rate); - - /* Isolate control register */ - div_sel = (SRC_RATE_SEL_MASK & clk->flags); - div_off = clk->rate_offset; - - validrate = omap2_clksel_round_rate(clk, rate, &new_div); - if (validrate != rate) - return(ret); - - field_val = omap2_get_clksel(&div_sel, &field_mask, clk); - if (div_sel == 0) - return ret; - - if (clk->flags & CM_SYSCLKOUT_SEL1) { - switch (new_div) { - case 16: - field_val = 4; - break; - case 8: - field_val = 3; - break; - case 4: - field_val = 2; - break; - case 2: - field_val = 1; - break; - case 1: - field_val = 0; - break; - } - } else - field_val = new_div; - reg = (void __iomem *)div_sel; + pr_debug("clock: set_rate for clock %s to rate %ld\n", clk->name, rate); - reg_val = __raw_readl(reg); - reg_val &= ~(field_mask << div_off); - reg_val |= (field_val << div_off); - __raw_writel(reg_val, reg); - wmb(); - clk->rate = clk->parent->rate / field_val; + /* CONFIG_PARTICIPANT clocks are changed only in sets via the + rate table mechanism, driven by mpu_speed */ + if (clk->flags & CONFIG_PARTICIPANT) + return -EINVAL; - if (clk->flags & DELAYED_APP) { - __raw_writel(0x1, (void __iomem *)&PRCM_CLKCFG_CTRL); - wmb(); - } - ret = 0; - } else if (clk->set_rate != 0) + /* dpll_ck, core_ck, virt_prcm_set; plus all clksel clocks */ + if (clk->set_rate != 0) ret = clk->set_rate(clk, rate); if (unlikely(ret == 0 && (clk->flags & RATE_PROPAGATES))) @@ -782,242 +631,92 @@ static int omap2_clk_set_rate(struct clk *clk, unsigned long rate) return ret; } -/* Converts encoded control register address into a full address */ -static u32 omap2_get_src_field(u32 *type_to_addr, u32 reg_offset, - struct clk *src_clk, u32 *field_mask) -{ - u32 val = ~0, src_reg_addr = 0, mask = 0; - - /* Find target control register.*/ - switch ((*type_to_addr & SRC_RATE_SEL_MASK)) { - case CM_CORE_SEL1: - src_reg_addr = (u32)&CM_CLKSEL1_CORE; - if (reg_offset == 13) { /* DSS2_fclk */ - mask = 0x1; - if (src_clk == &sys_ck) - val = 0; - if (src_clk == &func_48m_ck) - val = 1; - } else if (reg_offset == 8) { /* DSS1_fclk */ - mask = 0x1f; - if (src_clk == &sys_ck) - val = 0; - else if (src_clk == &core_ck) /* divided clock */ - val = 0x10; /* rate needs fixing */ - } else if ((reg_offset == 15) && cpu_is_omap2420()){ /*vlnyq*/ - mask = 0x1F; - if(src_clk == &func_96m_ck) - val = 0; - else if (src_clk == &core_ck) - val = 0x10; - } - break; - case CM_CORE_SEL2: - src_reg_addr = (u32)&CM_CLKSEL2_CORE; - mask = 0x3; - if (src_clk == &func_32k_ck) - val = 0x0; - if (src_clk == &sys_ck) - val = 0x1; - if (src_clk == &alt_ck) - val = 0x2; - break; - case CM_WKUP_SEL1: - src_reg_addr = (u32)&CM_CLKSEL_WKUP; - mask = 0x3; - if (src_clk == &func_32k_ck) - val = 0x0; - if (src_clk == &sys_ck) - val = 0x1; - if (src_clk == &alt_ck) - val = 0x2; - break; - case CM_PLL_SEL1: - src_reg_addr = (u32)&CM_CLKSEL1_PLL; - mask = 0x1; - if (reg_offset == 0x3) { - if (src_clk == &apll96_ck) - val = 0; - if (src_clk == &alt_ck) - val = 1; - } - else if (reg_offset == 0x5) { - if (src_clk == &apll54_ck) - val = 0; - if (src_clk == &alt_ck) - val = 1; - } - break; - case CM_PLL_SEL2: - src_reg_addr = (u32)&CM_CLKSEL2_PLL; - mask = 0x3; - if (src_clk == &func_32k_ck) - val = 0x0; - if (src_clk == &dpll_ck) - val = 0x2; - break; - case CM_SYSCLKOUT_SEL1: - src_reg_addr = (u32)&PRCM_CLKOUT_CTRL; - mask = 0x3; - if (src_clk == &dpll_ck) - val = 0; - if (src_clk == &sys_ck) - val = 1; - if (src_clk == &func_96m_ck) - val = 2; - if (src_clk == &func_54m_ck) - val = 3; - break; - } - - if (val == ~0) /* Catch errors in offset */ - *type_to_addr = 0; - else - *type_to_addr = src_reg_addr; - *field_mask = mask; - - return val; -} - -static int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent) +/* + * Converts encoded control register address into a full address + * On error, *src_addr will be returned as 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) { - void __iomem * reg; - u32 src_sel, src_off, field_val, field_mask, reg_val, rate; - int ret = -EINVAL; - - if (unlikely(clk->flags & CONFIG_PARTICIPANT)) - return ret; - - if (clk->flags & SRC_SEL_MASK) { /* On-chip SEL collection */ - src_sel = (SRC_RATE_SEL_MASK & clk->flags); - src_off = clk->src_offset; - - if (src_sel == 0) - goto set_parent_error; + const struct clksel *clks; + const struct clksel_rate *clkr; - field_val = omap2_get_src_field(&src_sel, src_off, new_parent, - &field_mask); + *parent_div = 0; + *src_addr = 0; - reg = (void __iomem *)src_sel; - - if (clk->usecount > 0) - _omap2_clk_disable(clk); - - /* Set new source value (previous dividers if any in effect) */ - reg_val = __raw_readl(reg) & ~(field_mask << src_off); - reg_val |= (field_val << src_off); - __raw_writel(reg_val, reg); - wmb(); - - if (clk->flags & DELAYED_APP) { - __raw_writel(0x1, (void __iomem *)&PRCM_CLKCFG_CTRL); - wmb(); - } - if (clk->usecount > 0) - _omap2_clk_enable(clk); - - clk->parent = new_parent; - - /* SRC_RATE_SEL_MASK clocks follow their parents rates.*/ - if ((new_parent == &core_ck) && (clk == &dss1_fck)) - clk->rate = new_parent->rate / 0x10; - else - clk->rate = new_parent->rate; + clks = omap2_get_clksel_by_parent(clk, src_clk); + if (clks == NULL) + return 0; - if (unlikely(clk->flags & RATE_PROPAGATES)) - propagate_rate(clk); + for (clkr = clks->rates; clkr->div; clkr++) { + if (clkr->flags & (cpu_mask | DEFAULT_RATE)) + break; /* Found the default rate for this platform */ + } + if (!clkr->div) { + printk(KERN_ERR "clock: Could not find default rate for " + "clock %s parent %s\n", clk->name, + src_clk->parent->name); return 0; - } else { - clk->parent = new_parent; - rate = new_parent->rate; - omap2_clk_set_rate(clk, rate); - ret = 0; } - set_parent_error: - return ret; + /* 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; + + return clkr->val; } -/* Sets basic clocks based on the specified rate */ -static int omap2_select_table_rate(struct clk * clk, unsigned long rate) +int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent) { - u32 flags, cur_rate, done_rate, bypass = 0; - u8 cpu_mask = 0; - struct prcm_config *prcm; - unsigned long found_speed = 0; + void __iomem *src_addr; + u32 field_val, field_mask, reg_val, parent_div; - if (clk != &virt_prcm_set) + if (unlikely(clk->flags & CONFIG_PARTICIPANT)) return -EINVAL; - /* FIXME: Change cpu_is_omap2420() to cpu_is_omap242x() */ - if (cpu_is_omap2420()) - cpu_mask = RATE_IN_242X; - else if (cpu_is_omap2430()) - cpu_mask = RATE_IN_243X; - - for (prcm = rate_table; prcm->mpu_speed; prcm++) { - if (!(prcm->flags & cpu_mask)) - continue; - - if (prcm->xtal_speed != sys_ck.rate) - continue; - - if (prcm->mpu_speed <= rate) { - found_speed = prcm->mpu_speed; - break; - } - } - - if (!found_speed) { - printk(KERN_INFO "Could not set MPU rate to %luMHz\n", - rate / 1000000); + if (!clk->clksel) return -EINVAL; - } - - curr_prcm_set = prcm; - cur_rate = omap2_get_dpll_rate(&dpll_ck); - if (prcm->dpll_speed == cur_rate / 2) { - omap2_reprogram_sdrc(PRCM_HALF_SPEED, 1); - } else if (prcm->dpll_speed == cur_rate * 2) { - omap2_reprogram_sdrc(PRCM_FULL_SPEED, 1); - } else if (prcm->dpll_speed != cur_rate) { - local_irq_save(flags); - - if (prcm->dpll_speed == prcm->xtal_speed) - bypass = 1; + field_val = omap2_clksel_get_src_field(&src_addr, new_parent, + &field_mask, clk, &parent_div); + if (src_addr == 0) + return -EINVAL; - if ((prcm->cm_clksel2_pll & 0x3) == 2) - done_rate = PRCM_FULL_SPEED; - else - done_rate = PRCM_HALF_SPEED; + if (clk->usecount > 0) + _omap2_clk_disable(clk); - /* MPU divider */ - CM_CLKSEL_MPU = prcm->cm_clksel_mpu; + /* 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(); - /* dsp + iva1 div(2420), iva2.1(2430) */ - CM_CLKSEL_DSP = prcm->cm_clksel_dsp; + if (clk->flags & DELAYED_APP && cpu_is_omap24xx()) { + __raw_writel(OMAP24XX_VALID_CONFIG, OMAP24XX_PRCM_CLKCFG_CTRL); + wmb(); + } - CM_CLKSEL_GFX = prcm->cm_clksel_gfx; + if (clk->usecount > 0) + _omap2_clk_enable(clk); - /* Major subsystem dividers */ - CM_CLKSEL1_CORE = prcm->cm_clksel1_core; - if (cpu_is_omap2430()) - CM_CLKSEL_MDM = prcm->cm_clksel_mdm; + clk->parent = new_parent; - /* x2 to enter init_mem */ - omap2_reprogram_sdrc(PRCM_FULL_SPEED, 1); + /* CLKSEL clocks follow their parents' rates, divided by a divisor */ + clk->rate = new_parent->rate; - omap2_set_prcm(prcm->cm_clksel1_pll, prcm->base_sdrc_rfr, - bypass); + if (parent_div > 0) + clk->rate /= parent_div; - omap2_init_memory_params(omap2_dll_force_needed()); - omap2_reprogram_sdrc(done_rate, 0); + pr_debug("clock: set parent of %s to %s (new rate %ld)\n", + clk->name, clk->parent->name, clk->rate); - local_irq_restore(flags); - } - omap2_clksel_recalc(&dpll_ck); + if (unlikely(clk->flags & RATE_PROPAGATES)) + propagate_rate(clk); return 0; } @@ -1027,159 +726,17 @@ static int omap2_select_table_rate(struct clk * clk, unsigned long rate) *-------------------------------------------------------------------------*/ #ifdef CONFIG_OMAP_RESET_CLOCKS -static void __init omap2_clk_disable_unused(struct clk *clk) +void omap2_clk_disable_unused(struct clk *clk) { - u32 regval32; + u32 regval32, v; + + v = (clk->flags & INVERT_ENABLE) ? (1 << clk->enable_bit) : 0; regval32 = __raw_readl(clk->enable_reg); - if ((regval32 & (1 << clk->enable_bit)) == 0) + if ((regval32 & (1 << clk->enable_bit)) == v) return; printk(KERN_INFO "Disabling unused clock \"%s\"\n", clk->name); _omap2_clk_disable(clk); } -#else -#define omap2_clk_disable_unused NULL #endif - -static struct clk_functions omap2_clk_functions = { - .clk_enable = omap2_clk_enable, - .clk_disable = omap2_clk_disable, - .clk_round_rate = omap2_clk_round_rate, - .clk_set_rate = omap2_clk_set_rate, - .clk_set_parent = omap2_clk_set_parent, - .clk_disable_unused = omap2_clk_disable_unused, -}; - -static void __init omap2_get_crystal_rate(struct clk *osc, struct clk *sys) -{ - u32 div, aplls, sclk = 13000000; - - aplls = CM_CLKSEL1_PLL; - aplls &= ((1 << 23) | (1 << 24) | (1 << 25)); - aplls >>= 23; /* Isolate field, 0,2,3 */ - - if (aplls == 0) - sclk = 19200000; - else if (aplls == 2) - sclk = 13000000; - else if (aplls == 3) - sclk = 12000000; - - div = PRCM_CLKSRC_CTRL; - div &= ((1 << 7) | (1 << 6)); - div >>= sys->rate_offset; - - osc->rate = sclk * div; - sys->rate = sclk; -} - -/* - * Set clocks for bypass mode for reboot to work. - */ -void omap2_clk_prepare_for_reboot(void) -{ - u32 rate; - - if (vclk == NULL || sclk == NULL) - return; - - rate = clk_get_rate(sclk); - clk_set_rate(vclk, rate); -} - -/* - * Switch the MPU rate if specified on cmdline. - * We cannot do this early until cmdline is parsed. - */ -static int __init omap2_clk_arch_init(void) -{ - if (!mpurate) - return -EINVAL; - - if (omap2_select_table_rate(&virt_prcm_set, mpurate)) - printk(KERN_ERR "Could not find matching MPU rate\n"); - - propagate_rate(&osc_ck); /* update main root fast */ - propagate_rate(&func_32k_ck); /* update main root slow */ - - printk(KERN_INFO "Switched to new clocking rate (Crystal/DPLL/MPU): " - "%ld.%01ld/%ld/%ld MHz\n", - (sys_ck.rate / 1000000), (sys_ck.rate / 100000) % 10, - (dpll_ck.rate / 1000000), (mpu_ck.rate / 1000000)) ; - - return 0; -} -arch_initcall(omap2_clk_arch_init); - -int __init omap2_clk_init(void) -{ - struct prcm_config *prcm; - struct clk ** clkp; - u32 clkrate; - - clk_init(&omap2_clk_functions); - omap2_get_crystal_rate(&osc_ck, &sys_ck); - - for (clkp = onchip_clks; clkp < onchip_clks + ARRAY_SIZE(onchip_clks); - clkp++) { - - if ((*clkp)->flags & CLOCK_IN_OMAP242X && cpu_is_omap2420()) { - clk_register(*clkp); - continue; - } - - if ((*clkp)->flags & CLOCK_IN_OMAP243X && cpu_is_omap2430()) { - clk_register(*clkp); - continue; - } - } - - /* Check the MPU rate set by bootloader */ - clkrate = omap2_get_dpll_rate(&dpll_ck); - for (prcm = rate_table; prcm->mpu_speed; prcm++) { - if (prcm->xtal_speed != sys_ck.rate) - continue; - if (prcm->dpll_speed <= clkrate) - break; - } - curr_prcm_set = prcm; - - propagate_rate(&osc_ck); /* update main root fast */ - propagate_rate(&func_32k_ck); /* update main root slow */ - - printk(KERN_INFO "Clocking rate (Crystal/DPLL/MPU): " - "%ld.%01ld/%ld/%ld MHz\n", - (sys_ck.rate / 1000000), (sys_ck.rate / 100000) % 10, - (dpll_ck.rate / 1000000), (mpu_ck.rate / 1000000)) ; - - /* - * Only enable those clocks we will need, let the drivers - * enable other clocks as necessary - */ - clk_enable(&sync_32k_ick); - clk_enable(&omapctrl_ick); - - /* Force the APLLs active during bootup to avoid disabling and - * enabling them unnecessarily. */ - clk_enable(&apll96_ck); - clk_enable(&apll54_ck); - - if (cpu_is_omap2430()) - clk_enable(&sdrc_ick); - - /* Avoid sleeping sleeping during omap2_clk_prepare_for_reboot() */ - vclk = clk_get(NULL, "virt_prcm_set"); - sclk = clk_get(NULL, "sys_ck"); - - return 0; -} - -static int __init omap2_disable_aplls(void) -{ - clk_disable(&apll96_ck); - clk_disable(&apll54_ck); - - return 0; -} -late_initcall(omap2_disable_aplls);