nvg.c 8.71 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
/*
 * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <arch.h>
#include <arch_helpers.h>
#include <common/debug.h>
#include <denver.h>
Steven Kao's avatar
Steven Kao committed
11
#include <errno.h>
12
13
#include <lib/mmio.h>
#include <mce_private.h>
Steven Kao's avatar
Steven Kao committed
14
15
#include <platform_def.h>
#include <t194_nvg.h>
16
#include <tegra_private.h>
17
18
19
20
21

extern void nvg_set_request_data(uint64_t req, uint64_t data);
extern void nvg_set_request(uint64_t req);
extern uint64_t nvg_get_result(void);

Steven Kao's avatar
Steven Kao committed
22
23
24
25
26
27
28
/*
 * Reports the major and minor version of this interface.
 *
 * NVGDATA[0:31]: SW(R) Minor Version
 * NVGDATA[32:63]: SW(R) Major Version
 */
uint64_t nvg_get_version(void)
29
{
Steven Kao's avatar
Steven Kao committed
30
	nvg_set_request(TEGRA_NVG_CHANNEL_VERSION);
31

Steven Kao's avatar
Steven Kao committed
32
33
	return (uint64_t)nvg_get_result();
}
34

Steven Kao's avatar
Steven Kao committed
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
/*
 * Enable the perf per watt mode.
 *
 * NVGDATA[0]: SW(RW), 1 = enable perf per watt mode
 */
int32_t nvg_enable_power_perf_mode(void)
{
	nvg_set_request_data(TEGRA_NVG_CHANNEL_POWER_PERF, 1U);

	return 0;
}

/*
 * Disable the perf per watt mode.
 *
 * NVGDATA[0]: SW(RW), 0 = disable perf per watt mode
 */
int32_t nvg_disable_power_perf_mode(void)
{
	nvg_set_request_data(TEGRA_NVG_CHANNEL_POWER_PERF, 0U);

	return 0;
}

/*
 * Enable the battery saver mode.
 *
 * NVGDATA[2]: SW(RW), 1 = enable battery saver mode
 */
int32_t nvg_enable_power_saver_modes(void)
{
	nvg_set_request_data(TEGRA_NVG_CHANNEL_POWER_MODES, 1U);

	return 0;
}

/*
 * Disable the battery saver mode.
 *
 * NVGDATA[2]: SW(RW), 0 = disable battery saver mode
 */
int32_t nvg_disable_power_saver_modes(void)
{
	nvg_set_request_data(TEGRA_NVG_CHANNEL_POWER_MODES, 0U);
79
80
81
82

	return 0;
}

Steven Kao's avatar
Steven Kao committed
83
84
85
86
87
88
89
90
91
92
93
94
/*
 * Set the expected wake time in TSC ticks for the next low-power state the
 * core enters.
 *
 * NVGDATA[0:31]: SW(RW), WAKE_TIME
 */
void nvg_set_wake_time(uint32_t wake_time)
{
	/* time (TSC ticks) until the core is expected to get a wake event */
	nvg_set_request_data(TEGRA_NVG_CHANNEL_WAKE_TIME, (uint64_t)wake_time);
}

95
96
97
/*
 * This request allows updating of CLUSTER_CSTATE, CCPLEX_CSTATE and
 * SYSTEM_CSTATE values.
Steven Kao's avatar
Steven Kao committed
98
99
100
101
102
103
104
105
106
 *
 * NVGDATA[0:2]: SW(RW), CLUSTER_CSTATE
 * NVGDATA[7]: SW(W), update cluster flag
 * NVGDATA[8:9]: SW(RW), CG_CSTATE
 * NVGDATA[15]: SW(W), update ccplex flag
 * NVGDATA[16:19]: SW(RW), SYSTEM_CSTATE
 * NVGDATA[23]: SW(W), update system flag
 * NVGDATA[31]: SW(W), update wake mask flag
 * NVGDATA[32:63]: SW(RW), WAKE_MASK
107
 */
Steven Kao's avatar
Steven Kao committed
108
109
void nvg_update_cstate_info(uint32_t cluster, uint32_t ccplex,
		uint32_t system, uint32_t wake_mask, uint8_t update_wake_mask)
110
111
112
113
{
	uint64_t val = 0;

	/* update CLUSTER_CSTATE? */
Steven Kao's avatar
Steven Kao committed
114
115
116
117
	if (cluster != 0U) {
		val |= ((uint64_t)cluster & CLUSTER_CSTATE_MASK) |
				CLUSTER_CSTATE_UPDATE_BIT;
	}
118
119

	/* update CCPLEX_CSTATE? */
Steven Kao's avatar
Steven Kao committed
120
121
122
123
	if (ccplex != 0U) {
		val |= (((uint64_t)ccplex & CCPLEX_CSTATE_MASK) << CCPLEX_CSTATE_SHIFT) |
				CCPLEX_CSTATE_UPDATE_BIT;
	}
124
125

	/* update SYSTEM_CSTATE? */
Steven Kao's avatar
Steven Kao committed
126
127
128
129
	if (system != 0U) {
		val |= (((uint64_t)system & SYSTEM_CSTATE_MASK) << SYSTEM_CSTATE_SHIFT) |
				SYSTEM_CSTATE_UPDATE_BIT;
	}
130
131

	/* update wake mask value? */
Steven Kao's avatar
Steven Kao committed
132
	if (update_wake_mask != 0U) {
133
		val |= CSTATE_WAKE_MASK_UPDATE_BIT;
Steven Kao's avatar
Steven Kao committed
134
	}
135
136

	/* set the wake mask */
Steven Kao's avatar
Steven Kao committed
137
	val |= ((uint64_t)wake_mask & CSTATE_WAKE_MASK_CLEAR) << CSTATE_WAKE_MASK_SHIFT;
138
139
140
141
142

	/* set the updated cstate info */
	nvg_set_request_data(TEGRA_NVG_CHANNEL_CSTATE_INFO, val);
}

Steven Kao's avatar
Steven Kao committed
143
144
145
146
147
148
149
150
151
/*
 * Indices gives MTS the crossover point in TSC ticks for when it becomes
 * no longer viable to enter the named state
 *
 * Type 0 : NVGDATA[0:31]: C6 Lower bound
 * Type 1 : NVGDATA[0:31]: CC6 Lower bound
 * Type 2 : NVGDATA[0:31]: CG7 Lower bound
 */
int32_t nvg_update_crossover_time(uint32_t type, uint32_t time)
152
{
Steven Kao's avatar
Steven Kao committed
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
	int32_t ret = 0;

	switch (type) {
	case TEGRA_NVG_CROSSOVER_C6:
		nvg_set_request_data(TEGRA_NVG_CHANNEL_CROSSOVER_C6_LOWER_BOUND,
			(uint64_t)time);
		break;

	case TEGRA_NVG_CROSSOVER_CC6:
		nvg_set_request_data(TEGRA_NVG_CHANNEL_CROSSOVER_CC6_LOWER_BOUND,
			(uint64_t)time);
		break;

	case TEGRA_NVG_CROSSOVER_CG7:
		nvg_set_request_data(TEGRA_NVG_CHANNEL_CROSSOVER_CG7_LOWER_BOUND,
			(uint64_t)time);
		break;

	default:
		ERROR("%s: unknown crossover type (%d)\n", __func__, type);
		ret = EINVAL;
		break;
	}
176

Steven Kao's avatar
Steven Kao committed
177
	return ret;
178
179
}

Steven Kao's avatar
Steven Kao committed
180
181
182
183
184
185
186
/*
 * These NVG calls allow ARM SW to access CSTATE statistical information
 *
 * NVGDATA[0:3]: SW(RW) Core/cluster/cg id
 * NVGDATA[16:31]: SW(RW) Stat id
 */
int32_t nvg_set_cstate_stat_query_value(uint64_t data)
187
{
Steven Kao's avatar
Steven Kao committed
188
189
	int32_t ret = 0;

190
191
192
193
194
195
196
197
198
	/* sanity check stat id and core id*/
	if ((data >> MCE_STAT_ID_SHIFT) >
		(uint64_t)NVG_STAT_QUERY_C7_RESIDENCY_SUM) {
		ERROR("%s: unknown stat id (%d)\n", __func__,
			(uint32_t)(data >> MCE_STAT_ID_SHIFT));
		ret = EINVAL;
	} else if ((data & MCE_CORE_ID_MASK) > (uint64_t)PLATFORM_CORE_COUNT) {
		ERROR("%s: unknown core id (%d)\n", __func__,
			(uint32_t)(data & MCE_CORE_ID_MASK));
Steven Kao's avatar
Steven Kao committed
199
200
201
202
		ret = EINVAL;
	} else {
		nvg_set_request_data(TEGRA_NVG_CHANNEL_CSTATE_STAT_QUERY_REQUEST, data);
	}
203

Steven Kao's avatar
Steven Kao committed
204
	return ret;
205
206
}

Steven Kao's avatar
Steven Kao committed
207
208
209
210
211
212
/*
 * The read-only value associated with the CSTATE_STAT_QUERY_REQUEST
 *
 * NVGDATA[0:63]: SW(R) Stat count
 */
uint64_t nvg_get_cstate_stat_query_value(void)
213
{
Steven Kao's avatar
Steven Kao committed
214
	nvg_set_request(TEGRA_NVG_CHANNEL_CSTATE_STAT_QUERY_VALUE);
215

Steven Kao's avatar
Steven Kao committed
216
	return (uint64_t)nvg_get_result();
217
218
}

Steven Kao's avatar
Steven Kao committed
219
220
221
222
223
224
/*
 * Return a non-zero value if the CCPLEX is able to enter SC7
 *
 * NVGDATA[0]: SW(R), Is allowed result
 */
int32_t nvg_is_sc7_allowed(void)
225
{
Steven Kao's avatar
Steven Kao committed
226
227
228
229
230
	/* issue command to check if SC7 is allowed */
	nvg_set_request(TEGRA_NVG_CHANNEL_IS_SC7_ALLOWED);

	/* 1 = SC7 allowed, 0 = SC7 not allowed */
	return (int32_t)nvg_get_result();
231
232
}

Steven Kao's avatar
Steven Kao committed
233
234
235
236
237
238
239
/*
 * Wake an offlined logical core. Note that a core is offlined by entering
 * a C-state where the WAKE_MASK is all 0.
 *
 * NVGDATA[0:3]: SW(W) logical core to online
 */
int32_t nvg_online_core(uint32_t core)
240
{
Steven Kao's avatar
Steven Kao committed
241
	int32_t ret = 0;
242

Steven Kao's avatar
Steven Kao committed
243
244
245
246
247
248
249
250
	/* sanity check the core ID value */
	if (core > (uint32_t)PLATFORM_CORE_COUNT) {
		ERROR("%s: unknown core id (%d)\n", __func__, core);
		ret = EINVAL;
	} else {
		/* get a core online */
		nvg_set_request_data(TEGRA_NVG_CHANNEL_ONLINE_CORE,
								(uint64_t)core & MCE_CORE_ID_MASK);
251
252
	}

Steven Kao's avatar
Steven Kao committed
253
254
255
256
257
258
259
260
261
262
263
264
265
266
	return ret;
}

/*
 * Enables and controls the voltage/frequency hint for CC3. CC3 is disabled
 * by default.
 *
 * NVGDATA[7:0] SW(RW) frequency request
 * NVGDATA[31:31] SW(RW) enable bit
 */
int32_t nvg_cc3_ctrl(uint32_t freq, uint8_t enable)
{
	uint64_t val = 0;

267
	/*
Steven Kao's avatar
Steven Kao committed
268
269
270
271
272
273
274
275
	 * If the enable bit is cleared, Auto-CC3 will be disabled by setting
	 * the SW visible frequency request registers for all non
	 * floorswept cores valid independent of StandbyWFI and disabling
	 * the IDLE frequency request register. If set, Auto-CC3
	 * will be enabled by setting the ARM SW visible frequency
	 * request registers for all non floorswept cores to be enabled by
	 * StandbyWFI or the equivalent signal, and always keeping the IDLE
	 * frequency request register enabled.
276
	 */
Steven Kao's avatar
Steven Kao committed
277
278
279
280
	if (enable != 0U) {
		val = ((uint64_t)freq & MCE_AUTO_CC3_FREQ_MASK) | MCE_AUTO_CC3_ENABLE_BIT;
	}
	nvg_set_request_data(TEGRA_NVG_CHANNEL_CC3_CTRL, val);
281

Steven Kao's avatar
Steven Kao committed
282
	return 0;
283
284
}

Steven Kao's avatar
Steven Kao committed
285
286
287
288
289
290
291
/*
 * MC GSC (General Security Carveout) register values are expected to be
 * changed by TrustZone ARM code after boot.
 *
 * NVGDATA[0:15] SW(R) GSC enun
 */
int32_t nvg_update_ccplex_gsc(uint32_t gsc_idx)
292
{
Steven Kao's avatar
Steven Kao committed
293
294
295
296
297
298
299
300
301
	int32_t ret = 0;

	/* sanity check GSC ID */
	if (gsc_idx > (uint32_t)TEGRA_NVG_GSC_VPR_IDX) {
		ERROR("%s: unknown gsc_idx (%d)\n", __func__, gsc_idx);
		ret = EINVAL;
	} else {
		nvg_set_request_data(TEGRA_NVG_CHANNEL_UPDATE_CCPLEX_GSC,
								(uint64_t)gsc_idx);
302
303
	}

Steven Kao's avatar
Steven Kao committed
304
305
	return ret;
}
306

Steven Kao's avatar
Steven Kao committed
307
308
309
310
311
312
313
314
315
/*
 * Cache clean operation for all CCPLEX caches.
 *
 * NVGDATA[0] cache_clean
 */
int32_t nvg_roc_clean_cache(void)
{
	nvg_set_request_data(TEGRA_NVG_CHANNEL_CCPLEX_CACHE_INVAL,
							(uint64_t)CACHE_CLEAN_SET);
316
317
318
319

	return 0;
}

Steven Kao's avatar
Steven Kao committed
320
321
322
323
324
325
/*
 * Cache clean and invalidate operation for all CCPLEX caches.
 *
 * NVGDATA[1] cache_clean_inval
 */
int32_t nvg_roc_flush_cache(void)
326
{
Steven Kao's avatar
Steven Kao committed
327
328
	nvg_set_request_data(TEGRA_NVG_CHANNEL_CCPLEX_CACHE_INVAL,
							(uint64_t)CACHE_CLEAN_INVAL_SET);
329

Steven Kao's avatar
Steven Kao committed
330
331
	return 0;
}
332

Steven Kao's avatar
Steven Kao committed
333
334
335
336
337
338
339
340
341
/*
 * Cache clean and invalidate, clear TR-bit operation for all CCPLEX caches.
 *
 * NVGDATA[2] cache_clean_inval_tr
 */
int32_t nvg_roc_clean_cache_trbits(void)
{
	nvg_set_request_data(TEGRA_NVG_CHANNEL_CCPLEX_CACHE_INVAL,
							(uint64_t)CACHE_CLEAN_INVAL_TR_SET);
342
343
344

	return 0;
}
Steven Kao's avatar
Steven Kao committed
345
346
347
348
349
350
351

/*
 * Set the power state for a core
 */
int32_t nvg_enter_cstate(uint32_t state, uint32_t wake_time)
{
	int32_t ret = 0;
352
	uint64_t val = 0ULL;
Steven Kao's avatar
Steven Kao committed
353
354
355
356
357
358
359
360
361
362
363
364
365
366

	/* check for allowed power state */
	if ((state != (uint32_t)TEGRA_NVG_CORE_C0) &&
		(state != (uint32_t)TEGRA_NVG_CORE_C1) &&
	    (state != (uint32_t)TEGRA_NVG_CORE_C6) &&
		(state != (uint32_t)TEGRA_NVG_CORE_C7))
	{
		ERROR("%s: unknown cstate (%d)\n", __func__, state);
		ret = EINVAL;
	} else {
		/* time (TSC ticks) until the core is expected to get a wake event */
		nvg_set_wake_time(wake_time);

		/* set the core cstate */
367
368
		val = read_actlr_el1() & ~ACTLR_EL1_PMSTATE_MASK;
		write_actlr_el1(val | (uint64_t)state);
Steven Kao's avatar
Steven Kao committed
369
370
371
372
	}

	return ret;
}