se.c 6.99 KB
Newer Older
1
2
/*
 * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved.
3
 * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved.
4
5
6
7
8
9
10
11
12
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <assert.h>
#include <errno.h>
#include <stdbool.h>

#include <arch_helpers.h>
13
#include <bpmp_ipc.h>
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#include <common/debug.h>
#include <drivers/delay_timer.h>
#include <lib/mmio.h>
#include <lib/psci/psci.h>
#include <tegra_platform.h>

#include "se_private.h"

/*******************************************************************************
 * Constants and Macros
 ******************************************************************************/
#define ERR_STATUS_SW_CLEAR	U(0xFFFFFFFF)
#define INT_STATUS_SW_CLEAR	U(0xFFFFFFFF)
#define MAX_TIMEOUT_MS		U(100)	/* Timeout in 100ms */
#define NUM_SE_REGS_TO_SAVE	U(4)

/*******************************************************************************
 * Data structure and global variables
 ******************************************************************************/
static uint32_t se_regs[NUM_SE_REGS_TO_SAVE];

/*
 * Check that SE operation has completed after kickoff.
 *
 * This function is invoked after an SE operation has been started,
 * and it checks the following conditions:
 *
 * 1. SE_STATUS = IDLE
 * 2. AHB bus data transfer is complete.
 * 3. SE_ERR_STATUS is clean.
 */
static bool tegra_se_is_operation_complete(void)
{
	uint32_t val = 0, timeout = 0, sha_status, aes_status;
	int32_t ret = 0;
	bool se_is_busy, txn_has_errors, txn_successful;

	/*
	 * Poll the status register to check if the operation
	 * completed.
	 */
	do {
		val = tegra_se_read_32(CTX_SAVE_AUTO_STATUS);
		se_is_busy = !!(val & CTX_SAVE_AUTO_SE_BUSY);

		/* sleep until SE finishes */
		if (se_is_busy) {
			mdelay(1);
			timeout++;
		}

	} while (se_is_busy && (timeout < MAX_TIMEOUT_MS));

	/* any transaction errors? */
	txn_has_errors = (tegra_se_read_32(SHA_ERR_STATUS) != 0U) ||
			 (tegra_se_read_32(AES0_ERR_STATUS) != 0U);

	/* transaction successful? */
	sha_status = tegra_se_read_32(SHA_INT_STATUS) & SHA_SE_OP_DONE;
	aes_status = tegra_se_read_32(AES0_INT_STATUS) & AES0_SE_OP_DONE;
	txn_successful = (sha_status == SHA_SE_OP_DONE) &&
			 (aes_status == AES0_SE_OP_DONE);

	if ((timeout == MAX_TIMEOUT_MS) || txn_has_errors || !txn_successful) {
		ERROR("%s: Atomic context save operation failed!\n",
				__func__);
		ret = -ECANCELED;
	}

	return (ret == 0);
}

/*
 * Wait for SE engine to be idle and clear any pending interrupts, before
 * starting the next SE operation.
 */
static bool tegra_se_is_ready(void)
{
	int32_t ret = 0;
	uint32_t val = 0, timeout = 0;
	bool se_is_ready;

	/* Wait for previous operation to finish */
	do {
		val = tegra_se_read_32(CTX_SAVE_AUTO_STATUS);
		se_is_ready = (val == CTX_SAVE_AUTO_SE_READY);

		/* sleep until SE is ready */
		if (!se_is_ready) {
			mdelay(1);
			timeout++;
		}

	} while (!se_is_ready && (timeout < MAX_TIMEOUT_MS));

	if (timeout == MAX_TIMEOUT_MS) {
		ERROR("%s: SE is not ready!\n", __func__);
		ret = -ETIMEDOUT;
	}

	/* Clear any pending interrupts from previous operation */
	tegra_se_write_32(AES0_INT_STATUS, INT_STATUS_SW_CLEAR);
	tegra_se_write_32(AES1_INT_STATUS, INT_STATUS_SW_CLEAR);
	tegra_se_write_32(RSA_INT_STATUS, INT_STATUS_SW_CLEAR);
	tegra_se_write_32(SHA_INT_STATUS, INT_STATUS_SW_CLEAR);

	/* Clear error status for each engine seen from current port */
	tegra_se_write_32(AES0_ERR_STATUS, ERR_STATUS_SW_CLEAR);
	tegra_se_write_32(AES1_ERR_STATUS, ERR_STATUS_SW_CLEAR);
	tegra_se_write_32(RSA_ERR_STATUS, ERR_STATUS_SW_CLEAR);
	tegra_se_write_32(SHA_ERR_STATUS, ERR_STATUS_SW_CLEAR);

	return (ret == 0);
}

/*
 * During System Suspend, this handler triggers the hardware context
 * save operation.
 */
static int32_t tegra_se_save_context(void)
{
	int32_t ret = -ECANCELED;

	/*
	 * 1. Ensure all SE Driver including RNG1/PKA1 are shut down.
	 *    TSEC/R5s are powergated/idle. All tasks on SE1~SE4, RNG1,
	 *    PKA1 are wrapped up. SE0 is ready for use.
	 * 2. Clear interrupt/error in SE0 status register.
	 * 3. Scrub SE0 register to avoid false failure for illegal
	 *    configuration. Probably not needed, dependent on HW
	 *    implementation.
	 * 4. Check SE is ready for HW CTX_SAVE by polling
	 *    SE_CTX_SAVE_AUTO_STATUS.SE_READY.
	 *
	 *    Steps 1-4 are executed by tegra_se_is_ready().
	 *
	 * 5. Issue context save command.
	 * 6. Check SE is busy with CTX_SAVE, the command in step5 was not
	 *    dropped for ongoing traffic in any of SE port/engine.
	 * 7. Poll SE register or wait for SE APB interrupt for task completion
	 *    a. Polling: Read SE_CTX_SAVE_AUTO_STATUS.BUSY till it reports IDLE
	 *    b. Interrupt: After receiving interrupt from SE APB, read
	 *       SE_CTX_SAVE_AUTO_STATUS.BUSY till it reports IDLE.
	 * 8. Check AES0 and SHA ERR_STATUS to ensure no error case.
	 * 9. Check AES0 and SHA INT_STATUS to ensure operation has successfully
	 *    completed.
	 *
	 *    Steps 6-9 are executed by tegra_se_is_operation_complete().
	 */
	if (tegra_se_is_ready()) {

		/* Issue context save command */
		tegra_se_write_32(AES0_OPERATION, SE_OP_CTX_SAVE);

		/* Wait for operation to finish */
		if (tegra_se_is_operation_complete()) {
			ret = 0;
		}
	}

	return ret;
}

/*
 * Handler to power down the SE hardware blocks - SE, RNG1 and PKA1. This
 * needs to be called only during System Suspend.
 */
int32_t tegra_se_suspend(void)
{
	int32_t ret = 0;

185
186
187
188
189
190
	/* initialise communication channel with BPMP */
	assert(tegra_bpmp_ipc_init() == 0);

	/* Enable SE clock before SE context save */
	tegra_bpmp_ipc_enable_clock(TEGRA_CLK_SE);

191
192
193
194
195
196
197
198
199
200
201
202
	/* save SE registers */
	se_regs[0] = mmio_read_32(TEGRA_SE0_BASE + SE0_MUTEX_WATCHDOG_NS_LIMIT);
	se_regs[1] = mmio_read_32(TEGRA_SE0_BASE + SE0_AES0_ENTROPY_SRC_AGE_CTRL);
	se_regs[2] = mmio_read_32(TEGRA_RNG1_BASE + RNG1_MUTEX_WATCHDOG_NS_LIMIT);
	se_regs[3] = mmio_read_32(TEGRA_PKA1_BASE + PKA1_MUTEX_WATCHDOG_NS_LIMIT);

	/* Save SE context. The BootROM restores it during System Resume */
	ret = tegra_se_save_context();
	if (ret != 0) {
		ERROR("%s: context save failed (%d)\n", __func__, ret);
	}

203
204
205
	/* Disable SE clock after SE context save */
	tegra_bpmp_ipc_disable_clock(TEGRA_CLK_SE);

206
207
208
209
210
211
212
213
	return ret;
}

/*
 * Handler to power up the SE hardware block(s) during System Resume.
 */
void tegra_se_resume(void)
{
214
215
216
217
218
219
	/* initialise communication channel with BPMP */
	assert(tegra_bpmp_ipc_init() == 0);

	/* Enable SE clock before SE context restore */
	tegra_bpmp_ipc_enable_clock(TEGRA_CLK_SE);

220
221
222
223
224
225
226
227
228
229
	/*
	 * When TZ takes over after System Resume, TZ should first reconfigure
	 * SE_MUTEX_WATCHDOG_NS_LIMIT, PKA1_MUTEX_WATCHDOG_NS_LIMIT,
	 * RNG1_MUTEX_WATCHDOG_NS_LIMIT and SE_ENTROPY_SRC_AGE_CTRL before
	 * other operations.
	 */
	mmio_write_32(TEGRA_SE0_BASE + SE0_MUTEX_WATCHDOG_NS_LIMIT, se_regs[0]);
	mmio_write_32(TEGRA_SE0_BASE + SE0_AES0_ENTROPY_SRC_AGE_CTRL, se_regs[1]);
	mmio_write_32(TEGRA_RNG1_BASE + RNG1_MUTEX_WATCHDOG_NS_LIMIT, se_regs[2]);
	mmio_write_32(TEGRA_PKA1_BASE + PKA1_MUTEX_WATCHDOG_NS_LIMIT, se_regs[3]);
230
231
232

	/* Disable SE clock after SE context restore */
	tegra_bpmp_ipc_disable_clock(TEGRA_CLK_SE);
233
}