plat_psci_handlers.c 10.5 KB
Newer Older
1
/*
2
 * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved.
3
 *
dp-arm's avatar
dp-arm committed
4
 * SPDX-License-Identifier: BSD-3-Clause
5
6
7
 */

#include <assert.h>
8
#include <cortex_a57.h>
9
10
11
12
13
14
15
#include <arch_helpers.h>
#include <common/debug.h>
#include <drivers/delay_timer.h>
#include <lib/mmio.h>
#include <lib/psci/psci.h>
#include <plat/common/platform.h>

16
#include <bpmp.h>
17
#include <flowctrl.h>
18
#include <pmc.h>
19
20
#include <platform_def.h>
#include <security_engine.h>
21
22
#include <tegra_def.h>
#include <tegra_private.h>
23
#include <tegra_platform.h>
24

25
26
27
28
29
30
31
/*
 * Register used to clear CPU reset signals. Each CPU has two reset
 * signals: CPU reset (3:0) and Core reset (19:16).
 */
#define CPU_CMPLX_RESET_CLR		0x454
#define CPU_CORE_RESET_MASK		0x10001

32
33
34
35
36
/* Clock and Reset controller registers for system clock's settings */
#define SCLK_RATE			0x30
#define SCLK_BURST_POLICY		0x28
#define SCLK_BURST_POLICY_DEFAULT	0x10000000

37
static int cpu_powergate_mask[PLATFORM_MAX_CPUS_PER_CLUSTER];
38
static bool tegra_bpmp_available = true;
39

40
41
int32_t tegra_soc_validate_power_state(unsigned int power_state,
					psci_power_state_t *req_state)
42
{
43
44
	int state_id = psci_get_pstate_id(power_state);

45
	/* Sanity check the requested state id */
46
	switch (state_id) {
47
	case PSTATE_ID_CORE_POWERDN:
48
49
50
51
52
53
54
		/*
		 * Core powerdown request only for afflvl 0
		 */
		req_state->pwr_domain_state[MPIDR_AFFLVL0] = state_id & 0xff;

		break;

55
56
	case PSTATE_ID_CLUSTER_IDLE:
	case PSTATE_ID_CLUSTER_POWERDN:
57

58
		/*
59
		 * Cluster idle request for afflvl 0
60
		 */
61
		req_state->pwr_domain_state[MPIDR_AFFLVL0] = PSTATE_ID_CORE_POWERDN;
62
		req_state->pwr_domain_state[MPIDR_AFFLVL1] = state_id;
63
64
65

		break;

66
	case PSTATE_ID_SOC_POWERDN:
67
68
69
		/*
		 * System powerdown request only for afflvl 2
		 */
70
		for (uint32_t i = MPIDR_AFFLVL0; i < PLAT_MAX_PWR_LVL; i++)
71
72
73
74
75
			req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;

		req_state->pwr_domain_state[PLAT_MAX_PWR_LVL] =
			PLAT_SYS_SUSPEND_STATE_ID;

76
77
78
		break;

	default:
79
80
		ERROR("%s: unsupported state id (%d)\n", __func__, state_id);
		return PSCI_E_INVALID_PARAMS;
81
82
83
84
85
	}

	return PSCI_E_SUCCESS;
}

86
87
/*******************************************************************************
 * Platform handler to calculate the proper target power level at the
88
 * specified affinity level.
89
90
91
92
93
 ******************************************************************************/
plat_local_state_t tegra_soc_get_target_pwr_state(unsigned int lvl,
					     const plat_local_state_t *states,
					     unsigned int ncpu)
{
94
	plat_local_state_t target = PSCI_LOCAL_STATE_RUN;
95
96
	int cpu = plat_my_core_pos();
	int core_pos = read_mpidr() & MPIDR_CPU_MASK;
97
	uint32_t bpmp_reply, data[3], val;
98
	int ret;
99
100
101
102
103
104
105

	/* get the power state at this level */
	if (lvl == MPIDR_AFFLVL1)
		target = *(states + core_pos);
	if (lvl == MPIDR_AFFLVL2)
		target = *(states + cpu);

106
107
108
	if ((lvl == MPIDR_AFFLVL1) && (target == PSTATE_ID_CLUSTER_IDLE)) {

		/* initialize the bpmp interface */
109
110
		ret = tegra_bpmp_init();
		if (ret != 0U) {
111
112
113

			/* Cluster idle not allowed */
			target = PSCI_LOCAL_STATE_RUN;
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

			/*******************************************
			 * BPMP is not present, so handle CC6 entry
			 * from the CPU
			 ******************************************/

			/* check if cluster idle state has been enabled */
			val = mmio_read_32(TEGRA_CL_DVFS_BASE + DVFS_DFLL_CTRL);
			if (val == ENABLE_CLOSED_LOOP) {
				/*
				 * flag to indicate that BPMP firmware is not
				 * available and the CPU has to handle entry/exit
				 * for all power states
				 */
				tegra_bpmp_available = false;

				/*
				 * Acquire the cluster idle lock to stop
				 * other CPUs from powering up.
				 */
				tegra_fc_ccplex_pgexit_lock();

				/* Cluster idle only from the last standing CPU */
				if (tegra_pmc_is_last_on_cpu() && tegra_fc_is_ccx_allowed()) {
					/* Cluster idle allowed */
					target = PSTATE_ID_CLUSTER_IDLE;
				} else {
					/* release cluster idle lock */
					tegra_fc_ccplex_pgexit_unlock();
				}
			}
145
146
		} else {

147
			/* Cluster power-down */
148
149
150
151
152
153
154
155
			data[0] = (uint32_t)cpu;
			data[1] = TEGRA_PM_CC6;
			data[2] = TEGRA_PM_SC1;
			ret = tegra_bpmp_send_receive_atomic(MRQ_DO_IDLE,
					(void *)&data, (int)sizeof(data),
					(void *)&bpmp_reply,
					(int)sizeof(bpmp_reply));

156
			/* check if cluster power down is allowed */
157
158
			if ((ret != 0L) || (bpmp_reply != BPMP_CCx_ALLOWED)) {

159
				/* Cluster power down not allowed */
160
161
				target = PSCI_LOCAL_STATE_RUN;
			}
162
		}
163

164
	} else if ((lvl == MPIDR_AFFLVL1) && (target == PSTATE_ID_CLUSTER_POWERDN)) {
165

166
		/* initialize the bpmp interface */
167
168
		ret = tegra_bpmp_init();
		if (ret != 0U) {
169
170
171

			/* Cluster power down not allowed */
			target = PSCI_LOCAL_STATE_RUN;
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
		} else {

			/* Cluster power-down */
			data[0] = (uint32_t)cpu;
			data[1] = TEGRA_PM_CC7;
			data[2] = TEGRA_PM_SC1;
			ret = tegra_bpmp_send_receive_atomic(MRQ_DO_IDLE,
					(void *)&data, (int)sizeof(data),
					(void *)&bpmp_reply,
					(int)sizeof(bpmp_reply));

			/* check if cluster power down is allowed */
			if ((ret != 0L) || (bpmp_reply != BPMP_CCx_ALLOWED)) {

				/* Cluster power down not allowed */
				target = PSCI_LOCAL_STATE_RUN;
			}
189
190
191
192
193
194
195
196
197
198
199
200
201
		}

	} else if (((lvl == MPIDR_AFFLVL2) || (lvl == MPIDR_AFFLVL1)) &&
	    (target == PSTATE_ID_SOC_POWERDN)) {

		/* System Suspend */
		target = PSTATE_ID_SOC_POWERDN;

	} else {
		; /* do nothing */
	}

	return target;
202
203
}

204
int tegra_soc_pwr_domain_suspend(const psci_power_state_t *target_state)
205
{
206
207
208
209
210
211
	u_register_t mpidr = read_mpidr();
	const plat_local_state_t *pwr_domain_state =
		target_state->pwr_domain_state;
	unsigned int stateid_afflvl2 = pwr_domain_state[MPIDR_AFFLVL2];
	unsigned int stateid_afflvl1 = pwr_domain_state[MPIDR_AFFLVL1];
	unsigned int stateid_afflvl0 = pwr_domain_state[MPIDR_AFFLVL0];
212
	uint32_t cfg;
213
	int ret = PSCI_E_SUCCESS;
214
	uint32_t val;
215

216
	if (stateid_afflvl2 == PSTATE_ID_SOC_POWERDN) {
217

218
		assert((stateid_afflvl0 == PLAT_MAX_OFF_STATE) ||
219
			(stateid_afflvl0 == PSTATE_ID_SOC_POWERDN));
220
		assert((stateid_afflvl1 == PLAT_MAX_OFF_STATE) ||
221
222
223
			(stateid_afflvl1 == PSTATE_ID_SOC_POWERDN));

		if (tegra_chipid_is_t210_b01()) {
224

225
226
227
228
229
			/* Suspend se/se2 and pka1 */
			if (tegra_se_suspend() != 0) {
				ret = PSCI_E_INTERN_FAIL;
			}
		}
230

231
	} else if (stateid_afflvl1 == PSTATE_ID_CLUSTER_IDLE) {
232

233
		assert(stateid_afflvl0 == PSTATE_ID_CORE_POWERDN);
234

235
236
237
238
239
240
241
242
243
244
245
		if (!tegra_bpmp_available) {

			/* PWM tristate */
			cfg = mmio_read_32(TEGRA_CL_DVFS_BASE + DVFS_DFLL_OUTPUT_CFG);
			if (cfg & DFLL_OUTPUT_CFG_CLK_EN_BIT) {
				val = mmio_read_32(TEGRA_MISC_BASE + PINMUX_AUX_DVFS_PWM);
				val |= PINMUX_PWM_TRISTATE;
				mmio_write_32(TEGRA_MISC_BASE + PINMUX_AUX_DVFS_PWM, val);
			}
		}

246
247
		/* Prepare for cluster idle */
		tegra_fc_cluster_idle(mpidr);
248

249
	} else if (stateid_afflvl1 == PSTATE_ID_CLUSTER_POWERDN) {
250

251
		assert(stateid_afflvl0 == PSTATE_ID_CORE_POWERDN);
252
253
254
255
256
257
258
259
260
261

		/* Prepare for cluster powerdn */
		tegra_fc_cluster_powerdn(mpidr);

	} else if (stateid_afflvl0 == PSTATE_ID_CORE_POWERDN) {

		/* Prepare for cpu powerdn */
		tegra_fc_cpu_powerdn(mpidr);

	} else {
262
263
		ERROR("%s: Unknown state id (%d, %d, %d)\n", __func__,
			stateid_afflvl2, stateid_afflvl1, stateid_afflvl0);
264
		ret = PSCI_E_NOT_SUPPORTED;
265
266
	}

267
	return ret;
268
269
}

270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
int tegra_soc_pwr_domain_power_down_wfi(const psci_power_state_t *target_state)
{
	u_register_t mpidr = read_mpidr();
	const plat_local_state_t *pwr_domain_state =
		target_state->pwr_domain_state;
	unsigned int stateid_afflvl2 = pwr_domain_state[PLAT_MAX_PWR_LVL];

	if (stateid_afflvl2 == PSTATE_ID_SOC_POWERDN) {

		if (tegra_chipid_is_t210_b01()) {
			/* Save tzram contents */
			tegra_se_save_tzram();
		}

		/* enter system suspend */
		tegra_fc_soc_powerdn(mpidr);
	}

	return PSCI_E_SUCCESS;
}

291
int tegra_soc_pwr_domain_on_finish(const psci_power_state_t *target_state)
292
{
293
	const plat_params_from_bl2_t *plat_params = bl31_get_plat_params();
294
	uint32_t cfg;
295
296
	uint32_t val;

297
298
299
300
301
302
303
304
	/* platform parameter passed by the previous bootloader */
	if (plat_params->l2_ecc_parity_prot_dis != 1) {
		/* Enable ECC Parity Protection for Cortex-A57 CPUs */
		val = read_l2ctlr_el1();
		val |= (uint64_t)CORTEX_A57_L2_ECC_PARITY_PROTECTION_BIT;
		write_l2ctlr_el1(val);
	}

305
306
307
	/*
	 * Check if we are exiting from SOC_POWERDN.
	 */
308
309
	if (target_state->pwr_domain_state[PLAT_MAX_PWR_LVL] ==
			PLAT_SYS_SUSPEND_STATE_ID) {
310

311
312
313
314
315
316
317
		/*
		 * Security engine resume
		 */
		if (tegra_chipid_is_t210_b01()) {
			tegra_se_resume();
		}

318
319
320
321
322
		/*
		 * Lock scratch registers which hold the CPU vectors
		 */
		tegra_pmc_lock_cpu_vectors();

323
324
325
326
327
328
329
330
331
		/*
		 * Enable WRAP to INCR burst type conversions for
		 * incoming requests on the AXI slave ports.
		 */
		val = mmio_read_32(TEGRA_MSELECT_BASE + MSELECT_CONFIG);
		val &= ~ENABLE_UNSUP_TX_ERRORS;
		val |= ENABLE_WRAP_TO_INCR_BURSTS;
		mmio_write_32(TEGRA_MSELECT_BASE + MSELECT_CONFIG, val);

332
333
334
335
		/*
		 * Restore Boot and Power Management Processor (BPMP) reset
		 * address and reset it.
		 */
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
		if (tegra_bpmp_available)
			tegra_fc_reset_bpmp();
	}

	/*
	 * Check if we are exiting cluster idle state
	 */
	if (target_state->pwr_domain_state[MPIDR_AFFLVL1] ==
			PSTATE_ID_CLUSTER_IDLE) {

		if (!tegra_bpmp_available) {

			/* PWM un-tristate */
			cfg = mmio_read_32(TEGRA_CL_DVFS_BASE + DVFS_DFLL_OUTPUT_CFG);
			if (cfg & DFLL_OUTPUT_CFG_CLK_EN_BIT) {
				val = mmio_read_32(TEGRA_MISC_BASE + PINMUX_AUX_DVFS_PWM);
				val &= ~PINMUX_PWM_TRISTATE;
				mmio_write_32(TEGRA_MISC_BASE + PINMUX_AUX_DVFS_PWM, val);
			}

			/* release cluster idle lock */
			tegra_fc_ccplex_pgexit_unlock();
		}
359
360
361
362
363
364
365
	}

	/*
	 * T210 has a dedicated ARMv7 boot and power mgmt processor, BPMP. It's
	 * used for power management and boot purposes. Inform the BPMP that
	 * we have completed the cluster power up.
	 */
366
	tegra_fc_lock_active_cluster();
367
368
369
370

	return PSCI_E_SUCCESS;
}

371
int tegra_soc_pwr_domain_on(u_register_t mpidr)
372
373
{
	int cpu = mpidr & MPIDR_CPU_MASK;
374
375
376
377
	uint32_t mask = CPU_CORE_RESET_MASK << cpu;

	/* Deassert CPU reset signals */
	mmio_write_32(TEGRA_CAR_RESET_BASE + CPU_CMPLX_RESET_CLR, mask);
378
379
380
381
382
383
384
385
386
387
388
389

	/* Turn on CPU using flow controller or PMC */
	if (cpu_powergate_mask[cpu] == 0) {
		tegra_pmc_cpu_on(cpu);
		cpu_powergate_mask[cpu] = 1;
	} else {
		tegra_fc_cpu_on(cpu);
	}

	return PSCI_E_SUCCESS;
}

390
int tegra_soc_pwr_domain_off(const psci_power_state_t *target_state)
391
{
392
	tegra_fc_cpu_off(read_mpidr() & MPIDR_CPU_MASK);
393
394
	return PSCI_E_SUCCESS;
}
395
396
397
398
399
400
401
402

int tegra_soc_prepare_system_reset(void)
{
	/*
	 * Set System Clock (SCLK) to POR default so that the clock source
	 * for the PMC APB clock would not be changed due to system reset.
	 */
	mmio_write_32((uintptr_t)TEGRA_CAR_RESET_BASE + SCLK_BURST_POLICY,
403
		SCLK_BURST_POLICY_DEFAULT);
404
405
406
407
408
409
410
	mmio_write_32((uintptr_t)TEGRA_CAR_RESET_BASE + SCLK_RATE, 0);

	/* Wait 1 ms to make sure clock source/device logic is stabilized. */
	mdelay(1);

	return PSCI_E_SUCCESS;
}