blob: f9c34e857117bd5c5e2ef62630fc12e85fd325d7 [file] [log] [blame]
Ley Foon Tane5b6a662018-05-24 00:17:25 +08001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2017-2018 Intel Corporation <www.intel.com>
4 *
5 */
6
Siew Chin Limc1888b02021-03-24 17:16:50 +08007#include <asm/arch/clock_manager.h>
Ley Foon Tane5b6a662018-05-24 00:17:25 +08008#include <asm/arch/mailbox_s10.h>
Alif Zakuan Yuslaimia7e1ba32025-03-10 23:38:51 -07009#include <asm/arch/smc_api.h>
Ley Foon Tane5b6a662018-05-24 00:17:25 +080010#include <asm/arch/system_manager.h>
Siew Chin Limc1888b02021-03-24 17:16:50 +080011#include <asm/global_data.h>
12#include <asm/io.h>
Ley Foon Tane5b6a662018-05-24 00:17:25 +080013#include <asm/secure.h>
Chee Hong Ang6aa10582020-12-24 18:21:08 +080014#include <asm/system.h>
Siew Chin Limc1888b02021-03-24 17:16:50 +080015#include <hang.h>
16#include <wait_bit.h>
Ley Foon Tane5b6a662018-05-24 00:17:25 +080017
18DECLARE_GLOBAL_DATA_PTR;
19
20#define MBOX_READL(reg) \
21 readl(SOCFPGA_MAILBOX_ADDRESS + (reg))
22
23#define MBOX_WRITEL(data, reg) \
24 writel(data, SOCFPGA_MAILBOX_ADDRESS + (reg))
25
26#define MBOX_READ_RESP_BUF(rout) \
27 MBOX_READL(MBOX_RESP_BUF + ((rout) * sizeof(u32)))
28
29#define MBOX_WRITE_CMD_BUF(data, cin) \
30 MBOX_WRITEL(data, MBOX_CMD_BUF + ((cin) * sizeof(u32)))
31
32static __always_inline int mbox_polling_resp(u32 rout)
33{
34 u32 rin;
Chee Hong Ang0cc64c82020-08-12 09:56:21 +080035 unsigned long i = 2000;
Ley Foon Tane5b6a662018-05-24 00:17:25 +080036
37 while (i) {
38 rin = MBOX_READL(MBOX_RIN);
39 if (rout != rin)
40 return 0;
41
Chee Hong Ang0cc64c82020-08-12 09:56:21 +080042 udelay(1000);
Ley Foon Tane5b6a662018-05-24 00:17:25 +080043 i--;
44 }
45
46 return -ETIMEDOUT;
47}
48
Chee Hong Ange4805862020-08-12 09:56:23 +080049static __always_inline int mbox_is_cmdbuf_full(u32 cin)
50{
51 return (((cin + 1) % MBOX_CMD_BUFFER_SIZE) == MBOX_READL(MBOX_COUT));
52}
53
54static __always_inline int mbox_is_cmdbuf_empty(u32 cin)
55{
56 return (((MBOX_READL(MBOX_COUT) + 1) % MBOX_CMD_BUFFER_SIZE) == cin);
57}
58
59static __always_inline int mbox_wait_for_cmdbuf_empty(u32 cin)
60{
61 int timeout = 2000;
62
63 while (timeout) {
64 if (mbox_is_cmdbuf_empty(cin))
65 return 0;
66 udelay(1000);
67 timeout--;
68 }
69
70 return -ETIMEDOUT;
71}
72
73static __always_inline int mbox_write_cmd_buffer(u32 *cin, u32 data,
74 int *is_cmdbuf_overflow)
75{
76 int timeout = 1000;
77
78 while (timeout) {
79 if (mbox_is_cmdbuf_full(*cin)) {
80 if (is_cmdbuf_overflow &&
81 *is_cmdbuf_overflow == 0) {
82 /* Trigger SDM doorbell */
83 MBOX_WRITEL(1, MBOX_DOORBELL_TO_SDM);
84 *is_cmdbuf_overflow = 1;
85 }
86 udelay(1000);
87 } else {
88 /* write header to circular buffer */
89 MBOX_WRITE_CMD_BUF(data, (*cin)++);
90 *cin %= MBOX_CMD_BUFFER_SIZE;
91 MBOX_WRITEL(*cin, MBOX_CIN);
Alif Zakuan Yuslaimi39c68c52025-01-22 10:21:02 +080092 if (is_cmdbuf_overflow)
93 *is_cmdbuf_overflow = 0;
Chee Hong Ange4805862020-08-12 09:56:23 +080094 break;
95 }
96 timeout--;
97 }
98
99 if (!timeout)
100 return -ETIMEDOUT;
101
Chee Hong Ange4805862020-08-12 09:56:23 +0800102 return 0;
103}
104
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800105/* Check for available slot and write to circular buffer.
106 * It also update command valid offset (cin) register.
107 */
108static __always_inline int mbox_fill_cmd_circular_buff(u32 header, u32 len,
109 u32 *arg)
110{
Chee Hong Ange4805862020-08-12 09:56:23 +0800111 int i, ret;
112 int is_cmdbuf_overflow = 0;
113 u32 cin = MBOX_READL(MBOX_CIN) % MBOX_CMD_BUFFER_SIZE;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800114
Chee Hong Ange4805862020-08-12 09:56:23 +0800115 ret = mbox_write_cmd_buffer(&cin, header, &is_cmdbuf_overflow);
116 if (ret)
117 return ret;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800118
119 /* write arguments */
120 for (i = 0; i < len; i++) {
Chee Hong Ange4805862020-08-12 09:56:23 +0800121 is_cmdbuf_overflow = 0;
122 ret = mbox_write_cmd_buffer(&cin, arg[i], &is_cmdbuf_overflow);
123 if (ret)
124 return ret;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800125 }
126
Alif Zakuan Yuslaimi39c68c52025-01-22 10:21:02 +0800127 /*
128 * Always trigger the SDM doorbell at the end to ensure SDM able to read
129 * the remaining data.
Chee Hong Ange4805862020-08-12 09:56:23 +0800130 */
Alif Zakuan Yuslaimi39c68c52025-01-22 10:21:02 +0800131 MBOX_WRITEL(1, MBOX_DOORBELL_TO_SDM);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800132
133 return 0;
134}
135
136/* Check the command and fill it into circular buffer */
137static __always_inline int mbox_prepare_cmd_only(u8 id, u32 cmd,
138 u8 is_indirect, u32 len,
139 u32 *arg)
140{
141 u32 header;
142 int ret;
143
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800144 if (cmd > MBOX_MAX_CMD_INDEX)
145 return -EINVAL;
146
147 header = MBOX_CMD_HEADER(MBOX_CLIENT_ID_UBOOT, id, len,
148 (is_indirect) ? 1 : 0, cmd);
149
150 ret = mbox_fill_cmd_circular_buff(header, len, arg);
151
152 return ret;
153}
154
155/* Send command only without waiting for responses from SDM */
156static __always_inline int mbox_send_cmd_only_common(u8 id, u32 cmd,
157 u8 is_indirect, u32 len,
158 u32 *arg)
159{
Chee Hong Ange4805862020-08-12 09:56:23 +0800160 return mbox_prepare_cmd_only(id, cmd, is_indirect, len, arg);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800161}
162
163/* Return number of responses received in buffer */
164static __always_inline int __mbox_rcv_resp(u32 *resp_buf, u32 resp_buf_max_len)
165{
166 u32 rin;
167 u32 rout;
168 u32 resp_len = 0;
169
170 /* clear doorbell from SDM if it was SET */
171 if (MBOX_READL(MBOX_DOORBELL_FROM_SDM) & 1)
172 MBOX_WRITEL(0, MBOX_DOORBELL_FROM_SDM);
173
174 /* read current response offset */
175 rout = MBOX_READL(MBOX_ROUT);
176 /* read response valid offset */
177 rin = MBOX_READL(MBOX_RIN);
178
179 while (rin != rout && (resp_len < resp_buf_max_len)) {
180 /* Response received */
181 if (resp_buf)
182 resp_buf[resp_len++] = MBOX_READ_RESP_BUF(rout);
183
184 rout++;
185 /* wrapping around when it reach the buffer size */
186 rout %= MBOX_RESP_BUFFER_SIZE;
187 /* update next ROUT */
188 MBOX_WRITEL(rout, MBOX_ROUT);
189 }
190
191 return resp_len;
192}
193
194/* Support one command and up to 31 words argument length only */
195static __always_inline int mbox_send_cmd_common(u8 id, u32 cmd, u8 is_indirect,
196 u32 len, u32 *arg, u8 urgent,
197 u32 *resp_buf_len,
198 u32 *resp_buf)
199{
200 u32 rin;
201 u32 resp;
202 u32 rout;
203 u32 status;
204 u32 resp_len;
205 u32 buf_len;
206 int ret;
207
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800208 if (urgent) {
209 /* Read status because it is toggled */
210 status = MBOX_READL(MBOX_STATUS) & MBOX_STATUS_UA_MSK;
Ley Foon Tanff756cc2018-08-17 16:22:03 +0800211 /* Write urgent command to urgent register */
212 MBOX_WRITEL(cmd, MBOX_URG);
Chee Hong Ange4805862020-08-12 09:56:23 +0800213 /* write doorbell */
214 MBOX_WRITEL(1, MBOX_DOORBELL_TO_SDM);
Ley Foon Tanff756cc2018-08-17 16:22:03 +0800215 } else {
216 ret = mbox_prepare_cmd_only(id, cmd, is_indirect, len, arg);
217 if (ret)
218 return ret;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800219 }
220
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800221 while (1) {
Chee Hong Ang0cc64c82020-08-12 09:56:21 +0800222 ret = 1000;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800223
224 /* Wait for doorbell from SDM */
Chee Hong Ang0cc64c82020-08-12 09:56:21 +0800225 do {
226 if (MBOX_READL(MBOX_DOORBELL_FROM_SDM))
227 break;
228 udelay(1000);
229 } while (--ret);
230
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800231 if (!ret)
232 return -ETIMEDOUT;
233
234 /* clear interrupt */
235 MBOX_WRITEL(0, MBOX_DOORBELL_FROM_SDM);
236
237 if (urgent) {
238 u32 new_status = MBOX_READL(MBOX_STATUS);
Ley Foon Tanff756cc2018-08-17 16:22:03 +0800239
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800240 /* Urgent ACK is toggled */
241 if ((new_status & MBOX_STATUS_UA_MSK) ^ status)
242 return 0;
243
244 return -ECOMM;
245 }
246
247 /* read current response offset */
248 rout = MBOX_READL(MBOX_ROUT);
249
250 /* read response valid offset */
251 rin = MBOX_READL(MBOX_RIN);
252
253 if (rout != rin) {
254 /* Response received */
255 resp = MBOX_READ_RESP_BUF(rout);
256 rout++;
257 /* wrapping around when it reach the buffer size */
258 rout %= MBOX_RESP_BUFFER_SIZE;
259 /* update next ROUT */
260 MBOX_WRITEL(rout, MBOX_ROUT);
261
262 /* check client ID and ID */
263 if ((MBOX_RESP_CLIENT_GET(resp) ==
264 MBOX_CLIENT_ID_UBOOT) &&
265 (MBOX_RESP_ID_GET(resp) == id)) {
Chee Hong Angfe33ea32020-08-12 09:56:22 +0800266 int resp_err = MBOX_RESP_ERR_GET(resp);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800267
268 if (resp_buf_len) {
269 buf_len = *resp_buf_len;
270 *resp_buf_len = 0;
271 } else {
272 buf_len = 0;
273 }
274
275 resp_len = MBOX_RESP_LEN_GET(resp);
276 while (resp_len) {
277 ret = mbox_polling_resp(rout);
278 if (ret)
279 return ret;
280 /* we need to process response buffer
281 * even caller doesn't need it
282 */
283 resp = MBOX_READ_RESP_BUF(rout);
284 rout++;
285 resp_len--;
286 rout %= MBOX_RESP_BUFFER_SIZE;
287 MBOX_WRITEL(rout, MBOX_ROUT);
288 if (buf_len) {
289 /* copy response to buffer */
290 resp_buf[*resp_buf_len] = resp;
291 (*resp_buf_len)++;
292 buf_len--;
293 }
294 }
Chee Hong Angfe33ea32020-08-12 09:56:22 +0800295 return resp_err;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800296 }
297 }
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800298 }
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800299
300 return -EIO;
301}
302
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800303static __always_inline int mbox_send_cmd_common_retry(u8 id, u32 cmd,
304 u8 is_indirect,
305 u32 len, u32 *arg,
306 u8 urgent,
307 u32 *resp_buf_len,
308 u32 *resp_buf)
309{
310 int ret;
311 int i;
312
313 for (i = 0; i < 3; i++) {
314 ret = mbox_send_cmd_common(id, cmd, is_indirect, len, arg,
315 urgent, resp_buf_len, resp_buf);
316 if (ret == MBOX_RESP_TIMEOUT || ret == MBOX_RESP_DEVICE_BUSY)
317 udelay(2000); /* wait for 2ms before resend */
318 else
319 break;
320 }
321
322 return ret;
323}
324
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800325int mbox_init(void)
326{
327 int ret;
328
329 /* enable mailbox interrupts */
330 MBOX_WRITEL(MBOX_ALL_INTRS, MBOX_FLAGS);
331
332 /* Ensure urgent request is cleared */
333 MBOX_WRITEL(0, MBOX_URG);
334
335 /* Ensure the Doorbell Interrupt is cleared */
336 MBOX_WRITEL(0, MBOX_DOORBELL_FROM_SDM);
337
338 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_RESTART, MBOX_CMD_DIRECT, 0,
339 NULL, 1, 0, NULL);
340 if (ret)
341 return ret;
342
343 /* Renable mailbox interrupts after MBOX_RESTART */
344 MBOX_WRITEL(MBOX_ALL_INTRS, MBOX_FLAGS);
345
346 return 0;
347}
348
349#ifdef CONFIG_CADENCE_QSPI
350int mbox_qspi_close(void)
351{
352 return mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_CLOSE, MBOX_CMD_DIRECT,
353 0, NULL, 0, 0, NULL);
354}
355
356int mbox_qspi_open(void)
357{
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800358 int ret;
359 u32 resp_buf[1];
360 u32 resp_buf_len;
361
362 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_OPEN, MBOX_CMD_DIRECT,
363 0, NULL, 0, 0, NULL);
364 if (ret) {
365 /* retry again by closing and reopen the QSPI again */
366 ret = mbox_qspi_close();
367 if (ret)
368 return ret;
369
370 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_OPEN,
371 MBOX_CMD_DIRECT, 0, NULL, 0, 0, NULL);
372 if (ret)
373 return ret;
374 }
375
376 /* HPS will directly control the QSPI controller, no longer mailbox */
377 resp_buf_len = 1;
378 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_DIRECT, MBOX_CMD_DIRECT,
379 0, NULL, 0, (u32 *)&resp_buf_len,
380 (u32 *)&resp_buf);
381 if (ret)
382 goto error;
383
Siew Chin Limc1888b02021-03-24 17:16:50 +0800384 /* Store QSPI controller ref clock frequency */
385 ret = cm_set_qspi_controller_clk_hz(resp_buf[0]);
386 if (ret)
387 goto error;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800388
389 return 0;
390
391error:
392 mbox_qspi_close();
393
394 return ret;
395}
396#endif /* CONFIG_CADENCE_QSPI */
397
398int mbox_reset_cold(void)
399{
Simon Glass85ed77d2024-09-29 19:49:46 -0600400#if !defined(CONFIG_XPL_BUILD) && defined(CONFIG_SPL_ATF)
Chee Hong Ang6aa10582020-12-24 18:21:08 +0800401 psci_system_reset();
402#else
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800403 int ret;
404
405 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_REBOOT_HPS, MBOX_CMD_DIRECT,
406 0, NULL, 0, 0, NULL);
407 if (ret) {
408 /* mailbox sent failure, wait for watchdog to kick in */
409 hang();
410 }
Chee Hong Ang6aa10582020-12-24 18:21:08 +0800411#endif
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800412 return 0;
413}
414
Ang, Chee Hong31b79632018-12-19 18:35:12 -0800415/* Accepted commands: CONFIG_STATUS or RECONFIG_STATUS */
416static __always_inline int mbox_get_fpga_config_status_common(u32 cmd)
417{
418 u32 reconfig_status_resp_len;
419 u32 reconfig_status_resp[RECONFIG_STATUS_RESPONSE_LEN];
420 int ret;
421
422 reconfig_status_resp_len = RECONFIG_STATUS_RESPONSE_LEN;
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800423 ret = mbox_send_cmd_common_retry(MBOX_ID_UBOOT, cmd,
424 MBOX_CMD_DIRECT, 0, NULL, 0,
425 &reconfig_status_resp_len,
426 reconfig_status_resp);
Ang, Chee Hong31b79632018-12-19 18:35:12 -0800427
428 if (ret)
429 return ret;
430
431 /* Check for any error */
432 ret = reconfig_status_resp[RECONFIG_STATUS_STATE];
433 if (ret && ret != MBOX_CFGSTAT_STATE_CONFIG)
434 return ret;
435
436 /* Make sure nStatus is not 0 */
437 ret = reconfig_status_resp[RECONFIG_STATUS_PIN_STATUS];
438 if (!(ret & RCF_PIN_STATUS_NSTATUS))
439 return MBOX_CFGSTAT_STATE_ERROR_HARDWARE;
440
441 ret = reconfig_status_resp[RECONFIG_STATUS_SOFTFUNC_STATUS];
442 if (ret & RCF_SOFTFUNC_STATUS_SEU_ERROR)
443 return MBOX_CFGSTAT_STATE_ERROR_HARDWARE;
444
445 if ((ret & RCF_SOFTFUNC_STATUS_CONF_DONE) &&
446 (ret & RCF_SOFTFUNC_STATUS_INIT_DONE) &&
447 !reconfig_status_resp[RECONFIG_STATUS_STATE])
448 return 0; /* configuration success */
449
450 return MBOX_CFGSTAT_STATE_CONFIG;
451}
452
453int mbox_get_fpga_config_status(u32 cmd)
454{
455 return mbox_get_fpga_config_status_common(cmd);
456}
457
458int __secure mbox_get_fpga_config_status_psci(u32 cmd)
459{
460 return mbox_get_fpga_config_status_common(cmd);
461}
462
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800463int mbox_send_cmd(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg,
464 u8 urgent, u32 *resp_buf_len, u32 *resp_buf)
465{
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800466 return mbox_send_cmd_common_retry(id, cmd, is_indirect, len, arg,
467 urgent, resp_buf_len, resp_buf);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800468}
469
470int __secure mbox_send_cmd_psci(u8 id, u32 cmd, u8 is_indirect, u32 len,
471 u32 *arg, u8 urgent, u32 *resp_buf_len,
472 u32 *resp_buf)
473{
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800474 return mbox_send_cmd_common_retry(id, cmd, is_indirect, len, arg,
475 urgent, resp_buf_len, resp_buf);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800476}
477
Alif Zakuan Yuslaimia7e1ba32025-03-10 23:38:51 -0700478int mbox_hps_stage_notify(u32 execution_stage)
479{
480#if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_ATF)
481 return smc_send_mailbox(MBOX_HPS_STAGE_NOTIFY, 1, &execution_stage,
482 0, 0, NULL);
483#else
484 return mbox_send_cmd(MBOX_ID_UBOOT, MBOX_HPS_STAGE_NOTIFY,
485 MBOX_CMD_DIRECT, 1, &execution_stage, 0, 0, NULL);
486#endif
487}
488
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800489int mbox_send_cmd_only(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg)
490{
491 return mbox_send_cmd_only_common(id, cmd, is_indirect, len, arg);
492}
493
494int __secure mbox_send_cmd_only_psci(u8 id, u32 cmd, u8 is_indirect, u32 len,
495 u32 *arg)
496{
497 return mbox_send_cmd_only_common(id, cmd, is_indirect, len, arg);
498}
499
500int mbox_rcv_resp(u32 *resp_buf, u32 resp_buf_max_len)
501{
502 return __mbox_rcv_resp(resp_buf, resp_buf_max_len);
503}
504
505int __secure mbox_rcv_resp_psci(u32 *resp_buf, u32 resp_buf_max_len)
506{
507 return __mbox_rcv_resp(resp_buf, resp_buf_max_len);
508}