nvg.c 8.37 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
17
18
19
20

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
21
22
23
24
25
26
27
/*
 * 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)
28
{
Steven Kao's avatar
Steven Kao committed
29
	nvg_set_request(TEGRA_NVG_CHANNEL_VERSION);
30

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

Steven Kao's avatar
Steven Kao committed
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
/*
 * 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);
78
79
80
81

	return 0;
}

Steven Kao's avatar
Steven Kao committed
82
83
84
85
86
87
88
89
90
91
92
93
/*
 * 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);
}

94
95
96
/*
 * This request allows updating of CLUSTER_CSTATE, CCPLEX_CSTATE and
 * SYSTEM_CSTATE values.
Steven Kao's avatar
Steven Kao committed
97
98
99
100
101
102
103
104
105
 *
 * 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
106
 */
Steven Kao's avatar
Steven Kao committed
107
108
void nvg_update_cstate_info(uint32_t cluster, uint32_t ccplex,
		uint32_t system, uint32_t wake_mask, uint8_t update_wake_mask)
109
110
111
112
{
	uint64_t val = 0;

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

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

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

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

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

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

Steven Kao's avatar
Steven Kao committed
142
143
144
145
146
147
148
149
150
/*
 * 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)
151
{
Steven Kao's avatar
Steven Kao committed
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
	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;
	}
175

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

Steven Kao's avatar
Steven Kao committed
179
180
181
182
183
184
185
/*
 * 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)
186
{
Steven Kao's avatar
Steven Kao committed
187
188
189
190
191
192
193
194
195
	int32_t ret = 0;

	/* sanity check stat id */
	if (data > (uint64_t)NVG_STAT_QUERY_C7_RESIDENCY_SUM) {
		ERROR("%s: unknown stat id (%d)\n", __func__, (uint32_t)data);
		ret = EINVAL;
	} else {
		nvg_set_request_data(TEGRA_NVG_CHANNEL_CSTATE_STAT_QUERY_REQUEST, data);
	}
196

Steven Kao's avatar
Steven Kao committed
197
	return ret;
198
199
}

Steven Kao's avatar
Steven Kao committed
200
201
202
203
204
205
/*
 * 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)
206
{
Steven Kao's avatar
Steven Kao committed
207
	nvg_set_request(TEGRA_NVG_CHANNEL_CSTATE_STAT_QUERY_VALUE);
208

Steven Kao's avatar
Steven Kao committed
209
	return (uint64_t)nvg_get_result();
210
211
}

Steven Kao's avatar
Steven Kao committed
212
213
214
215
216
217
/*
 * 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)
218
{
Steven Kao's avatar
Steven Kao committed
219
220
221
222
223
	/* 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();
224
225
}

Steven Kao's avatar
Steven Kao committed
226
227
228
229
230
231
232
/*
 * 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)
233
{
Steven Kao's avatar
Steven Kao committed
234
	int32_t ret = 0;
235

Steven Kao's avatar
Steven Kao committed
236
237
238
239
240
241
242
243
	/* 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);
244
245
	}

Steven Kao's avatar
Steven Kao committed
246
247
248
249
250
251
252
253
254
255
256
257
258
259
	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;

260
	/*
Steven Kao's avatar
Steven Kao committed
261
262
263
264
265
266
267
268
	 * 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.
269
	 */
Steven Kao's avatar
Steven Kao committed
270
271
272
273
	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);
274

Steven Kao's avatar
Steven Kao committed
275
	return 0;
276
277
}

Steven Kao's avatar
Steven Kao committed
278
279
280
281
282
283
284
/*
 * 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)
285
{
Steven Kao's avatar
Steven Kao committed
286
287
288
289
290
291
292
293
294
	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);
295
296
	}

Steven Kao's avatar
Steven Kao committed
297
298
	return ret;
}
299

Steven Kao's avatar
Steven Kao committed
300
301
302
303
304
305
306
307
308
/*
 * 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);
309
310
311
312

	return 0;
}

Steven Kao's avatar
Steven Kao committed
313
314
315
316
317
318
/*
 * Cache clean and invalidate operation for all CCPLEX caches.
 *
 * NVGDATA[1] cache_clean_inval
 */
int32_t nvg_roc_flush_cache(void)
319
{
Steven Kao's avatar
Steven Kao committed
320
321
	nvg_set_request_data(TEGRA_NVG_CHANNEL_CCPLEX_CACHE_INVAL,
							(uint64_t)CACHE_CLEAN_INVAL_SET);
322

Steven Kao's avatar
Steven Kao committed
323
324
	return 0;
}
325

Steven Kao's avatar
Steven Kao committed
326
327
328
329
330
331
332
333
334
/*
 * 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);
335
336
337

	return 0;
}
Steven Kao's avatar
Steven Kao committed
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

/*
 * Set the power state for a core
 */
int32_t nvg_enter_cstate(uint32_t state, uint32_t wake_time)
{
	int32_t ret = 0;

	/* 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 */
		write_actlr_el1(state);
	}

	return ret;
}