linux/arch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c
<<
>>
Prefs
   1/*
   2 * OMAP2xxx DVFS virtual clock functions
   3 *
   4 * Copyright (C) 2005-2008, 2012 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 Some of this code should be replaceable by the upcoming OPP layer
  19 * code.  However, some notion of "rate set" is probably still necessary
  20 * for OMAP2xxx at least.  Rate sets should be generalized so they can be
  21 * used for any OMAP chip, not just OMAP2xxx.  In particular, Richard Woodruff
  22 * has in the past expressed a preference to use rate sets for OPP changes,
  23 * rather than dynamically recalculating the clock tree, so if someone wants
  24 * this badly enough to write the code to handle it, we should support it
  25 * as an option.
  26 */
  27#undef DEBUG
  28
  29#include <linux/kernel.h>
  30#include <linux/errno.h>
  31#include <linux/clk.h>
  32#include <linux/io.h>
  33#include <linux/cpufreq.h>
  34#include <linux/slab.h>
  35
  36#include "soc.h"
  37#include "clock.h"
  38#include "clock2xxx.h"
  39#include "opp2xxx.h"
  40#include "cm2xxx.h"
  41#include "cm-regbits-24xx.h"
  42#include "sdrc.h"
  43#include "sram.h"
  44
  45const struct prcm_config *curr_prcm_set;
  46const struct prcm_config *rate_table;
  47
  48/*
  49 * sys_ck_rate: the rate of the external high-frequency clock
  50 * oscillator on the board.  Set by the SoC-specific clock init code.
  51 * Once set during a boot, will not change.
  52 */
  53static unsigned long sys_ck_rate;
  54
  55/**
  56 * omap2_table_mpu_recalc - just return the MPU speed
  57 * @clk: virt_prcm_set struct clk
  58 *
  59 * Set virt_prcm_set's rate to the mpu_speed field of the current PRCM set.
  60 */
  61unsigned long omap2_table_mpu_recalc(struct clk_hw *clk,
  62                                     unsigned long parent_rate)
  63{
  64        return curr_prcm_set->mpu_speed;
  65}
  66
  67/*
  68 * Look for a rate equal or less than the target rate given a configuration set.
  69 *
  70 * What's not entirely clear is "which" field represents the key field.
  71 * Some might argue L3-DDR, others ARM, others IVA. This code is simple and
  72 * just uses the ARM rates.
  73 */
  74long omap2_round_to_table_rate(struct clk_hw *hw, unsigned long rate,
  75                               unsigned long *parent_rate)
  76{
  77        const struct prcm_config *ptr;
  78        long highest_rate;
  79
  80        highest_rate = -EINVAL;
  81
  82        for (ptr = rate_table; ptr->mpu_speed; ptr++) {
  83                if (!(ptr->flags & cpu_mask))
  84                        continue;
  85                if (ptr->xtal_speed != sys_ck_rate)
  86                        continue;
  87
  88                highest_rate = ptr->mpu_speed;
  89
  90                /* Can check only after xtal frequency check */
  91                if (ptr->mpu_speed <= rate)
  92                        break;
  93        }
  94        return highest_rate;
  95}
  96
  97/* Sets basic clocks based on the specified rate */
  98int omap2_select_table_rate(struct clk_hw *hw, unsigned long rate,
  99                            unsigned long parent_rate)
 100{
 101        u32 cur_rate, done_rate, bypass = 0, tmp;
 102        const struct prcm_config *prcm;
 103        unsigned long found_speed = 0;
 104        unsigned long flags;
 105
 106        for (prcm = rate_table; prcm->mpu_speed; prcm++) {
 107                if (!(prcm->flags & cpu_mask))
 108                        continue;
 109
 110                if (prcm->xtal_speed != sys_ck_rate)
 111                        continue;
 112
 113                if (prcm->mpu_speed <= rate) {
 114                        found_speed = prcm->mpu_speed;
 115                        break;
 116                }
 117        }
 118
 119        if (!found_speed) {
 120                printk(KERN_INFO "Could not set MPU rate to %luMHz\n",
 121                       rate / 1000000);
 122                return -EINVAL;
 123        }
 124
 125        curr_prcm_set = prcm;
 126        cur_rate = omap2xxx_clk_get_core_rate();
 127
 128        if (prcm->dpll_speed == cur_rate / 2) {
 129                omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL, 1);
 130        } else if (prcm->dpll_speed == cur_rate * 2) {
 131                omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL_X2, 1);
 132        } else if (prcm->dpll_speed != cur_rate) {
 133                local_irq_save(flags);
 134
 135                if (prcm->dpll_speed == prcm->xtal_speed)
 136                        bypass = 1;
 137
 138                if ((prcm->cm_clksel2_pll & OMAP24XX_CORE_CLK_SRC_MASK) ==
 139                    CORE_CLK_SRC_DPLL_X2)
 140                        done_rate = CORE_CLK_SRC_DPLL_X2;
 141                else
 142                        done_rate = CORE_CLK_SRC_DPLL;
 143
 144                /* MPU divider */
 145                omap2_cm_write_mod_reg(prcm->cm_clksel_mpu, MPU_MOD, CM_CLKSEL);
 146
 147                /* dsp + iva1 div(2420), iva2.1(2430) */
 148                omap2_cm_write_mod_reg(prcm->cm_clksel_dsp,
 149                                 OMAP24XX_DSP_MOD, CM_CLKSEL);
 150
 151                omap2_cm_write_mod_reg(prcm->cm_clksel_gfx, GFX_MOD, CM_CLKSEL);
 152
 153                /* Major subsystem dividers */
 154                tmp = omap2_cm_read_mod_reg(CORE_MOD, CM_CLKSEL1) & OMAP24XX_CLKSEL_DSS2_MASK;
 155                omap2_cm_write_mod_reg(prcm->cm_clksel1_core | tmp, CORE_MOD,
 156                                 CM_CLKSEL1);
 157
 158                if (cpu_is_omap2430())
 159                        omap2_cm_write_mod_reg(prcm->cm_clksel_mdm,
 160                                         OMAP2430_MDM_MOD, CM_CLKSEL);
 161
 162                /* x2 to enter omap2xxx_sdrc_init_params() */
 163                omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL_X2, 1);
 164
 165                omap2_set_prcm(prcm->cm_clksel1_pll, prcm->base_sdrc_rfr,
 166                               bypass);
 167
 168                omap2xxx_sdrc_init_params(omap2xxx_sdrc_dll_is_unlocked());
 169                omap2xxx_sdrc_reprogram(done_rate, 0);
 170
 171                local_irq_restore(flags);
 172        }
 173
 174        return 0;
 175}
 176
 177/**
 178 * omap2xxx_clkt_vps_check_bootloader_rate - determine which of the rate
 179 * table sets matches the current CORE DPLL hardware rate
 180 *
 181 * Check the MPU rate set by bootloader.  Sets the 'curr_prcm_set'
 182 * global to point to the active rate set when found; otherwise, sets
 183 * it to NULL.  No return value;
 184 */
 185void omap2xxx_clkt_vps_check_bootloader_rates(void)
 186{
 187        const struct prcm_config *prcm = NULL;
 188        unsigned long rate;
 189
 190        rate = omap2xxx_clk_get_core_rate();
 191        for (prcm = rate_table; prcm->mpu_speed; prcm++) {
 192                if (!(prcm->flags & cpu_mask))
 193                        continue;
 194                if (prcm->xtal_speed != sys_ck_rate)
 195                        continue;
 196                if (prcm->dpll_speed <= rate)
 197                        break;
 198        }
 199        curr_prcm_set = prcm;
 200}
 201
 202/**
 203 * omap2xxx_clkt_vps_late_init - store a copy of the sys_ck rate
 204 *
 205 * Store a copy of the sys_ck rate for later use by the OMAP2xxx DVFS
 206 * code.  (The sys_ck rate does not -- or rather, must not -- change
 207 * during kernel runtime.)  Must be called after we have a valid
 208 * sys_ck rate, but before the virt_prcm_set clock rate is
 209 * recalculated.  No return value.
 210 */
 211void omap2xxx_clkt_vps_late_init(void)
 212{
 213        struct clk *c;
 214
 215        c = clk_get(NULL, "sys_ck");
 216        if (IS_ERR(c)) {
 217                WARN(1, "could not locate sys_ck\n");
 218        } else {
 219                sys_ck_rate = clk_get_rate(c);
 220                clk_put(c);
 221        }
 222}
 223