linux/arch/arm/mach-omap2/clkt2xxx_dpllcore.c
<<
>>
Prefs
   1/*
   2 * DPLL + CORE_CLK composite clock functions
   3 *
   4 * Copyright (C) 2005-2008 Texas Instruments, Inc.
   5 * Copyright (C) 2004-2010 Nokia Corporation
   6 *
   7 * Contacts:
   8 * Richard Woodruff <r-woodruff2@ti.com>
   9 * Paul Walmsley
  10 *
  11 * Based on earlier work by Tuukka Tikkanen, Tony Lindgren,
  12 * Gordon McNutt and RidgeRun, Inc.
  13 *
  14 * This program is free software; you can redistribute it and/or modify
  15 * it under the terms of the GNU General Public License version 2 as
  16 * published by the Free Software Foundation.
  17 *
  18 * XXX The DPLL and CORE clocks should be split into two separate clock
  19 * types.
  20 */
  21#undef DEBUG
  22
  23#include <linux/kernel.h>
  24#include <linux/errno.h>
  25#include <linux/clk.h>
  26#include <linux/io.h>
  27
  28#include <plat/clock.h>
  29#include <plat/sram.h>
  30#include <plat/sdrc.h>
  31
  32#include "clock.h"
  33#include "clock2xxx.h"
  34#include "opp2xxx.h"
  35#include "cm2xxx_3xxx.h"
  36#include "cm-regbits-24xx.h"
  37
  38/* #define DOWN_VARIABLE_DPLL 1 */              /* Experimental */
  39
  40/**
  41 * omap2xxx_clk_get_core_rate - return the CORE_CLK rate
  42 * @clk: pointer to the combined dpll_ck + core_ck (currently "dpll_ck")
  43 *
  44 * Returns the CORE_CLK rate.  CORE_CLK can have one of three rate
  45 * sources on OMAP2xxx: the DPLL CLKOUT rate, DPLL CLKOUTX2, or 32KHz
  46 * (the latter is unusual).  This currently should be called with
  47 * struct clk *dpll_ck, which is a composite clock of dpll_ck and
  48 * core_ck.
  49 */
  50unsigned long omap2xxx_clk_get_core_rate(struct clk *clk)
  51{
  52        long long core_clk;
  53        u32 v;
  54
  55        core_clk = omap2_get_dpll_rate(clk);
  56
  57        v = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKSEL2);
  58        v &= OMAP24XX_CORE_CLK_SRC_MASK;
  59
  60        if (v == CORE_CLK_SRC_32K)
  61                core_clk = 32768;
  62        else
  63                core_clk *= v;
  64
  65        return core_clk;
  66}
  67
  68/*
  69 * Uses the current prcm set to tell if a rate is valid.
  70 * You can go slower, but not faster within a given rate set.
  71 */
  72static long omap2_dpllcore_round_rate(unsigned long target_rate)
  73{
  74        u32 high, low, core_clk_src;
  75
  76        core_clk_src = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKSEL2);
  77        core_clk_src &= OMAP24XX_CORE_CLK_SRC_MASK;
  78
  79        if (core_clk_src == CORE_CLK_SRC_DPLL) {        /* DPLL clockout */
  80                high = curr_prcm_set->dpll_speed * 2;
  81                low = curr_prcm_set->dpll_speed;
  82        } else {                                /* DPLL clockout x 2 */
  83                high = curr_prcm_set->dpll_speed;
  84                low = curr_prcm_set->dpll_speed / 2;
  85        }
  86
  87#ifdef DOWN_VARIABLE_DPLL
  88        if (target_rate > high)
  89                return high;
  90        else
  91                return target_rate;
  92#else
  93        if (target_rate > low)
  94                return high;
  95        else
  96                return low;
  97#endif
  98
  99}
 100
 101unsigned long omap2_dpllcore_recalc(struct clk *clk)
 102{
 103        return omap2xxx_clk_get_core_rate(clk);
 104}
 105
 106int omap2_reprogram_dpllcore(struct clk *clk, unsigned long rate)
 107{
 108        u32 cur_rate, low, mult, div, valid_rate, done_rate;
 109        u32 bypass = 0;
 110        struct prcm_config tmpset;
 111        const struct dpll_data *dd;
 112
 113        cur_rate = omap2xxx_clk_get_core_rate(dclk);
 114        mult = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKSEL2);
 115        mult &= OMAP24XX_CORE_CLK_SRC_MASK;
 116
 117        if ((rate == (cur_rate / 2)) && (mult == 2)) {
 118                omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL, 1);
 119        } else if ((rate == (cur_rate * 2)) && (mult == 1)) {
 120                omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL_X2, 1);
 121        } else if (rate != cur_rate) {
 122                valid_rate = omap2_dpllcore_round_rate(rate);
 123                if (valid_rate != rate)
 124                        return -EINVAL;
 125
 126                if (mult == 1)
 127                        low = curr_prcm_set->dpll_speed;
 128                else
 129                        low = curr_prcm_set->dpll_speed / 2;
 130
 131                dd = clk->dpll_data;
 132                if (!dd)
 133                        return -EINVAL;
 134
 135                tmpset.cm_clksel1_pll = __raw_readl(dd->mult_div1_reg);
 136                tmpset.cm_clksel1_pll &= ~(dd->mult_mask |
 137                                           dd->div1_mask);
 138                div = ((curr_prcm_set->xtal_speed / 1000000) - 1);
 139                tmpset.cm_clksel2_pll = omap2_cm_read_mod_reg(PLL_MOD, CM_CLKSEL2);
 140                tmpset.cm_clksel2_pll &= ~OMAP24XX_CORE_CLK_SRC_MASK;
 141                if (rate > low) {
 142                        tmpset.cm_clksel2_pll |= CORE_CLK_SRC_DPLL_X2;
 143                        mult = ((rate / 2) / 1000000);
 144                        done_rate = CORE_CLK_SRC_DPLL_X2;
 145                } else {
 146                        tmpset.cm_clksel2_pll |= CORE_CLK_SRC_DPLL;
 147                        mult = (rate / 1000000);
 148                        done_rate = CORE_CLK_SRC_DPLL;
 149                }
 150                tmpset.cm_clksel1_pll |= (div << __ffs(dd->mult_mask));
 151                tmpset.cm_clksel1_pll |= (mult << __ffs(dd->div1_mask));
 152
 153                /* Worst case */
 154                tmpset.base_sdrc_rfr = SDRC_RFR_CTRL_BYPASS;
 155
 156                if (rate == curr_prcm_set->xtal_speed)  /* If asking for 1-1 */
 157                        bypass = 1;
 158
 159                /* For omap2xxx_sdrc_init_params() */
 160                omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL_X2, 1);
 161
 162                /* Force dll lock mode */
 163                omap2_set_prcm(tmpset.cm_clksel1_pll, tmpset.base_sdrc_rfr,
 164                               bypass);
 165
 166                /* Errata: ret dll entry state */
 167                omap2xxx_sdrc_init_params(omap2xxx_sdrc_dll_is_unlocked());
 168                omap2xxx_sdrc_reprogram(done_rate, 0);
 169        }
 170
 171        return 0;
 172}
 173
 174