Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c
29266 views
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 <[email protected]>
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 int omap2_determine_rate_to_table(struct clk_hw *hw,
74
struct clk_rate_request *req)
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 <= req->rate)
91
break;
92
}
93
req->rate = highest_rate;
94
95
return 0;
96
}
97
98
/* Sets basic clocks based on the specified rate */
99
static int omap2_select_table_rate(struct clk_hw *hw, unsigned long rate,
100
unsigned long parent_rate)
101
{
102
u32 cur_rate, done_rate, bypass = 0;
103
const struct prcm_config *prcm;
104
unsigned long found_speed = 0;
105
unsigned long flags;
106
107
for (prcm = rate_table; prcm->mpu_speed; prcm++) {
108
if (!(prcm->flags & cpu_mask))
109
continue;
110
111
if (prcm->xtal_speed != sys_ck_rate)
112
continue;
113
114
if (prcm->mpu_speed <= rate) {
115
found_speed = prcm->mpu_speed;
116
break;
117
}
118
}
119
120
if (!found_speed) {
121
printk(KERN_INFO "Could not set MPU rate to %luMHz\n",
122
rate / 1000000);
123
return -EINVAL;
124
}
125
126
curr_prcm_set = prcm;
127
cur_rate = omap2xxx_clk_get_core_rate();
128
129
if (prcm->dpll_speed == cur_rate / 2) {
130
omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL, 1);
131
} else if (prcm->dpll_speed == cur_rate * 2) {
132
omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL_X2, 1);
133
} else if (prcm->dpll_speed != cur_rate) {
134
local_irq_save(flags);
135
136
if (prcm->dpll_speed == prcm->xtal_speed)
137
bypass = 1;
138
139
if ((prcm->cm_clksel2_pll & OMAP24XX_CORE_CLK_SRC_MASK) ==
140
CORE_CLK_SRC_DPLL_X2)
141
done_rate = CORE_CLK_SRC_DPLL_X2;
142
else
143
done_rate = CORE_CLK_SRC_DPLL;
144
145
omap2xxx_cm_set_mod_dividers(prcm->cm_clksel_mpu,
146
prcm->cm_clksel_dsp,
147
prcm->cm_clksel_gfx,
148
prcm->cm_clksel1_core,
149
prcm->cm_clksel_mdm);
150
151
/* x2 to enter omap2xxx_sdrc_init_params() */
152
omap2xxx_sdrc_reprogram(CORE_CLK_SRC_DPLL_X2, 1);
153
154
omap2_set_prcm(prcm->cm_clksel1_pll, prcm->base_sdrc_rfr,
155
bypass);
156
157
omap2xxx_sdrc_init_params(omap2xxx_sdrc_dll_is_unlocked());
158
omap2xxx_sdrc_reprogram(done_rate, 0);
159
160
local_irq_restore(flags);
161
}
162
163
return 0;
164
}
165
166
/**
167
* omap2xxx_clkt_vps_check_bootloader_rates - determine which of the rate
168
* table sets matches the current CORE DPLL hardware rate
169
*
170
* Check the MPU rate set by bootloader. Sets the 'curr_prcm_set'
171
* global to point to the active rate set when found; otherwise, sets
172
* it to NULL. No return value;
173
*/
174
static void omap2xxx_clkt_vps_check_bootloader_rates(void)
175
{
176
const struct prcm_config *prcm = NULL;
177
unsigned long rate;
178
179
rate = omap2xxx_clk_get_core_rate();
180
for (prcm = rate_table; prcm->mpu_speed; prcm++) {
181
if (!(prcm->flags & cpu_mask))
182
continue;
183
if (prcm->xtal_speed != sys_ck_rate)
184
continue;
185
if (prcm->dpll_speed <= rate)
186
break;
187
}
188
curr_prcm_set = prcm;
189
}
190
191
/**
192
* omap2xxx_clkt_vps_late_init - store a copy of the sys_ck rate
193
*
194
* Store a copy of the sys_ck rate for later use by the OMAP2xxx DVFS
195
* code. (The sys_ck rate does not -- or rather, must not -- change
196
* during kernel runtime.) Must be called after we have a valid
197
* sys_ck rate, but before the virt_prcm_set clock rate is
198
* recalculated. No return value.
199
*/
200
static void omap2xxx_clkt_vps_late_init(void)
201
{
202
struct clk *c;
203
204
c = clk_get(NULL, "sys_ck");
205
if (IS_ERR(c)) {
206
WARN(1, "could not locate sys_ck\n");
207
} else {
208
sys_ck_rate = clk_get_rate(c);
209
clk_put(c);
210
}
211
}
212
213
#ifdef CONFIG_OF
214
#include <linux/clk-provider.h>
215
#include <linux/clkdev.h>
216
217
static const struct clk_ops virt_prcm_set_ops = {
218
.recalc_rate = &omap2_table_mpu_recalc,
219
.set_rate = &omap2_select_table_rate,
220
.determine_rate = &omap2_determine_rate_to_table,
221
};
222
223
/**
224
* omap2xxx_clkt_vps_init - initialize virt_prcm_set clock
225
*
226
* Does a manual init for the virtual prcm DVFS clock for OMAP2. This
227
* function is called only from omap2 DT clock init, as the virtual
228
* node is not modelled in the DT clock data.
229
*/
230
void omap2xxx_clkt_vps_init(void)
231
{
232
struct clk_init_data init = { NULL };
233
struct clk_hw_omap *hw = NULL;
234
struct clk *clk;
235
const char *parent_name = "mpu_ck";
236
237
omap2xxx_clkt_vps_late_init();
238
omap2xxx_clkt_vps_check_bootloader_rates();
239
240
hw = kzalloc(sizeof(*hw), GFP_KERNEL);
241
if (!hw)
242
return;
243
init.name = "virt_prcm_set";
244
init.ops = &virt_prcm_set_ops;
245
init.parent_names = &parent_name;
246
init.num_parents = 1;
247
248
hw->hw.init = &init;
249
250
clk = clk_register(NULL, &hw->hw);
251
if (IS_ERR(clk)) {
252
printk(KERN_ERR "Failed to register clock\n");
253
kfree(hw);
254
return;
255
}
256
257
clkdev_create(clk, "cpufreq_ck", NULL);
258
}
259
#endif
260
261