summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/dvfs.h
blob: b6bda2e4f8f6229e028d8c75319d6a8a11f91a0a (plain)
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
/*
 *
 * Copyright (C) 2010 Google, Inc.
 *
 * Author:
 *	Colin Cross <ccross@google.com>
 *
 * Copyright (C) 2010-2012 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 <mach/thermal.h>

#define MAX_DVFS_FREQS	40
#define MAX_DVFS_TABLES	80
#define DVFS_RAIL_STATS_TOP_BIN	60

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;
};

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 dvfs_rail {
	const char *reg_id;
	int min_millivolts;
	int max_millivolts;
	int reg_max_millivolts;
	int nominal_millivolts;
	int min_millivolts_cold;
	int override_millivolts;
	int min_override_millivolts;
	int step;
	bool jmp_to_zero;
	bool disabled;
	bool updating;
	bool resolving_to;

	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 offs_millivolts;
	bool suspended;
	bool dfll_mode;
	bool dfll_mode_updating;
	int thermal_idx;
	struct tegra_cooling_device *pll_mode_cdev;
	struct tegra_cooling_device *dfll_mode_cdev;
	struct rail_stats stats;
};

enum dfll_range {
	DFLL_RANGE_NONE = 0,
	DFLL_RANGE_ALL_RATES,
	DFLL_RANGE_HIGH_RATES,
};

struct dvfs_dfll_data {
	u32		tune0;
	u32		tune0_high_mv;
	u32		tune1;
	unsigned long	droop_rate_min;
	unsigned long	use_dfll_rate_min;
	unsigned long	out_rate_min;
	unsigned long	max_rate_boost;
	int tune_high_min_millivolts;
	int min_millivolts;
	enum dfll_range	range;
};

struct dvfs {
	/* Used only by tegra2_clock.c */
	const char *clk_name;
	int speedo_id;
	int process_id;

	/* Must be initialized before tegra_dvfs_init */
	int freqs_mult;
	unsigned long freqs[MAX_DVFS_FREQS];
	unsigned long *alt_freqs;
	const int *millivolts;
	const int *dfll_millivolts;
	struct dvfs_rail *dvfs_rail;
	bool auto_dvfs;

	/* Filled in by tegra_dvfs_init */
	int max_millivolts;
	int num_freqs;
	struct dvfs_dfll_data dfll_data;

	int cur_millivolts;
	unsigned long cur_rate;
	struct list_head node;
	struct list_head debug_node;
	struct list_head reg_node;
};

struct cpu_cvb_dvfs_parameters {
	int	c0;
	int	c1;
	int	c2;
};

struct cpu_cvb_dvfs_table {
	unsigned long freq;
	struct cpu_cvb_dvfs_parameters cvb_dfll_param;
	struct cpu_cvb_dvfs_parameters cvb_pll_param;
};

struct cpu_cvb_dvfs {
	int speedo_id;
	int process_id;

	struct dvfs_dfll_data dfll_tune_data;
	int max_mv;
	int freqs_mult;
	int speedo_scale;
	int voltage_scale;
	struct cpu_cvb_dvfs_table cvb_table[MAX_DVFS_FREQS];
};

extern struct dvfs_rail *tegra_cpu_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;
};

struct core_dvfs_cap_table {
	const char *cap_name;
	struct clk *cap_clk;
	unsigned long freqs[MAX_DVFS_FREQS];
};

struct core_bus_cap_table {
	const char *cap_name;
	struct clk *cap_clk;
	struct kobj_attribute refcnt_attr;
	struct kobj_attribute level_attr;
	int refcnt;
	int level;
};

#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);
#else
static inline int of_tegra_dvfs_init(const struct of_device_id *matches)
{ return -ENODATA; }
#endif

void tegra2_init_dvfs(void);
void tegra3_init_dvfs(void);
void tegra11x_init_dvfs(void);
int tegra_enable_dvfs_on_clk(struct clk *c, struct dvfs *d);
int dvfs_debugfs_init(struct dentry *clk_debugfs_root);
int tegra_dvfs_late_init(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);
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);
struct dvfs_rail *tegra_dvfs_get_rail_by_name(const char *reg_id);
int tegra_dvfs_predict_millivolts(struct clk *c, unsigned long rate);
int tegra_dvfs_predict_millivolts_pll(struct clk *c, unsigned long rate);
int tegra_dvfs_predict_millivolts_dfll(struct clk *c, unsigned long rate);
int tegra_dvfs_core_cap_level_apply(int level);
int tegra_dvfs_alt_freqs_set(struct dvfs *d, unsigned long *alt_freqs);
int tegra_cpu_dvfs_alter(int edp_thermal_index, const cpumask_t *cpus,
			 bool before_clk_update, int cpu_event);
int tegra_dvfs_dfll_mode_set(struct dvfs *d, unsigned long rate);
int tegra_dvfs_dfll_mode_clear(struct dvfs *d, unsigned long rate);
struct tegra_cooling_device *tegra_dvfs_get_cpu_dfll_cdev(void);
struct tegra_cooling_device *tegra_dvfs_get_cpu_pll_cdev(void);
struct tegra_cooling_device *tegra_dvfs_get_core_cdev(void);
int tegra_dvfs_rail_dfll_mode_set_cold(struct dvfs_rail *rail);

#ifndef CONFIG_ARCH_TEGRA_2x_SOC
int tegra_dvfs_rail_disable_prepare(struct dvfs_rail *rail);
int tegra_dvfs_rail_post_enable(struct dvfs_rail *rail);
#else
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; }
#endif
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
void tegra_dvfs_age_cpu(int cur_linear_age);
#else
static inline void tegra_dvfs_age_cpu(int cur_linear_age)
{ return; }
#endif

int tegra_init_core_cap(struct core_dvfs_cap_table *table, int table_size,
	const int *millivolts, int millivolts_num, struct kobject *cap_kobj);
int tegra_init_shared_bus_cap(struct core_bus_cap_table *table, int table_size,
	struct kobject *cap_kobj);

static inline bool tegra_dvfs_rail_is_dfll_mode(struct dvfs_rail *rail)
{
	return rail ? rail->dfll_mode : false;
}
static inline bool tegra_dvfs_is_dfll_range_entry(struct dvfs *d,
						  unsigned long rate)
{
	return  d->dvfs_rail && (!d->dvfs_rail->dfll_mode) &&
		(d->dfll_data.range == DFLL_RANGE_HIGH_RATES) &&
		(rate >= d->dfll_data.use_dfll_rate_min) &&
		(d->cur_rate < d->dfll_data.use_dfll_rate_min);
}

static inline bool tegra_dvfs_is_dfll_scale(struct dvfs *d, unsigned long rate)
{
	return tegra_dvfs_rail_is_dfll_mode(d->dvfs_rail) ||
		tegra_dvfs_is_dfll_range_entry(d, rate);
}

static inline bool tegra_dvfs_is_dfll_range(struct dvfs *d, unsigned long rate)
{
	return (d->dfll_data.range == DFLL_RANGE_ALL_RATES) ||
		((d->dfll_data.range == DFLL_RANGE_HIGH_RATES) &&
		(rate >= d->dfll_data.use_dfll_rate_min));
}
static inline int tegra_dvfs_set_dfll_range(struct dvfs *d, int range)
{
	if (!d->dfll_millivolts)
		return -ENOSYS;

	if ((range < DFLL_RANGE_NONE) || (range > DFLL_RANGE_HIGH_RATES))
		return -EINVAL;

	d->dfll_data.range = range;
	return 0;
}
static inline void tegra_dvfs_rail_mode_updating(struct dvfs_rail *rail,
						 bool updating)
{
	if (rail)
		rail->dfll_mode_updating = updating;
}

static inline int tegra_dvfs_rail_get_nominal_millivolts(struct dvfs_rail *rail)
{
	if (rail)
		return rail->nominal_millivolts;
	return -ENOENT;
}

static inline int tegra_dvfs_rail_get_override_floor(struct dvfs_rail *rail)
{
	if (rail)
		return rail->min_override_millivolts;
	return -ENOENT;
}

#endif