~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 static u16 cpu_mask;
 43 
 44 const struct prcm_config *curr_prcm_set;
 45 const struct prcm_config *rate_table;
 46 
 47 /*
 48  * sys_ck_rate: the rate of the external high-frequency clock
 49  * oscillator on the board.  Set by the SoC-specific clock init code.
 50  * Once set during a boot, will not change.
 51  */
 52 static unsigned long sys_ck_rate;
 53 
 54 /**
 55  * omap2_table_mpu_recalc - just return the MPU speed
 56  * @clk: virt_prcm_set struct clk
 57  *
 58  * Set virt_prcm_set's rate to the mpu_speed field of the current PRCM set.
 59  */
 60 static unsigned long omap2_table_mpu_recalc(struct clk_hw *clk,
 61                                      unsigned long parent_rate)
 62 {
 63         return curr_prcm_set->mpu_speed;
 64 }
 65 
 66 /*
 67  * Look for a rate equal or less than the target rate given a configuration set.
 68  *
 69  * What's not entirely clear is "which" field represents the key field.
 70  * Some might argue L3-DDR, others ARM, others IVA. This code is simple and
 71  * just uses the ARM rates.
 72  */
 73 static long omap2_round_to_table_rate(struct clk_hw *hw, unsigned long rate,
 74                                unsigned long *parent_rate)
 75 {
 76         const struct prcm_config *ptr;
 77         long highest_rate;
 78 
 79         highest_rate = -EINVAL;
 80 
 81         for (ptr = rate_table; ptr->mpu_speed; ptr++) {
 82                 if (!(ptr->flags & cpu_mask))
 83                         continue;
 84                 if (ptr->xtal_speed != sys_ck_rate)
 85                         continue;
 86 
 87                 highest_rate = ptr->mpu_speed;
 88 
 89                 /* Can check only after xtal frequency check */
 90                 if (ptr->mpu_speed <= rate)
 91                         break;
 92         }
 93         return highest_rate;
 94 }
 95 
 96 /* Sets basic clocks based on the specified rate */
 97 static int omap2_select_table_rate(struct clk_hw *hw, unsigned long rate,
 98                                    unsigned long parent_rate)
 99 {
100         u32 cur_rate, done_rate, bypass = 0;
101         const struct prcm_config *prcm;
102         unsigned long found_speed = 0;
103         unsigned long flags;
104 
105         for (prcm = rate_table; prcm->mpu_speed; prcm++) {
106                 if (!(prcm->flags & cpu_mask))
107                         continue;
108 
109                 if (prcm->xtal_speed != sys_ck_rate)
110                         continue;
111 
112                 if (prcm->mpu_speed <= rate) {
113                         found_speed = prcm->mpu_speed;
114                         break;
115                 }
116         }
117 
118         if (!found_speed) {
119                 printk(KERN_INFO "Could not set MPU rate to %luMHz\n",
120                        rate / 1000000);
121                 return -EINVAL;
122         }
123 
124         curr_prcm_set = prcm;
125         cur_rate = omap2xxx_clk_get_core_rate();
126 
127         if (prcm->dpll_speed == cur_rate / 2) {
128                 omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL, 1);
129         } else if (prcm->dpll_speed == cur_rate * 2) {
130                 omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL_X2, 1);
131         } else if (prcm->dpll_speed != cur_rate) {
132                 local_irq_save(flags);
133 
134                 if (prcm->dpll_speed == prcm->xtal_speed)
135                         bypass = 1;
136 
137                 if ((prcm->cm_clksel2_pll & OMAP24XX_CORE_CLK_SRC_MASK) ==
138                     CORE_CLK_SRC_DPLL_X2)
139                         done_rate = CORE_CLK_SRC_DPLL_X2;
140                 else
141                         done_rate = CORE_CLK_SRC_DPLL;
142 
143                 omap2xxx_cm_set_mod_dividers(prcm->cm_clksel_mpu,
144                                              prcm->cm_clksel_dsp,
145                                              prcm->cm_clksel_gfx,
146                                              prcm->cm_clksel1_core,
147                                              prcm->cm_clksel_mdm);
148 
149                 /* x2 to enter omap2xxx_sdrc_init_params() */
150                 omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL_X2, 1);
151 
152                 omap2_set_prcm(prcm->cm_clksel1_pll, prcm->base_sdrc_rfr,
153                                bypass);
154 
155                 omap2xxx_sdrc_init_params(omap2xxx_sdrc_dll_is_unlocked());
156                 omap2xxx_sdrc_reprogram(done_rate, 0);
157 
158                 local_irq_restore(flags);
159         }
160 
161         return 0;
162 }
163 
164 /**
165  * omap2xxx_clkt_vps_check_bootloader_rates - determine which of the rate
166  * table sets matches the current CORE DPLL hardware rate
167  *
168  * Check the MPU rate set by bootloader.  Sets the 'curr_prcm_set'
169  * global to point to the active rate set when found; otherwise, sets
170  * it to NULL.  No return value;
171  */
172 static void omap2xxx_clkt_vps_check_bootloader_rates(void)
173 {
174         const struct prcm_config *prcm = NULL;
175         unsigned long rate;
176 
177         rate = omap2xxx_clk_get_core_rate();
178         for (prcm = rate_table; prcm->mpu_speed; prcm++) {
179                 if (!(prcm->flags & cpu_mask))
180                         continue;
181                 if (prcm->xtal_speed != sys_ck_rate)
182                         continue;
183                 if (prcm->dpll_speed <= rate)
184                         break;
185         }
186         curr_prcm_set = prcm;
187 }
188 
189 /**
190  * omap2xxx_clkt_vps_late_init - store a copy of the sys_ck rate
191  *
192  * Store a copy of the sys_ck rate for later use by the OMAP2xxx DVFS
193  * code.  (The sys_ck rate does not -- or rather, must not -- change
194  * during kernel runtime.)  Must be called after we have a valid
195  * sys_ck rate, but before the virt_prcm_set clock rate is
196  * recalculated.  No return value.
197  */
198 static void omap2xxx_clkt_vps_late_init(void)
199 {
200         struct clk *c;
201 
202         c = clk_get(NULL, "sys_ck");
203         if (IS_ERR(c)) {
204                 WARN(1, "could not locate sys_ck\n");
205         } else {
206                 sys_ck_rate = clk_get_rate(c);
207                 clk_put(c);
208         }
209 }
210 
211 #ifdef CONFIG_OF
212 #include <linux/clk-provider.h>
213 #include <linux/clkdev.h>
214 
215 static const struct clk_ops virt_prcm_set_ops = {
216         .recalc_rate    = &omap2_table_mpu_recalc,
217         .set_rate       = &omap2_select_table_rate,
218         .round_rate     = &omap2_round_to_table_rate,
219 };
220 
221 /**
222  * omap2xxx_clkt_vps_init - initialize virt_prcm_set clock
223  *
224  * Does a manual init for the virtual prcm DVFS clock for OMAP2. This
225  * function is called only from omap2 DT clock init, as the virtual
226  * node is not modelled in the DT clock data.
227  */
228 void omap2xxx_clkt_vps_init(void)
229 {
230         struct clk_init_data init = { NULL };
231         struct clk_hw_omap *hw = NULL;
232         struct clk *clk;
233         const char *parent_name = "mpu_ck";
234 
235         omap2xxx_clkt_vps_late_init();
236         omap2xxx_clkt_vps_check_bootloader_rates();
237 
238         hw = kzalloc(sizeof(*hw), GFP_KERNEL);
239         if (!hw)
240                 return;
241         init.name = "virt_prcm_set";
242         init.ops = &virt_prcm_set_ops;
243         init.parent_names = &parent_name;
244         init.num_parents = 1;
245 
246         hw->hw.init = &init;
247 
248         clk = clk_register(NULL, &hw->hw);
249         if (IS_ERR(clk)) {
250                 printk(KERN_ERR "Failed to register clock\n");
251                 kfree(hw);
252                 return;
253         }
254 
255         clkdev_create(clk, "cpufreq_ck", NULL);
256 }
257 #endif
258 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php