1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
|
/*
*
* Copyright (C) 2010 Google, Inc.
*
* Author:
* Colin Cross <ccross@google.com>
*
* Copyright (c) 2010-2016 NVIDIA CORPORATION. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef _TEGRA_DVFS_H_
#define _TEGRA_DVFS_H_
#include <linux/of.h>
#include <linux/tegra_throttle.h>
#define MAX_DVFS_FREQS 40
#define MAX_DVFS_TABLES 80
#define DVFS_RAIL_STATS_TOP_BIN 100
#define MAX_THERMAL_LIMITS 8
#define MAX_THERMAL_RANGES (MAX_THERMAL_LIMITS + 1)
struct clk;
struct dvfs_rail;
/*
* dvfs_relationship between to rails, "from" and "to"
* when the rail changes, it will call dvfs_rail_update on the rails
* in the relationship_to list.
* when determining the voltage to set a rail to, it will consider each
* rail in the relationship_from list.
*/
struct dvfs_relationship {
struct dvfs_rail *to;
struct dvfs_rail *from;
int (*solve)(struct dvfs_rail *, struct dvfs_rail *);
struct list_head to_node; /* node in relationship_to list */
struct list_head from_node; /* node in relationship_from list */
bool solved_at_nominal;
bool solved_at_suspend;
};
struct rail_stats {
ktime_t time_at_mv[DVFS_RAIL_STATS_TOP_BIN + 1];
ktime_t last_update;
int last_index;
bool off;
int bin_uV;
};
struct rail_alignment {
int offset_uv;
int step_uv; /* Step voltage */
};
struct dvfs_rail {
const char *reg_id;
int min_millivolts;
int max_millivolts;
int reg_max_millivolts;
int nominal_millivolts; /* Max DVFS voltage */
int fixed_millivolts;
int override_millivolts;
int min_override_millivolts;
int override_unresolved;
int (*resolve_override)(int mv);
const int *therm_mv_floors;
const int *therm_mv_dfll_floors;
int therm_mv_floors_num;
const int *therm_mv_caps;
int therm_mv_caps_num;
const int *simon_vmin_offsets;
int simon_vmin_offs_num;
int simon_domain;
int step;
int step_up;
bool jmp_to_zero;
bool in_band_pm;
bool disabled;
bool updating;
bool resolving_to;
bool rate_set;
bool dt_reg_fixed;
bool dt_reg_pwm;
struct device_node *dt_node; /* device tree rail node */
struct list_head node; /* node in dvfs_rail_list */
struct list_head dvfs; /* list head of attached dvfs clocks */
struct list_head relationships_to;
struct list_head relationships_from;
struct regulator *reg;
int millivolts;
int new_millivolts;
int dbg_mv_offs;
int boot_millivolts;
int disable_millivolts;
int suspend_millivolts; /* voltage setting set during suspend */
bool suspended;
bool dfll_mode; /* DFLL mode ON/OFF */
bool dfll_mode_updating;
int therm_floor_idx; /* index of vmin thermal range */
int therm_cap_idx; /* index of vmax thermal range */
int therm_scale_idx; /* index of thermal DVFS or clk source range */
/* Trips for vmin cooling device */
struct tegra_cooling_device *vmin_cdev;
/* Trips for vmax cooling device */
struct tegra_cooling_device *vmax_cdev;
/* Trips for thermal DVFS cooling device */
struct tegra_cooling_device *vts_cdev;
/* Trips for clock source switch cooling device */
struct tegra_cooling_device *clk_switch_cdev;
/* Vmax capping method */
int (*apply_vmax_cap)(int *cap_idx, int new_idx, int cap_mv);
struct rail_alignment alignment;
struct rail_stats stats;
const char *version;
};
/*
* dfll_range -
* DFLL_RANGE_NONE : DFLL is not used
* DFLL_RANGE_ALL_RATES : DFLL is is used for all CPU rates
* DFLL_RANGE_HIGH_RATES : DFLL is used only for high rates
* above crossover with PLL dvfs curve
*/
enum dfll_range {
DFLL_RANGE_NONE = 0,
DFLL_RANGE_ALL_RATES,
DFLL_RANGE_HIGH_RATES,
};
/* DFLL usage is under thermal cooling device control */
#define TEGRA_USE_DFLL_CDEV_CNTRL 3
extern int tegra_override_dfll_range;
/* DVFS settings specific for DFLL clock source */
struct dvfs_dfll_data {
u32 tune0;
u32 tune0_high_mv;
u32 tune0_simon_mask;
u32 tune1;
bool tune0_low_at_cold;
unsigned long droop_rate_min;
unsigned long use_dfll_rate_min;
unsigned long out_rate_min;
unsigned long max_rate_boost;
/* Boot frequency if DFLL is enabled by boot-loader; zero otherwise */
unsigned long dfll_boot_khz;
int tune_high_min_millivolts;
int tune_high_margin_mv;
int min_millivolts;
enum dfll_range range;
void (*tune_trimmers)(bool trim_high);
unsigned int (*is_bypass_down)(void);
};
/* DVFS settings specific for PLL clock source */
struct dvfs_pll_data {
int min_millivolts;
};
struct dvfs {
const char *clk_name;
struct clk *clk;
int speedo_id;
int process_id;
/* Must be initialized before tegra_dvfs_init */
int freqs_mult;
unsigned long freqs[MAX_DVFS_FREQS];
const int *millivolts;
const int *peak_millivolts;
/* voltage settings as per DFLL clock source */
const int *dfll_millivolts;
struct dvfs_rail *dvfs_rail;
bool auto_dvfs;
bool can_override;
bool defer_override;
bool boost_table;
/* Filled in by tegra_dvfs_init */
int max_millivolts;
int num_freqs;
struct dvfs_dfll_data dfll_data;
/* Set if clock rate is adapted by h/w to voltage noise (noise-aware) */
bool na_dvfs;
/* Indicates thermal DVFS on/off */
bool therm_dvfs;
/* Maximum rate safe at minimum voltage across all thermal ranges */
unsigned long fmax_at_vmin_safe_t;
/*
* Current dvfs point = { index into V/F arrays, voltage, rate request }
* cur_index is invalid - set to MAX_DVFS_FREQS - if cur_rate and
* cur_millivolts are set to zero (may happen when dvfs rate is not set
* initially, or clock is disabled).
*/
int cur_index;
int cur_millivolts;
unsigned long cur_rate;
unsigned long *alt_freqs;
bool use_alt_freqs;
long dbg_hz_offs;
struct list_head node;
struct list_head debug_node;
struct list_head reg_node;
struct mutex *lock;
};
/* CVB coefficients */
struct cvb_dvfs_parameters {
int c0;
int c1;
int c2;
int c3;
int c4;
int c5;
};
struct cvb_dvfs_table {
unsigned long freq;
/* Coeffs for voltage calculation, when dfll clock source is selected */
struct cvb_dvfs_parameters cvb_dfll_param;
/* Coeffs for voltage calculation, when pll clock source is selected */
struct cvb_dvfs_parameters cvb_pll_param;
};
struct cvb_dvfs {
int speedo_id;
int process_id;
/* Tuning parameters for dfll */
struct dvfs_dfll_data dfll_tune_data;
/* tuning parameters for pll clock */
struct dvfs_pll_data pll_tune_data;
/* dvfs Max voltage */
int max_mv;
/* dvfs Max frequency */
unsigned long max_freq;
int freqs_mult;
/* scaling values for voltage calculation */
int speedo_scale;
int voltage_scale;
int thermal_scale;
struct cvb_dvfs_table cvb_vmin;
/* CVB table for various frequencies */
struct cvb_dvfs_table cvb_table[MAX_DVFS_FREQS];
/* Trips for minimal voltage settings per thermal ranges */
int vmin_trips_table[MAX_THERMAL_LIMITS];
int therm_floors_table[MAX_THERMAL_LIMITS];
/* Trips for thermal DVFS per thermal ranges */
int vts_trips_table[MAX_THERMAL_LIMITS];
/* Trips for clock source change per thermal ranges */
int clk_switch_trips[MAX_THERMAL_LIMITS];
};
#define cpu_cvb_dvfs cvb_dvfs
#define gpu_cvb_dvfs cvb_dvfs
#define core_cvb_dvfs cvb_dvfs
extern struct dvfs_rail *tegra_cpu_rail;
extern struct dvfs_rail *tegra_gpu_rail;
extern struct dvfs_rail *tegra_core_rail;
struct dvfs_data {
struct dvfs_rail *rail;
struct dvfs *tables;
int *millivolts;
unsigned int num_tables;
unsigned int num_voltages;
};
#ifdef CONFIG_OF
typedef int (*of_tegra_dvfs_init_cb_t)(struct device_node *);
int of_tegra_dvfs_init(const struct of_device_id *matches);
int of_tegra_dvfs_rail_node_parse(struct device_node *rail_dn,
struct dvfs_rail *rail);
int of_tegra_dvfs_rail_get_cdev_trips(
struct tegra_cooling_device *tegra_cdev, int *therm_trips_table,
int *therm_limits_table, struct rail_alignment *align, bool up);
#else
static inline int of_tegra_dvfs_init(const struct of_device_id *matches)
{ return -ENODATA; }
static inline int of_tegra_dvfs_rail_node_parse(struct device_node *rail_dn,
struct dvfs_rail *rail)
{ return -ENODEV; }
static inline int of_tegra_dvfs_rail_get_cdev_trips(
struct tegra_cooling_device *tegra_cdev, int *therm_trips_table,
int *therm_limits_table, struct rail_alignment *align, bool up)
{ return -ENODEV; }
#endif
void tegra21x_init_dvfs(void);
int dvfs_debugfs_init(struct dentry *clk_debugfs_root);
int tegra_dvfs_rail_connect_regulators(void);
int tegra_dvfs_rail_register_notifiers(void);
int tegra_dvfs_init_rails(struct dvfs_rail *dvfs_rails[], int n);
void tegra_dvfs_add_relationships(struct dvfs_relationship *rels, int n);
void tegra_dvfs_rail_enable(struct dvfs_rail *rail);
void tegra_dvfs_rail_disable(struct dvfs_rail *rail);
int tegra_dvfs_rail_power_up(struct dvfs_rail *rail);
int tegra_dvfs_rail_power_down(struct dvfs_rail *rail);
bool tegra_dvfs_is_rail_up(struct dvfs_rail *rail);
bool tegra_dvfs_rail_updating(struct clk *clk);
void tegra_dvfs_rail_off(struct dvfs_rail *rail, ktime_t now);
void tegra_dvfs_rail_on(struct dvfs_rail *rail, ktime_t now);
void tegra_dvfs_rail_pause(struct dvfs_rail *rail, ktime_t delta, bool on);
int tegra_dvfs_rail_set_mode(struct dvfs_rail *rail, unsigned int mode);
int tegra_dvfs_rail_register_notifier(struct dvfs_rail *rail,
struct notifier_block *nb);
int tegra_dvfs_rail_unregister_notifier(struct dvfs_rail *rail,
struct notifier_block *nb);
struct dvfs_rail *tegra_dvfs_get_rail_by_name(const char *reg_id);
int tegra_dvfs_rail_get_current_millivolts(struct dvfs_rail *rail);
int tegra_dvfs_override_core_cap_apply(int level);
int tegra_dvfs_therm_vmax_core_cap_apply(int *cap_idx, int new_idx, int level);
int tegra_dvfs_alt_freqs_install(struct dvfs *d, unsigned long *alt_freqs);
void tegra_dvfs_alt_freqs_install_always(
struct dvfs *d, unsigned long *alt_freqs);
int tegra_dvfs_replace_voltage_table(struct dvfs *d, const int *new_millivolts);
int tegra_dvfs_butterfly_throttle(struct clk *c1, unsigned long *rate1,
struct clk *c2, unsigned long *rate2);
int tegra_dvfs_dfll_mode_set(struct dvfs *d, unsigned long rate);
int tegra_dvfs_dfll_mode_clear(struct dvfs *d, unsigned long rate);
int tegra_clk_dfll_range_control(enum dfll_range use_dfll);
bool tegra_dvfs_is_dfll_scale(struct dvfs *d, unsigned long rate);
bool tegra_dvfs_is_dfll_range(struct dvfs *d, unsigned long rate);
int tegra_dvfs_swap_dfll_range(struct dvfs *d, int range, int *old_range);
int tegra_dvfs_set_dfll_range(struct dvfs *d, int range);
int tegra_dvfs_rail_set_reg_volatile(struct dvfs_rail *rail, bool set);
void tegra_clip_freqs(u32 *freqs, int *num_freqs, int freqs_mult,
const unsigned long *rates_ladder, int num_rates, bool up);
struct tegra_cooling_device *tegra_dvfs_get_cpu_vmax_cdev(void);
struct tegra_cooling_device *tegra_dvfs_get_cpu_vmin_cdev(void);
struct tegra_cooling_device *tegra_dvfs_get_core_vmax_cdev(void);
struct tegra_cooling_device *tegra_dvfs_get_core_vmin_cdev(void);
struct tegra_cooling_device *tegra_dvfs_get_gpu_vmin_cdev(void);
struct tegra_cooling_device *tegra_dvfs_get_gpu_vts_cdev(void);
struct tegra_cooling_device *tegra_dvfs_get_cpu_clk_switch_cdev(void);
void tegra_dvfs_rail_init_vmin_thermal_profile(
int *therm_trips_table, int *therm_floors_table,
struct dvfs_rail *rail, struct dvfs_dfll_data *d);
void tegra_dvfs_rail_init_vmax_thermal_profile(
int *therm_trips_table, int *therm_caps_table,
struct dvfs_rail *rail, struct dvfs_dfll_data *d);
int tegra_dvfs_rail_of_init_vmin_thermal_profile(
int *therm_trips_table, int *therm_floors_table,
struct dvfs_rail *rail, struct dvfs_dfll_data *d);
int tegra_dvfs_rail_of_init_vmax_thermal_profile(
int *therm_trips_table, int *therm_caps_table,
struct dvfs_rail *rail, struct dvfs_dfll_data *d);
int tegra_dvfs_rail_init_clk_switch_thermal_profile(
int *clk_switch_trips, struct dvfs_rail *rail);
int tegra_dvfs_rail_init_thermal_dvfs_trips(
int *therm_trips_table, struct dvfs_rail *rail);
int tegra_dvfs_init_thermal_dvfs_voltages(int *millivolts,
int *peak_millivolts, int freqs_num, int ranges_num, struct dvfs *d);
int tegra_dvfs_rail_dfll_mode_set_cold(struct dvfs_rail *rail,
struct clk *dfll_clk);
int tegra_dvfs_rail_get_thermal_floor(struct dvfs_rail *rail);
void tegra_dvfs_rail_register_vmax_cdev(struct dvfs_rail *rail);
#ifdef CONFIG_TEGRA_VDD_CORE_OVERRIDE
int tegra_dvfs_resolve_override(struct clk *c, unsigned long max_rate);
int tegra_dvfs_rail_get_override_floor(struct dvfs_rail *rail);
#else
static inline int tegra_dvfs_resolve_override(struct clk *c, unsigned long rate)
{ return 0; }
static inline int tegra_dvfs_rail_get_override_floor(struct dvfs_rail *rail)
{ return 0; }
#endif
static inline int tegra_dvfs_rail_disable_prepare(struct dvfs_rail *rail)
{ return 0; }
static inline int tegra_dvfs_rail_post_enable(struct dvfs_rail *rail)
{ return 0; }
bool tegra_dvfs_is_dfll_bypass(void);
static inline bool tegra_dvfs_rail_is_dfll_mode(struct dvfs_rail *rail)
{
return rail ? rail->dfll_mode : false;
}
static inline void tegra_dvfs_rail_mode_updating(struct dvfs_rail *rail,
bool updating)
{
if (rail)
rail->dfll_mode_updating = updating;
}
static inline void tegra_dvfs_set_dfll_tune_trimmers(
struct dvfs *d, void (*tune_trimmers)(bool trim_high))
{
if (d->dfll_data.tune_high_min_millivolts)
d->dfll_data.tune_trimmers = tune_trimmers;
}
#endif
|