nvg.c 8.48 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
190
191
192
193
194
195
196
	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);
	}
197

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

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

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

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

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

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

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

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

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

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

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

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

	return 0;
}

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

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

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

	return 0;
}
Steven Kao's avatar
Steven Kao committed
339
340
341
342
343
344
345

/*
 * Set the power state for a core
 */
int32_t nvg_enter_cstate(uint32_t state, uint32_t wake_time)
{
	int32_t ret = 0;
346
	uint64_t val = 0ULL;
Steven Kao's avatar
Steven Kao committed
347
348
349
350
351
352
353
354
355
356
357
358
359
360

	/* 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 */
361
362
		val = read_actlr_el1() & ~ACTLR_EL1_PMSTATE_MASK;
		write_actlr_el1(val | (uint64_t)state);
Steven Kao's avatar
Steven Kao committed
363
364
365
366
	}

	return ret;
}