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;
 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                omap2xxx_cm_set_mod_dividers(prcm->cm_clksel_mpu,
 145                                             prcm->cm_clksel_dsp,
 146                                             prcm->cm_clksel_gfx,
 147                                             prcm->cm_clksel1_core,
 148                                             prcm->cm_clksel_mdm);
 149
 150                /* x2 to enter omap2xxx_sdrc_init_params() */
 151                omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL_X2, 1);
 152
 153                omap2_set_prcm(prcm->cm_clksel1_pll, prcm->base_sdrc_rfr,
 154                               bypass);
 155
 156                omap2xxx_sdrc_init_params(omap2xxx_sdrc_dll_is_unlocked());
 157                omap2xxx_sdrc_reprogram(done_rate, 0);
 158
 159                local_irq_restore(flags);
 160        }
 161
 162        return 0;
 163}
 164
 165/**
 166 * omap2xxx_clkt_vps_check_bootloader_rate - determine which of the rate
 167 * table sets matches the current CORE DPLL hardware rate
 168 *
 169 * Check the MPU rate set by bootloader.  Sets the 'curr_prcm_set'
 170 * global to point to the active rate set when found; otherwise, sets
 171 * it to NULL.  No return value;
 172 */
 173void omap2xxx_clkt_vps_check_bootloader_rates(void)
 174{
 175        const struct prcm_config *prcm = NULL;
 176        unsigned long rate;
 177
 178        rate = omap2xxx_clk_get_core_rate();
 179        for (prcm = rate_table; prcm->mpu_speed; prcm++) {
 180                if (!(prcm->flags & cpu_mask))
 181                        continue;
 182                if (prcm->xtal_speed != sys_ck_rate)
 183                        continue;
 184                if (prcm->dpll_speed <= rate)
 185                        break;
 186        }
 187        curr_prcm_set = prcm;
 188}
 189
 190/**
 191 * omap2xxx_clkt_vps_late_init - store a copy of the sys_ck rate
 192 *
 193 * Store a copy of the sys_ck rate for later use by the OMAP2xxx DVFS
 194 * code.  (The sys_ck rate does not -- or rather, must not -- change
 195 * during kernel runtime.)  Must be called after we have a valid
 196 * sys_ck rate, but before the virt_prcm_set clock rate is
 197 * recalculated.  No return value.
 198 */
 199void omap2xxx_clkt_vps_late_init(void)
 200{
 201        struct clk *c;
 202
 203        c = clk_get(NULL, "sys_ck");
 204        if (IS_ERR(c)) {
 205                WARN(1, "could not locate sys_ck\n");
 206        } else {
 207                sys_ck_rate = clk_get_rate(c);
 208                clk_put(c);
 209        }
 210}
 211
 212#ifdef CONFIG_OF
 213#include <linux/clk-provider.h>
 214#include <linux/clkdev.h>
 215
 216static const struct clk_ops virt_prcm_set_ops = {
 217        .recalc_rate    = &omap2_table_mpu_recalc,
 218        .set_rate       = &omap2_select_table_rate,
 219        .round_rate     = &omap2_round_to_table_rate,
 220};
 221
 222/**
 223 * omap2xxx_clkt_vps_init - initialize virt_prcm_set clock
 224 *
 225 * Does a manual init for the virtual prcm DVFS clock for OMAP2. This
 226 * function is called only from omap2 DT clock init, as the virtual
 227 * node is not modelled in the DT clock data.
 228 */
 229void omap2xxx_clkt_vps_init(void)
 230{
 231        struct clk_init_data init = { NULL };
 232        struct clk_hw_omap *hw = NULL;
 233        struct clk *clk;
 234        const char *parent_name = "mpu_ck";
 235
 236        omap2xxx_clkt_vps_late_init();
 237        omap2xxx_clkt_vps_check_bootloader_rates();
 238
 239        hw = kzalloc(sizeof(*hw), GFP_KERNEL);
 240        if (!hw)
 241                goto cleanup;
 242        init.name = "virt_prcm_set";
 243        init.ops = &virt_prcm_set_ops;
 244        init.parent_names = &parent_name;
 245        init.num_parents = 1;
 246
 247        hw->hw.init = &init;
 248
 249        clk = clk_register(NULL, &hw->hw);
 250        clkdev_create(clk, "cpufreq_ck", NULL);
 251        return;
 252cleanup:
 253        kfree(hw);
 254}
 255#endif
 256