blob: 74db8b63a4b1e0a9974fd9953f6cca1eb0428afb [file] [log] [blame]
Andrew F. Davis537d3ff2018-05-04 19:06:08 +00001/*
2 * Texas Instruments K3 Secure Proxy Driver
3 * Based on Linux and U-Boot implementation
4 *
Dhruva Gole87c1e232025-03-21 11:53:19 +05305 * Copyright (C) 2018-2025 Texas Instruments Incorporated - http://www.ti.com/
Andrew F. Davis537d3ff2018-05-04 19:06:08 +00006 *
7 * SPDX-License-Identifier: BSD-3-Clause
8 */
9
Andrew F. Davis537d3ff2018-05-04 19:06:08 +000010#include <errno.h>
Andrew F. Davis537d3ff2018-05-04 19:06:08 +000011#include <stdlib.h>
Antonio Nino Diaze0f90632018-12-14 00:18:21 +000012
13#include <platform_def.h>
14
15#include <arch_helpers.h>
16#include <common/debug.h>
17#include <lib/mmio.h>
18#include <lib/utils.h>
19#include <lib/utils_def.h>
Andrew F. Davis537d3ff2018-05-04 19:06:08 +000020
Dhruva Gole87c1e232025-03-21 11:53:19 +053021#include <ti_sci_transport.h>
Andrew F. Davis537d3ff2018-05-04 19:06:08 +000022
23/* SEC PROXY RT THREAD STATUS */
24#define RT_THREAD_STATUS (0x0)
25#define RT_THREAD_STATUS_ERROR_SHIFT (31)
26#define RT_THREAD_STATUS_ERROR_MASK BIT(31)
27#define RT_THREAD_STATUS_CUR_CNT_SHIFT (0)
28#define RT_THREAD_STATUS_CUR_CNT_MASK GENMASK(7, 0)
29
30/* SEC PROXY SCFG THREAD CTRL */
31#define SCFG_THREAD_CTRL (0x1000)
32#define SCFG_THREAD_CTRL_DIR_SHIFT (31)
33#define SCFG_THREAD_CTRL_DIR_MASK BIT(31)
34
35#define SEC_PROXY_THREAD(base, x) ((base) + (0x1000 * (x)))
36#define THREAD_IS_RX (1)
37#define THREAD_IS_TX (0)
38
39/**
40 * struct k3_sec_proxy_desc - Description of secure proxy integration
41 * @timeout_us: Timeout for communication (in Microseconds)
42 * @max_msg_size: Message size in bytes
43 * @data_start_offset: Offset of the First data register of the thread
44 * @data_end_offset: Offset of the Last data register of the thread
45 */
46struct k3_sec_proxy_desc {
47 uint32_t timeout_us;
48 uint16_t max_msg_size;
49 uint16_t data_start_offset;
50 uint16_t data_end_offset;
51};
52
53/**
Andrew F. Davis8e725cb2019-04-10 11:38:56 -040054 * struct k3_sec_proxy_thread - Description of a Secure Proxy Thread
55 * @name: Thread Name
Andrew F. Davis537d3ff2018-05-04 19:06:08 +000056 * @data: Thread Data path region for target
57 * @scfg: Secure Config Region for Thread
58 * @rt: RealTime Region for Thread
59 */
60struct k3_sec_proxy_thread {
Andrew F. Davis8e725cb2019-04-10 11:38:56 -040061 const char *name;
Andrew F. Davis537d3ff2018-05-04 19:06:08 +000062 uintptr_t data;
63 uintptr_t scfg;
64 uintptr_t rt;
65};
66
67/**
68 * struct k3_sec_proxy_mbox - Description of a Secure Proxy Instance
69 * @desc: Description of the SoC integration
70 * @chans: Array for valid thread instances
71 */
72struct k3_sec_proxy_mbox {
73 const struct k3_sec_proxy_desc desc;
74 struct k3_sec_proxy_thread threads[];
75};
76
77/*
78 * Thread ID #0: DMSC notify
79 * Thread ID #1: DMSC request response
80 * Thread ID #2: DMSC request high priority
81 * Thread ID #3: DMSC request low priority
82 * Thread ID #4: DMSC notify response
83 */
84#define SP_THREAD(_x) \
85 [_x] = { \
Andrew F. Davis8e725cb2019-04-10 11:38:56 -040086 .name = #_x, \
Andrew F. Davis537d3ff2018-05-04 19:06:08 +000087 .data = SEC_PROXY_THREAD(SEC_PROXY_DATA_BASE, _x), \
88 .scfg = SEC_PROXY_THREAD(SEC_PROXY_SCFG_BASE, _x), \
89 .rt = SEC_PROXY_THREAD(SEC_PROXY_RT_BASE, _x), \
90 }
91
92static struct k3_sec_proxy_mbox spm = {
93 .desc = {
94 .timeout_us = SEC_PROXY_TIMEOUT_US,
95 .max_msg_size = SEC_PROXY_MAX_MESSAGE_SIZE,
96 .data_start_offset = 0x4,
97 .data_end_offset = 0x3C,
98 },
99 .threads = {
Dhruva Gole528a3f22025-03-27 14:45:49 +0530100 SP_THREAD(RX_SECURE_TRANSPORT_CHANNEL_ID),
101 SP_THREAD(TX_SECURE_TRANSPORT_CHANNEL_ID),
Andrew F. Davis537d3ff2018-05-04 19:06:08 +0000102 },
103};
104
105/**
106 * struct sec_msg_hdr - Message header for secure messages and responses
107 * @checksum: CRC of message for integrity checking
108 */
109union sec_msg_hdr {
110 struct {
111 uint16_t checksum;
112 uint16_t reserved;
113 } __packed;
114 uint32_t data;
115};
116
117/**
118 * k3_sec_proxy_verify_thread() - Verify thread status before
119 * sending/receiving data
120 * @spt: Pointer to Secure Proxy thread description
121 * @dir: Direction of the thread
122 *
123 * Return: 0 if all goes well, else appropriate error message
124 */
Andrew Davis817bdbb2022-11-11 12:49:38 -0600125static int k3_sec_proxy_verify_thread(struct k3_sec_proxy_thread *spt,
Andrew F. Davis537d3ff2018-05-04 19:06:08 +0000126 uint32_t dir)
127{
128 /* Check for any errors already available */
129 if (mmio_read_32(spt->rt + RT_THREAD_STATUS) &
130 RT_THREAD_STATUS_ERROR_MASK) {
Andrew F. Davis8e725cb2019-04-10 11:38:56 -0400131 ERROR("Thread %s is corrupted, cannot send data\n", spt->name);
Andrew F. Davis537d3ff2018-05-04 19:06:08 +0000132 return -EINVAL;
133 }
134
135 /* Make sure thread is configured for right direction */
136 if ((mmio_read_32(spt->scfg + SCFG_THREAD_CTRL) & SCFG_THREAD_CTRL_DIR_MASK)
137 != (dir << SCFG_THREAD_CTRL_DIR_SHIFT)) {
Andrew F. Davis40d1ded2019-04-10 11:45:19 -0400138 if (dir == THREAD_IS_TX)
Andrew F. Davis8e725cb2019-04-10 11:38:56 -0400139 ERROR("Trying to send data on RX Thread %s\n",
140 spt->name);
Andrew F. Davis537d3ff2018-05-04 19:06:08 +0000141 else
Andrew F. Davis8e725cb2019-04-10 11:38:56 -0400142 ERROR("Trying to receive data on TX Thread %s\n",
143 spt->name);
Andrew F. Davis537d3ff2018-05-04 19:06:08 +0000144 return -EINVAL;
145 }
146
147 /* Check the message queue before sending/receiving data */
148 uint32_t tick_start = (uint32_t)read_cntpct_el0();
149 uint32_t ticks_per_us = SYS_COUNTER_FREQ_IN_TICKS / 1000000;
150 while (!(mmio_read_32(spt->rt + RT_THREAD_STATUS) & RT_THREAD_STATUS_CUR_CNT_MASK)) {
Andrew F. Davis40d1ded2019-04-10 11:45:19 -0400151 VERBOSE("Waiting for thread %s to %s\n",
152 spt->name, (dir == THREAD_IS_TX) ? "empty" : "fill");
Andrew F. Davis537d3ff2018-05-04 19:06:08 +0000153 if (((uint32_t)read_cntpct_el0() - tick_start) >
154 (spm.desc.timeout_us * ticks_per_us)) {
Andrew F. Davis40d1ded2019-04-10 11:45:19 -0400155 ERROR("Timeout waiting for thread %s to %s\n",
156 spt->name, (dir == THREAD_IS_TX) ? "empty" : "fill");
Andrew F. Davis537d3ff2018-05-04 19:06:08 +0000157 return -ETIMEDOUT;
158 }
159 }
160
161 return 0;
162}
163
164/**
Dhruva Gole08df3752025-03-21 11:55:20 +0530165 * ti_sci_transport_clear_rx_thread() - Clear Secure Proxy thread
Andrew F. Davis82a3c1d2019-01-04 12:44:00 -0600166 *
167 * @id: Channel Identifier
168 *
169 * Return: 0 if all goes well, else appropriate error message
170 */
Dhruva Gole528a3f22025-03-27 14:45:49 +0530171int ti_sci_transport_clear_rx_thread(enum ti_sci_transport_chan_id id)
Andrew F. Davis82a3c1d2019-01-04 12:44:00 -0600172{
173 struct k3_sec_proxy_thread *spt = &spm.threads[id];
174
175 /* Check for any errors already available */
176 if (mmio_read_32(spt->rt + RT_THREAD_STATUS) &
177 RT_THREAD_STATUS_ERROR_MASK) {
Andrew F. Davis8e725cb2019-04-10 11:38:56 -0400178 ERROR("Thread %s is corrupted, cannot send data\n", spt->name);
Andrew F. Davis82a3c1d2019-01-04 12:44:00 -0600179 return -EINVAL;
180 }
181
182 /* Make sure thread is configured for right direction */
183 if (!(mmio_read_32(spt->scfg + SCFG_THREAD_CTRL) & SCFG_THREAD_CTRL_DIR_MASK)) {
Andrew F. Davis8e725cb2019-04-10 11:38:56 -0400184 ERROR("Cannot clear a transmit thread %s\n", spt->name);
Andrew F. Davis82a3c1d2019-01-04 12:44:00 -0600185 return -EINVAL;
186 }
187
188 /* Read off messages from thread until empty */
189 uint32_t try_count = 10;
190 while (mmio_read_32(spt->rt + RT_THREAD_STATUS) & RT_THREAD_STATUS_CUR_CNT_MASK) {
191 if (!(try_count--)) {
Andrew F. Davis8e725cb2019-04-10 11:38:56 -0400192 ERROR("Could not clear all messages from thread %s\n", spt->name);
Andrew F. Davis82a3c1d2019-01-04 12:44:00 -0600193 return -ETIMEDOUT;
194 }
Andrew F. Davis8e725cb2019-04-10 11:38:56 -0400195 WARN("Clearing message from thread %s\n", spt->name);
Andrew F. Davis82a3c1d2019-01-04 12:44:00 -0600196 mmio_read_32(spt->data + spm.desc.data_end_offset);
197 }
198
199 return 0;
200}
201
202/**
Dhruva Gole08df3752025-03-21 11:55:20 +0530203 * ti_sci_transport_send() - Send data over a Secure Proxy thread
Andrew F. Davis537d3ff2018-05-04 19:06:08 +0000204 * @id: Channel Identifier
Dhruva Gole08df3752025-03-21 11:55:20 +0530205 * @msg: Pointer to ti_sci_msg
Andrew F. Davis537d3ff2018-05-04 19:06:08 +0000206 *
207 * Return: 0 if all goes well, else appropriate error message
208 */
Dhruva Gole528a3f22025-03-27 14:45:49 +0530209int ti_sci_transport_send(enum ti_sci_transport_chan_id id, const struct ti_sci_msg *msg)
Andrew F. Davis537d3ff2018-05-04 19:06:08 +0000210{
211 struct k3_sec_proxy_thread *spt = &spm.threads[id];
212 union sec_msg_hdr secure_header;
213 int num_words, trail_bytes, i, ret;
214 uintptr_t data_reg;
215
216 ret = k3_sec_proxy_verify_thread(spt, THREAD_IS_TX);
217 if (ret) {
Andrew F. Davis8e725cb2019-04-10 11:38:56 -0400218 ERROR("Thread %s verification failed (%d)\n", spt->name, ret);
Andrew F. Davis537d3ff2018-05-04 19:06:08 +0000219 return ret;
220 }
221
222 /* Check the message size */
223 if (msg->len + sizeof(secure_header) > spm.desc.max_msg_size) {
Andrew F. Davis8e725cb2019-04-10 11:38:56 -0400224 ERROR("Thread %s message length %lu > max msg size\n",
225 spt->name, msg->len);
Andrew F. Davis537d3ff2018-05-04 19:06:08 +0000226 return -EINVAL;
227 }
228
229 /* TODO: Calculate checksum */
230 secure_header.checksum = 0;
231
232 /* Send the secure header */
233 data_reg = spm.desc.data_start_offset;
234 mmio_write_32(spt->data + data_reg, secure_header.data);
235 data_reg += sizeof(uint32_t);
236
237 /* Send whole words */
238 num_words = msg->len / sizeof(uint32_t);
239 for (i = 0; i < num_words; i++) {
240 mmio_write_32(spt->data + data_reg, ((uint32_t *)msg->buf)[i]);
241 data_reg += sizeof(uint32_t);
242 }
243
244 /* Send remaining bytes */
245 trail_bytes = msg->len % sizeof(uint32_t);
246 if (trail_bytes) {
247 uint32_t data_trail = 0;
248
249 i = msg->len - trail_bytes;
250 while (trail_bytes--) {
251 data_trail <<= 8;
252 data_trail |= msg->buf[i++];
253 }
254
255 mmio_write_32(spt->data + data_reg, data_trail);
256 data_reg += sizeof(uint32_t);
257 }
258 /*
259 * 'data_reg' indicates next register to write. If we did not already
260 * write on tx complete reg(last reg), we must do so for transmit
Nishanth Menon1ecd9e82020-12-10 17:37:04 -0600261 * In addition, we also need to make sure all intermediate data
262 * registers(if any required), are reset to 0 for TISCI backward
263 * compatibility to be maintained.
Andrew F. Davis537d3ff2018-05-04 19:06:08 +0000264 */
Nishanth Menon1ecd9e82020-12-10 17:37:04 -0600265 while (data_reg <= spm.desc.data_end_offset) {
266 mmio_write_32(spt->data + data_reg, 0);
267 data_reg += sizeof(uint32_t);
268 }
Andrew F. Davis537d3ff2018-05-04 19:06:08 +0000269
Andrew F. Davis8e725cb2019-04-10 11:38:56 -0400270 VERBOSE("Message successfully sent on thread %s\n", spt->name);
Andrew F. Davis537d3ff2018-05-04 19:06:08 +0000271
272 return 0;
273}
274
275/**
Dhruva Gole08df3752025-03-21 11:55:20 +0530276 * ti_sci_transport_recv() - Receive data from a Secure Proxy thread
Andrew F. Davis537d3ff2018-05-04 19:06:08 +0000277 * @id: Channel Identifier
Dhruva Gole08df3752025-03-21 11:55:20 +0530278 * @msg: Pointer to ti_sci_msg
Andrew F. Davis537d3ff2018-05-04 19:06:08 +0000279 *
280 * Return: 0 if all goes well, else appropriate error message
281 */
Dhruva Gole528a3f22025-03-27 14:45:49 +0530282int ti_sci_transport_recv(enum ti_sci_transport_chan_id id, struct ti_sci_msg *msg)
Andrew F. Davis537d3ff2018-05-04 19:06:08 +0000283{
284 struct k3_sec_proxy_thread *spt = &spm.threads[id];
285 union sec_msg_hdr secure_header;
286 uintptr_t data_reg;
287 int num_words, trail_bytes, i, ret;
288
289 ret = k3_sec_proxy_verify_thread(spt, THREAD_IS_RX);
290 if (ret) {
Andrew F. Davis8e725cb2019-04-10 11:38:56 -0400291 ERROR("Thread %s verification failed (%d)\n", spt->name, ret);
Andrew F. Davis537d3ff2018-05-04 19:06:08 +0000292 return ret;
293 }
294
295 /* Read secure header */
296 data_reg = spm.desc.data_start_offset;
297 secure_header.data = mmio_read_32(spt->data + data_reg);
298 data_reg += sizeof(uint32_t);
299
300 /* Read whole words */
301 num_words = msg->len / sizeof(uint32_t);
302 for (i = 0; i < num_words; i++) {
303 ((uint32_t *)msg->buf)[i] = mmio_read_32(spt->data + data_reg);
304 data_reg += sizeof(uint32_t);
305 }
306
307 /* Read remaining bytes */
308 trail_bytes = msg->len % sizeof(uint32_t);
309 if (trail_bytes) {
310 uint32_t data_trail = mmio_read_32(spt->data + data_reg);
311 data_reg += sizeof(uint32_t);
312
313 i = msg->len - trail_bytes;
314 while (trail_bytes--) {
Manorit Chawdhrydd512f22024-01-17 15:06:58 +0530315 msg->buf[i++] = data_trail & 0xff;
Andrew F. Davis537d3ff2018-05-04 19:06:08 +0000316 data_trail >>= 8;
317 }
318 }
319
320 /*
321 * 'data_reg' indicates next register to read. If we did not already
322 * read on rx complete reg(last reg), we must do so for receive
323 */
324 if (data_reg <= spm.desc.data_end_offset)
325 mmio_read_32(spt->data + spm.desc.data_end_offset);
326
327 /* TODO: Verify checksum */
328 (void)secure_header.checksum;
329
Andrew F. Davis8e725cb2019-04-10 11:38:56 -0400330 VERBOSE("Message successfully received from thread %s\n", spt->name);
Andrew F. Davis537d3ff2018-05-04 19:06:08 +0000331
332 return 0;
333}