From 3d876e59c819efcc4810a5945f9d780d05e28d93 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Tue, 18 Mar 2008 14:41:40 +0200 Subject: ARM: OMAP2: Move clock.c to clock24xx.c This patch moves clock.h to clock24xx.c to make room for adding common clock code for 24xx and 34xx. Signed-off-by: Paul Walmsley Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/Makefile | 5 +- arch/arm/mach-omap2/clock.c | 1176 --------------------------------------- arch/arm/mach-omap2/clock24xx.c | 1176 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 1180 insertions(+), 1177 deletions(-) delete mode 100644 arch/arm/mach-omap2/clock.c create mode 100644 arch/arm/mach-omap2/clock24xx.c diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index b9313c9c7420..be2b671552a6 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -3,12 +3,15 @@ # # Common support -obj-y := irq.o id.o io.o sram-fn.o memory.o control.o prcm.o clock.o mux.o \ +obj-y := irq.o id.o io.o sram-fn.o memory.o control.o prcm.o mux.o \ devices.o serial.o gpmc.o timer-gp.o # Power Management obj-$(CONFIG_PM) += pm.o pm-domain.o sleep.o +# Clock framework +obj-$(CONFIG_ARCH_OMAP2) += clock24xx.o + # Specific board support obj-$(CONFIG_MACH_OMAP_GENERIC) += board-generic.o obj-$(CONFIG_MACH_OMAP_H4) += board-h4.o diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c deleted file mode 100644 index 5c24781f6d9b..000000000000 --- a/arch/arm/mach-omap2/clock.c +++ /dev/null @@ -1,1176 +0,0 @@ -/* - * 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 - * - * Based on omap1 clock.c, Copyright (C) 2004 - 2005 Nokia corporation - * Written by Tuukka Tikkanen - * - * 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. - */ -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include "prcm-regs.h" -#include "memory.h" -#include "clock24xx.h" - -#undef DEBUG - -//#define DOWN_VARIABLE_DPLL 1 /* Experimental */ - -static struct prcm_config *curr_prcm_set; -static u32 curr_perf_level = PRCM_FULL_SPEED; -static struct clk *vclk; -static struct clk *sclk; - -/*------------------------------------------------------------------------- - * Omap2 specific clock functions - *-------------------------------------------------------------------------*/ - -/* Recalculate SYST_CLK */ -static void omap2_sys_clk_recalc(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); -} - -static u32 omap2_get_dpll_rate(struct clk * tclk) -{ - long long dpll_clk; - int dpll_mult, dpll_div, amult; - - 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; - 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) -{ - if (!(clk->flags & RATE_FIXED)) - clk->rate = clk->parent->rate; - - propagate_rate(clk); -} - -static void omap2_set_osc_ck(int enable) -{ - if (enable) - PRCM_CLKSRC_CTRL &= ~(0x3 << 3); - else - PRCM_CLKSRC_CTRL |= 0x3 << 3; -} - -/* Enable an APLL if off */ -static void omap2_clk_fixed_enable(struct clk *clk) -{ - u32 cval, i=0; - - if (clk->enable_bit == 0xff) /* Parent will do it */ - return; - - cval = CM_CLKEN_PLL; - - if ((cval & (0x3 << clk->enable_bit)) == (0x3 << clk->enable_bit)) - return; - - 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); - - 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; - } - } -} - -static void omap2_clk_wait_ready(struct clk *clk) -{ - unsigned long 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; - else - return; - - /* No check for DSS or cam clocks */ - if ((reg & 0x0f) == 0) { - if (clk->enable_bit <= 1 || clk->enable_bit == 31) - 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); -} - -/* 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) -{ - u32 regval32; - - if (clk->flags & ALWAYS_ENABLED) - return 0; - - if (unlikely(clk == &osc_ck)) { - omap2_set_osc_ck(1); - return 0; - } - - 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; - } - - regval32 = __raw_readl(clk->enable_reg); - regval32 |= (1 << clk->enable_bit); - __raw_writel(regval32, clk->enable_reg); - wmb(); - - omap2_clk_wait_ready(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) -{ - u32 regval32; - - if (unlikely(clk == &osc_ck)) { - omap2_set_osc_ck(0); - return; - } - - if (clk->enable_reg == 0) - return; - - if (clk->enable_reg == (void __iomem *)&CM_CLKEN_PLL) { - omap2_clk_fixed_disable(clk); - return; - } - - regval32 = __raw_readl(clk->enable_reg); - regval32 &= ~(1 << clk->enable_bit); - __raw_writel(regval32, clk->enable_reg); - wmb(); -} - -static 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; - } - - ret = _omap2_clk_enable(clk); - - if (unlikely(ret != 0) && clk->parent) { - omap2_clk_disable(clk->parent); - clk->usecount--; - } - } - - 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) -{ - u32 fixed = 0, div = 0; - - if (clk == &dpll_ck) { - clk->rate = omap2_get_dpll_rate(clk); - fixed = 1; - div = 0; - } - - if (clk == &iva1_mpu_int_ifck) { - div = 2; - fixed = 1; - } - - if ((clk == &dss1_fck) && ((CM_CLKSEL1_CORE & (0x1f << 8)) == 0)) { - clk->rate = sys_ck.rate; - return; - } - - if (!fixed) { - div = omap2_clksel_get_divisor(clk); - if (div == 0) - return; - } - - if (div != 0) { - if (unlikely(clk->rate == clk->parent->rate / div)) - return; - clk->rate = clk->parent->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. - */ -static inline u32 omap2_divider_from_table(u32 size, u32 *div_array, - u32 src_rate, u32 tgt_rate) -{ - int i, test_rate; - - if (div_array == NULL) - return ~1; - - for (i=0; i < size; i++) { - test_rate = src_rate / *div_array; - if (test_rate <= tgt_rate) - return *div_array; - ++div_array; - } - - return ~0; /* No acceptable divider */ -} - -/* - * Find divisor for the given clock and target rate. - * - * Note that this will not work for clocks which are part of CONFIG_PARTICIPANT, - * they are only settable as part of virtual_prcm set. - */ -static u32 omap2_clksel_round_rate(struct clk *tclk, u32 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; - } - - 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 */ - } - - *new_div = best_div; - return (tclk->parent->rate / best_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) -{ - u32 new_div = 0; - int valid_rate; - - if (clk->flags & RATE_FIXED) - return clk->rate; - - if (clk->flags & RATE_CKCTL) { - valid_rate = omap2_clksel_round_rate(clk, rate, &new_div); - return valid_rate; - } - - if (clk->round_rate != 0) - return clk->round_rate(clk, rate); - - 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. - */ -static u32 omap2_dll_force_needed(void) -{ - u32 dll_state = SDRC_DLLA_CTRL; /* dlla and dllb are a set */ - - if ((dll_state & (1 << 2)) == (1 << 2)) - return 1; - else - 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); - } - 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); - } - - return prev; -} - -static int omap2_reprogram_dpll(struct clk * clk, unsigned long rate) -{ - 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; - - if (rate == curr_prcm_set->xtal_speed) /* If asking for 1-1 */ - bypass = 1; - - 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); - - /* Errata: ret dll entry state */ - omap2_init_memory_params(omap2_dll_force_needed()); - omap2_reprogram_sdrc(done_rate, 0); - } - omap2_clksel_recalc(&dpll_ck); - ret = 0; - -dpll_exit: - local_irq_restore(flags); - return(ret); -} - -/* Just return the MPU speed */ -static void omap2_mpu_recalc(struct clk * clk) -{ - clk->rate = curr_prcm_set->mpu_speed; -} - -/* - * Look for a rate equal or less than the target rate given a configuration set. - * - * 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. - */ -static long omap2_round_to_table_rate(struct clk * clk, unsigned long rate) -{ - struct prcm_config * ptr; - long highest_rate; - - if (clk != &virt_prcm_set) - return -EINVAL; - - highest_rate = -EINVAL; - - 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; -} - -/* - * omap2_convert_field_to_div() - turn field value into integer divider - */ -static u32 omap2_clksel_to_divisor(u32 div_sel, u32 field_val) -{ - u32 i; - u32 clkout_array[] = {1, 2, 4, 8, 16}; - - 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; -} - -/* - * 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 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; - } - } - - *field_mask = mask; - - if (unlikely(mask == ~0)) - div_addr = 0; - - *div_sel = div_addr; - - if (unlikely(div_addr == 0)) - return ret; - - /* Isolate field */ - reg_val = __raw_readl((void __iomem *)div_addr) & (mask << div_off); - - /* Normalize back to divider value */ - reg_val >>= div_off; - - return reg_val; -} - -/* - * 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 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; - - 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; - - if (clk->flags & DELAYED_APP) { - __raw_writel(0x1, (void __iomem *)&PRCM_CLKCFG_CTRL); - wmb(); - } - ret = 0; - } else if (clk->set_rate != 0) - 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 */ -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) -{ - 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; - - field_val = omap2_get_src_field(&src_sel, src_off, new_parent, - &field_mask); - - 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; - - if (unlikely(clk->flags & RATE_PROPAGATES)) - propagate_rate(clk); - - return 0; - } else { - clk->parent = new_parent; - rate = new_parent->rate; - omap2_clk_set_rate(clk, rate); - ret = 0; - } - - set_parent_error: - return ret; -} - -/* Sets basic clocks based on the specified rate */ -static int omap2_select_table_rate(struct clk * clk, unsigned long rate) -{ - u32 flags, cur_rate, done_rate, bypass = 0; - u8 cpu_mask = 0; - struct prcm_config *prcm; - unsigned long found_speed = 0; - - if (clk != &virt_prcm_set) - 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); - 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; - - if ((prcm->cm_clksel2_pll & 0x3) == 2) - done_rate = PRCM_FULL_SPEED; - else - done_rate = PRCM_HALF_SPEED; - - /* MPU divider */ - CM_CLKSEL_MPU = prcm->cm_clksel_mpu; - - /* dsp + iva1 div(2420), iva2.1(2430) */ - CM_CLKSEL_DSP = prcm->cm_clksel_dsp; - - CM_CLKSEL_GFX = prcm->cm_clksel_gfx; - - /* Major subsystem dividers */ - CM_CLKSEL1_CORE = prcm->cm_clksel1_core; - if (cpu_is_omap2430()) - CM_CLKSEL_MDM = prcm->cm_clksel_mdm; - - /* x2 to enter init_mem */ - omap2_reprogram_sdrc(PRCM_FULL_SPEED, 1); - - omap2_set_prcm(prcm->cm_clksel1_pll, prcm->base_sdrc_rfr, - bypass); - - omap2_init_memory_params(omap2_dll_force_needed()); - omap2_reprogram_sdrc(done_rate, 0); - - local_irq_restore(flags); - } - omap2_clksel_recalc(&dpll_ck); - - return 0; -} - -/*------------------------------------------------------------------------- - * Omap2 clock reset and init functions - *-------------------------------------------------------------------------*/ - -#ifdef CONFIG_OMAP_RESET_CLOCKS -static void __init omap2_clk_disable_unused(struct clk *clk) -{ - u32 regval32; - - regval32 = __raw_readl(clk->enable_reg); - if ((regval32 & (1 << clk->enable_bit)) == 0) - 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 always active. The clocks are idled - * automatically by hardware. */ - 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; -} diff --git a/arch/arm/mach-omap2/clock24xx.c b/arch/arm/mach-omap2/clock24xx.c new file mode 100644 index 000000000000..5c24781f6d9b --- /dev/null +++ b/arch/arm/mach-omap2/clock24xx.c @@ -0,0 +1,1176 @@ +/* + * 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 + * + * Based on omap1 clock.c, Copyright (C) 2004 - 2005 Nokia corporation + * Written by Tuukka Tikkanen + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "prcm-regs.h" +#include "memory.h" +#include "clock24xx.h" + +#undef DEBUG + +//#define DOWN_VARIABLE_DPLL 1 /* Experimental */ + +static struct prcm_config *curr_prcm_set; +static u32 curr_perf_level = PRCM_FULL_SPEED; +static struct clk *vclk; +static struct clk *sclk; + +/*------------------------------------------------------------------------- + * Omap2 specific clock functions + *-------------------------------------------------------------------------*/ + +/* Recalculate SYST_CLK */ +static void omap2_sys_clk_recalc(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); +} + +static u32 omap2_get_dpll_rate(struct clk * tclk) +{ + long long dpll_clk; + int dpll_mult, dpll_div, amult; + + 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; + 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) +{ + if (!(clk->flags & RATE_FIXED)) + clk->rate = clk->parent->rate; + + propagate_rate(clk); +} + +static void omap2_set_osc_ck(int enable) +{ + if (enable) + PRCM_CLKSRC_CTRL &= ~(0x3 << 3); + else + PRCM_CLKSRC_CTRL |= 0x3 << 3; +} + +/* Enable an APLL if off */ +static void omap2_clk_fixed_enable(struct clk *clk) +{ + u32 cval, i=0; + + if (clk->enable_bit == 0xff) /* Parent will do it */ + return; + + cval = CM_CLKEN_PLL; + + if ((cval & (0x3 << clk->enable_bit)) == (0x3 << clk->enable_bit)) + return; + + 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); + + 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; + } + } +} + +static void omap2_clk_wait_ready(struct clk *clk) +{ + unsigned long 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; + else + return; + + /* No check for DSS or cam clocks */ + if ((reg & 0x0f) == 0) { + if (clk->enable_bit <= 1 || clk->enable_bit == 31) + 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); +} + +/* 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) +{ + u32 regval32; + + if (clk->flags & ALWAYS_ENABLED) + return 0; + + if (unlikely(clk == &osc_ck)) { + omap2_set_osc_ck(1); + return 0; + } + + 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; + } + + regval32 = __raw_readl(clk->enable_reg); + regval32 |= (1 << clk->enable_bit); + __raw_writel(regval32, clk->enable_reg); + wmb(); + + omap2_clk_wait_ready(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) +{ + u32 regval32; + + if (unlikely(clk == &osc_ck)) { + omap2_set_osc_ck(0); + return; + } + + if (clk->enable_reg == 0) + return; + + if (clk->enable_reg == (void __iomem *)&CM_CLKEN_PLL) { + omap2_clk_fixed_disable(clk); + return; + } + + regval32 = __raw_readl(clk->enable_reg); + regval32 &= ~(1 << clk->enable_bit); + __raw_writel(regval32, clk->enable_reg); + wmb(); +} + +static 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; + } + + ret = _omap2_clk_enable(clk); + + if (unlikely(ret != 0) && clk->parent) { + omap2_clk_disable(clk->parent); + clk->usecount--; + } + } + + 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) +{ + u32 fixed = 0, div = 0; + + if (clk == &dpll_ck) { + clk->rate = omap2_get_dpll_rate(clk); + fixed = 1; + div = 0; + } + + if (clk == &iva1_mpu_int_ifck) { + div = 2; + fixed = 1; + } + + if ((clk == &dss1_fck) && ((CM_CLKSEL1_CORE & (0x1f << 8)) == 0)) { + clk->rate = sys_ck.rate; + return; + } + + if (!fixed) { + div = omap2_clksel_get_divisor(clk); + if (div == 0) + return; + } + + if (div != 0) { + if (unlikely(clk->rate == clk->parent->rate / div)) + return; + clk->rate = clk->parent->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. + */ +static inline u32 omap2_divider_from_table(u32 size, u32 *div_array, + u32 src_rate, u32 tgt_rate) +{ + int i, test_rate; + + if (div_array == NULL) + return ~1; + + for (i=0; i < size; i++) { + test_rate = src_rate / *div_array; + if (test_rate <= tgt_rate) + return *div_array; + ++div_array; + } + + return ~0; /* No acceptable divider */ +} + +/* + * Find divisor for the given clock and target rate. + * + * Note that this will not work for clocks which are part of CONFIG_PARTICIPANT, + * they are only settable as part of virtual_prcm set. + */ +static u32 omap2_clksel_round_rate(struct clk *tclk, u32 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; + } + + 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 */ + } + + *new_div = best_div; + return (tclk->parent->rate / best_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) +{ + u32 new_div = 0; + int valid_rate; + + if (clk->flags & RATE_FIXED) + return clk->rate; + + if (clk->flags & RATE_CKCTL) { + valid_rate = omap2_clksel_round_rate(clk, rate, &new_div); + return valid_rate; + } + + if (clk->round_rate != 0) + return clk->round_rate(clk, rate); + + 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. + */ +static u32 omap2_dll_force_needed(void) +{ + u32 dll_state = SDRC_DLLA_CTRL; /* dlla and dllb are a set */ + + if ((dll_state & (1 << 2)) == (1 << 2)) + return 1; + else + 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); + } + 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); + } + + return prev; +} + +static int omap2_reprogram_dpll(struct clk * clk, unsigned long rate) +{ + 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; + + if (rate == curr_prcm_set->xtal_speed) /* If asking for 1-1 */ + bypass = 1; + + 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); + + /* Errata: ret dll entry state */ + omap2_init_memory_params(omap2_dll_force_needed()); + omap2_reprogram_sdrc(done_rate, 0); + } + omap2_clksel_recalc(&dpll_ck); + ret = 0; + +dpll_exit: + local_irq_restore(flags); + return(ret); +} + +/* Just return the MPU speed */ +static void omap2_mpu_recalc(struct clk * clk) +{ + clk->rate = curr_prcm_set->mpu_speed; +} + +/* + * Look for a rate equal or less than the target rate given a configuration set. + * + * 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. + */ +static long omap2_round_to_table_rate(struct clk * clk, unsigned long rate) +{ + struct prcm_config * ptr; + long highest_rate; + + if (clk != &virt_prcm_set) + return -EINVAL; + + highest_rate = -EINVAL; + + 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; +} + +/* + * omap2_convert_field_to_div() - turn field value into integer divider + */ +static u32 omap2_clksel_to_divisor(u32 div_sel, u32 field_val) +{ + u32 i; + u32 clkout_array[] = {1, 2, 4, 8, 16}; + + 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; +} + +/* + * 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 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; + } + } + + *field_mask = mask; + + if (unlikely(mask == ~0)) + div_addr = 0; + + *div_sel = div_addr; + + if (unlikely(div_addr == 0)) + return ret; + + /* Isolate field */ + reg_val = __raw_readl((void __iomem *)div_addr) & (mask << div_off); + + /* Normalize back to divider value */ + reg_val >>= div_off; + + return reg_val; +} + +/* + * 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 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; + + 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; + + if (clk->flags & DELAYED_APP) { + __raw_writel(0x1, (void __iomem *)&PRCM_CLKCFG_CTRL); + wmb(); + } + ret = 0; + } else if (clk->set_rate != 0) + 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 */ +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) +{ + 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; + + field_val = omap2_get_src_field(&src_sel, src_off, new_parent, + &field_mask); + + 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; + + if (unlikely(clk->flags & RATE_PROPAGATES)) + propagate_rate(clk); + + return 0; + } else { + clk->parent = new_parent; + rate = new_parent->rate; + omap2_clk_set_rate(clk, rate); + ret = 0; + } + + set_parent_error: + return ret; +} + +/* Sets basic clocks based on the specified rate */ +static int omap2_select_table_rate(struct clk * clk, unsigned long rate) +{ + u32 flags, cur_rate, done_rate, bypass = 0; + u8 cpu_mask = 0; + struct prcm_config *prcm; + unsigned long found_speed = 0; + + if (clk != &virt_prcm_set) + 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); + 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; + + if ((prcm->cm_clksel2_pll & 0x3) == 2) + done_rate = PRCM_FULL_SPEED; + else + done_rate = PRCM_HALF_SPEED; + + /* MPU divider */ + CM_CLKSEL_MPU = prcm->cm_clksel_mpu; + + /* dsp + iva1 div(2420), iva2.1(2430) */ + CM_CLKSEL_DSP = prcm->cm_clksel_dsp; + + CM_CLKSEL_GFX = prcm->cm_clksel_gfx; + + /* Major subsystem dividers */ + CM_CLKSEL1_CORE = prcm->cm_clksel1_core; + if (cpu_is_omap2430()) + CM_CLKSEL_MDM = prcm->cm_clksel_mdm; + + /* x2 to enter init_mem */ + omap2_reprogram_sdrc(PRCM_FULL_SPEED, 1); + + omap2_set_prcm(prcm->cm_clksel1_pll, prcm->base_sdrc_rfr, + bypass); + + omap2_init_memory_params(omap2_dll_force_needed()); + omap2_reprogram_sdrc(done_rate, 0); + + local_irq_restore(flags); + } + omap2_clksel_recalc(&dpll_ck); + + return 0; +} + +/*------------------------------------------------------------------------- + * Omap2 clock reset and init functions + *-------------------------------------------------------------------------*/ + +#ifdef CONFIG_OMAP_RESET_CLOCKS +static void __init omap2_clk_disable_unused(struct clk *clk) +{ + u32 regval32; + + regval32 = __raw_readl(clk->enable_reg); + if ((regval32 & (1 << clk->enable_bit)) == 0) + 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 always active. The clocks are idled + * automatically by hardware. */ + 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; +} -- cgit v1.2.3