nvg.c 9.19 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
#define	ID_AFR0_EL1_CACHE_OPS_SHIFT	12
#define	ID_AFR0_EL1_CACHE_OPS_MASK	0xFU
Steven Kao's avatar
Steven Kao committed
20
21
22
23
24
25
26
/*
 * 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)
27
{
Steven Kao's avatar
Steven Kao committed
28
	nvg_set_request(TEGRA_NVG_CHANNEL_VERSION);
29

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

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

	return 0;
}

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

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

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

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

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

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

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

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

Steven Kao's avatar
Steven Kao committed
141
142
143
144
/*
 * Indices gives MTS the crossover point in TSC ticks for when it becomes
 * no longer viable to enter the named state
 *
145
146
147
 * Type 5 : NVGDATA[0:31]: C6 Lower bound
 * Type 6 : NVGDATA[0:31]: CC6 Lower bound
 * Type 8 : NVGDATA[0:31]: CG7 Lower bound
Steven Kao's avatar
Steven Kao committed
148
149
 */
int32_t nvg_update_crossover_time(uint32_t type, uint32_t time)
150
{
Steven Kao's avatar
Steven Kao committed
151
152
153
	int32_t ret = 0;

	switch (type) {
154
	case TEGRA_NVG_CHANNEL_CROSSOVER_C6_LOWER_BOUND:
Steven Kao's avatar
Steven Kao committed
155
156
157
158
		nvg_set_request_data(TEGRA_NVG_CHANNEL_CROSSOVER_C6_LOWER_BOUND,
			(uint64_t)time);
		break;

159
	case TEGRA_NVG_CHANNEL_CROSSOVER_CC6_LOWER_BOUND:
Steven Kao's avatar
Steven Kao committed
160
161
162
163
		nvg_set_request_data(TEGRA_NVG_CHANNEL_CROSSOVER_CC6_LOWER_BOUND,
			(uint64_t)time);
		break;

164
	case TEGRA_NVG_CHANNEL_CROSSOVER_CG7_LOWER_BOUND:
Steven Kao's avatar
Steven Kao committed
165
166
167
168
169
170
171
172
173
		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;
	}
174

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

Steven Kao's avatar
Steven Kao committed
178
179
180
181
182
183
184
/*
 * 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)
185
{
Steven Kao's avatar
Steven Kao committed
186
187
	int32_t ret = 0;

188
189
190
191
192
193
194
195
196
	/* 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
197
198
199
200
		ret = EINVAL;
	} else {
		nvg_set_request_data(TEGRA_NVG_CHANNEL_CSTATE_STAT_QUERY_REQUEST, data);
	}
201

Steven Kao's avatar
Steven Kao committed
202
	return ret;
203
204
}

Steven Kao's avatar
Steven Kao committed
205
206
207
208
209
210
/*
 * 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)
211
{
Steven Kao's avatar
Steven Kao committed
212
	nvg_set_request(TEGRA_NVG_CHANNEL_CSTATE_STAT_QUERY_VALUE);
213

Steven Kao's avatar
Steven Kao committed
214
	return (uint64_t)nvg_get_result();
215
216
}

Steven Kao's avatar
Steven Kao committed
217
218
219
220
221
222
/*
 * 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)
223
{
Steven Kao's avatar
Steven Kao committed
224
225
226
227
228
	/* 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();
229
230
}

Steven Kao's avatar
Steven Kao committed
231
232
233
234
235
236
237
/*
 * 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)
238
{
Steven Kao's avatar
Steven Kao committed
239
	int32_t ret = 0;
240

Steven Kao's avatar
Steven Kao committed
241
242
243
244
245
246
247
248
	/* 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);
249
250
	}

Steven Kao's avatar
Steven Kao committed
251
252
253
254
255
256
257
258
259
260
261
262
263
264
	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;

265
	/*
Steven Kao's avatar
Steven Kao committed
266
267
268
269
270
271
272
273
	 * 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.
274
	 */
Steven Kao's avatar
Steven Kao committed
275
276
277
278
	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);
279

Steven Kao's avatar
Steven Kao committed
280
	return 0;
281
282
}

Steven Kao's avatar
Steven Kao committed
283
284
285
286
287
288
289
/*
 * 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)
290
{
291
	int32_t ret;
Steven Kao's avatar
Steven Kao committed
292
293

	/* sanity check GSC ID */
294
295
	if (gsc_idx > (uint32_t)TEGRA_NVG_CHANNEL_UPDATE_GSC_VPR) {
		ERROR("%s: unknown gsc_idx (%u)\n", __func__, gsc_idx);
Steven Kao's avatar
Steven Kao committed
296
297
298
299
		ret = EINVAL;
	} else {
		nvg_set_request_data(TEGRA_NVG_CHANNEL_UPDATE_CCPLEX_GSC,
								(uint64_t)gsc_idx);
300
301
	}

Steven Kao's avatar
Steven Kao committed
302
303
	return ret;
}
304

Steven Kao's avatar
Steven Kao committed
305
306
307
308
309
/*
 * Cache clean operation for all CCPLEX caches.
 */
int32_t nvg_roc_clean_cache(void)
{
310
	int32_t ret = 0;
311

312
313
314
315
316
317
318
319
320
321
322
	/* check if cache flush through mts is supported */
	if (((read_id_afr0_el1() >> ID_AFR0_EL1_CACHE_OPS_SHIFT) &
			ID_AFR0_EL1_CACHE_OPS_MASK) == 1U) {
		if (nvg_cache_clean() == 0U) {
			ERROR("%s: failed\n", __func__);
			ret = EINVAL;
		}
	} else {
		ret = EINVAL;
	}
	return ret;
323
324
}

Steven Kao's avatar
Steven Kao committed
325
326
327
328
/*
 * Cache clean and invalidate operation for all CCPLEX caches.
 */
int32_t nvg_roc_flush_cache(void)
329
{
330
	int32_t ret = 0;
331

332
333
334
335
336
337
338
339
340
341
342
	/* check if cache flush through mts is supported */
	if (((read_id_afr0_el1() >> ID_AFR0_EL1_CACHE_OPS_SHIFT) &
			ID_AFR0_EL1_CACHE_OPS_MASK) == 1U) {
		if (nvg_cache_clean_inval() == 0U) {
			ERROR("%s: failed\n", __func__);
			ret = EINVAL;
		}
	} else {
		ret = EINVAL;
	}
	return ret;
Steven Kao's avatar
Steven Kao committed
343
}
344

Steven Kao's avatar
Steven Kao committed
345
346
347
348
349
/*
 * Cache clean and invalidate, clear TR-bit operation for all CCPLEX caches.
 */
int32_t nvg_roc_clean_cache_trbits(void)
{
350
	int32_t ret = 0;
351

352
353
354
355
356
357
358
359
360
361
362
	/* check if cache flush through mts is supported */
	if (((read_id_afr0_el1() >> ID_AFR0_EL1_CACHE_OPS_SHIFT) &
			ID_AFR0_EL1_CACHE_OPS_MASK) == 1U) {
		if (nvg_cache_inval_all() == 0U) {
			ERROR("%s: failed\n", __func__);
			ret = EINVAL;
		}
	} else {
		ret = EINVAL;
	}
	return ret;
363
}
Steven Kao's avatar
Steven Kao committed
364
365
366
367
368
369
370

/*
 * Set the power state for a core
 */
int32_t nvg_enter_cstate(uint32_t state, uint32_t wake_time)
{
	int32_t ret = 0;
371
	uint64_t val = 0ULL;
Steven Kao's avatar
Steven Kao committed
372
373
374
375
376
377
378
379
380
381
382
383
384
385

	/* 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 */
386
387
		val = read_actlr_el1() & ~ACTLR_EL1_PMSTATE_MASK;
		write_actlr_el1(val | (uint64_t)state);
Steven Kao's avatar
Steven Kao committed
388
389
390
391
	}

	return ret;
}