root/arch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. omap2_table_mpu_recalc
  2. omap2_round_to_table_rate
  3. omap2_select_table_rate
  4. omap2xxx_clkt_vps_check_bootloader_rates
  5. omap2xxx_clkt_vps_late_init
  6. omap2xxx_clkt_vps_init

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * OMAP2xxx DVFS virtual clock functions
   4  *
   5  * Copyright (C) 2005-2008, 2012 Texas Instruments, Inc.
   6  * Copyright (C) 2004-2010 Nokia Corporation
   7  *
   8  * Contacts:
   9  * Richard Woodruff <r-woodruff2@ti.com>
  10  * Paul Walmsley
  11  *
  12  * Based on earlier work by Tuukka Tikkanen, Tony Lindgren,
  13  * Gordon McNutt and RidgeRun, Inc.
  14  *
  15  * XXX Some of this code should be replaceable by the upcoming OPP layer
  16  * code.  However, some notion of "rate set" is probably still necessary
  17  * for OMAP2xxx at least.  Rate sets should be generalized so they can be
  18  * used for any OMAP chip, not just OMAP2xxx.  In particular, Richard Woodruff
  19  * has in the past expressed a preference to use rate sets for OPP changes,
  20  * rather than dynamically recalculating the clock tree, so if someone wants
  21  * this badly enough to write the code to handle it, we should support it
  22  * as an option.
  23  */
  24 #undef DEBUG
  25 
  26 #include <linux/kernel.h>
  27 #include <linux/errno.h>
  28 #include <linux/clk.h>
  29 #include <linux/io.h>
  30 #include <linux/cpufreq.h>
  31 #include <linux/slab.h>
  32 
  33 #include "soc.h"
  34 #include "clock.h"
  35 #include "clock2xxx.h"
  36 #include "opp2xxx.h"
  37 #include "cm2xxx.h"
  38 #include "cm-regbits-24xx.h"
  39 #include "sdrc.h"
  40 #include "sram.h"
  41 
  42 const struct prcm_config *curr_prcm_set;
  43 const struct prcm_config *rate_table;
  44 
  45 /*
  46  * sys_ck_rate: the rate of the external high-frequency clock
  47  * oscillator on the board.  Set by the SoC-specific clock init code.
  48  * Once set during a boot, will not change.
  49  */
  50 static unsigned long sys_ck_rate;
  51 
  52 /**
  53  * omap2_table_mpu_recalc - just return the MPU speed
  54  * @clk: virt_prcm_set struct clk
  55  *
  56  * Set virt_prcm_set's rate to the mpu_speed field of the current PRCM set.
  57  */
  58 unsigned long omap2_table_mpu_recalc(struct clk_hw *clk,
  59                                      unsigned long parent_rate)
  60 {
  61         return curr_prcm_set->mpu_speed;
  62 }
  63 
  64 /*
  65  * Look for a rate equal or less than the target rate given a configuration set.
  66  *
  67  * What's not entirely clear is "which" field represents the key field.
  68  * Some might argue L3-DDR, others ARM, others IVA. This code is simple and
  69  * just uses the ARM rates.
  70  */
  71 long omap2_round_to_table_rate(struct clk_hw *hw, unsigned long rate,
  72                                unsigned long *parent_rate)
  73 {
  74         const struct prcm_config *ptr;
  75         long highest_rate;
  76 
  77         highest_rate = -EINVAL;
  78 
  79         for (ptr = rate_table; ptr->mpu_speed; ptr++) {
  80                 if (!(ptr->flags & cpu_mask))
  81                         continue;
  82                 if (ptr->xtal_speed != sys_ck_rate)
  83                         continue;
  84 
  85                 highest_rate = ptr->mpu_speed;
  86 
  87                 /* Can check only after xtal frequency check */
  88                 if (ptr->mpu_speed <= rate)
  89                         break;
  90         }
  91         return highest_rate;
  92 }
  93 
  94 /* Sets basic clocks based on the specified rate */
  95 int omap2_select_table_rate(struct clk_hw *hw, unsigned long rate,
  96                             unsigned long parent_rate)
  97 {
  98         u32 cur_rate, done_rate, bypass = 0;
  99         const struct prcm_config *prcm;
 100         unsigned long found_speed = 0;
 101         unsigned long flags;
 102 
 103         for (prcm = rate_table; prcm->mpu_speed; prcm++) {
 104                 if (!(prcm->flags & cpu_mask))
 105                         continue;
 106 
 107                 if (prcm->xtal_speed != sys_ck_rate)
 108                         continue;
 109 
 110                 if (prcm->mpu_speed <= rate) {
 111                         found_speed = prcm->mpu_speed;
 112                         break;
 113                 }
 114         }
 115 
 116         if (!found_speed) {
 117                 printk(KERN_INFO "Could not set MPU rate to %luMHz\n",
 118                        rate / 1000000);
 119                 return -EINVAL;
 120         }
 121 
 122         curr_prcm_set = prcm;
 123         cur_rate = omap2xxx_clk_get_core_rate();
 124 
 125         if (prcm->dpll_speed == cur_rate / 2) {
 126                 omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL, 1);
 127         } else if (prcm->dpll_speed == cur_rate * 2) {
 128                 omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL_X2, 1);
 129         } else if (prcm->dpll_speed != cur_rate) {
 130                 local_irq_save(flags);
 131 
 132                 if (prcm->dpll_speed == prcm->xtal_speed)
 133                         bypass = 1;
 134 
 135                 if ((prcm->cm_clksel2_pll & OMAP24XX_CORE_CLK_SRC_MASK) ==
 136                     CORE_CLK_SRC_DPLL_X2)
 137                         done_rate = CORE_CLK_SRC_DPLL_X2;
 138                 else
 139                         done_rate = CORE_CLK_SRC_DPLL;
 140 
 141                 omap2xxx_cm_set_mod_dividers(prcm->cm_clksel_mpu,
 142                                              prcm->cm_clksel_dsp,
 143                                              prcm->cm_clksel_gfx,
 144                                              prcm->cm_clksel1_core,
 145                                              prcm->cm_clksel_mdm);
 146 
 147                 /* x2 to enter omap2xxx_sdrc_init_params() */
 148                 omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL_X2, 1);
 149 
 150                 omap2_set_prcm(prcm->cm_clksel1_pll, prcm->base_sdrc_rfr,
 151                                bypass);
 152 
 153                 omap2xxx_sdrc_init_params(omap2xxx_sdrc_dll_is_unlocked());
 154                 omap2xxx_sdrc_reprogram(done_rate, 0);
 155 
 156                 local_irq_restore(flags);
 157         }
 158 
 159         return 0;
 160 }
 161 
 162 /**
 163  * omap2xxx_clkt_vps_check_bootloader_rate - determine which of the rate
 164  * table sets matches the current CORE DPLL hardware rate
 165  *
 166  * Check the MPU rate set by bootloader.  Sets the 'curr_prcm_set'
 167  * global to point to the active rate set when found; otherwise, sets
 168  * it to NULL.  No return value;
 169  */
 170 void omap2xxx_clkt_vps_check_bootloader_rates(void)
 171 {
 172         const struct prcm_config *prcm = NULL;
 173         unsigned long rate;
 174 
 175         rate = omap2xxx_clk_get_core_rate();
 176         for (prcm = rate_table; prcm->mpu_speed; prcm++) {
 177                 if (!(prcm->flags & cpu_mask))
 178                         continue;
 179                 if (prcm->xtal_speed != sys_ck_rate)
 180                         continue;
 181                 if (prcm->dpll_speed <= rate)
 182                         break;
 183         }
 184         curr_prcm_set = prcm;
 185 }
 186 
 187 /**
 188  * omap2xxx_clkt_vps_late_init - store a copy of the sys_ck rate
 189  *
 190  * Store a copy of the sys_ck rate for later use by the OMAP2xxx DVFS
 191  * code.  (The sys_ck rate does not -- or rather, must not -- change
 192  * during kernel runtime.)  Must be called after we have a valid
 193  * sys_ck rate, but before the virt_prcm_set clock rate is
 194  * recalculated.  No return value.
 195  */
 196 void omap2xxx_clkt_vps_late_init(void)
 197 {
 198         struct clk *c;
 199 
 200         c = clk_get(NULL, "sys_ck");
 201         if (IS_ERR(c)) {
 202                 WARN(1, "could not locate sys_ck\n");
 203         } else {
 204                 sys_ck_rate = clk_get_rate(c);
 205                 clk_put(c);
 206         }
 207 }
 208 
 209 #ifdef CONFIG_OF
 210 #include <linux/clk-provider.h>
 211 #include <linux/clkdev.h>
 212 
 213 static const struct clk_ops virt_prcm_set_ops = {
 214         .recalc_rate    = &omap2_table_mpu_recalc,
 215         .set_rate       = &omap2_select_table_rate,
 216         .round_rate     = &omap2_round_to_table_rate,
 217 };
 218 
 219 /**
 220  * omap2xxx_clkt_vps_init - initialize virt_prcm_set clock
 221  *
 222  * Does a manual init for the virtual prcm DVFS clock for OMAP2. This
 223  * function is called only from omap2 DT clock init, as the virtual
 224  * node is not modelled in the DT clock data.
 225  */
 226 void omap2xxx_clkt_vps_init(void)
 227 {
 228         struct clk_init_data init = { NULL };
 229         struct clk_hw_omap *hw = NULL;
 230         struct clk *clk;
 231         const char *parent_name = "mpu_ck";
 232 
 233         omap2xxx_clkt_vps_late_init();
 234         omap2xxx_clkt_vps_check_bootloader_rates();
 235 
 236         hw = kzalloc(sizeof(*hw), GFP_KERNEL);
 237         if (!hw)
 238                 goto cleanup;
 239         init.name = "virt_prcm_set";
 240         init.ops = &virt_prcm_set_ops;
 241         init.parent_names = &parent_name;
 242         init.num_parents = 1;
 243 
 244         hw->hw.init = &init;
 245 
 246         clk = clk_register(NULL, &hw->hw);
 247         clkdev_create(clk, "cpufreq_ck", NULL);
 248         return;
 249 cleanup:
 250         kfree(hw);
 251 }
 252 #endif

/* [<][>][^][v][top][bottom][index][help] */