blob: b69bd3e47ec26e7dcf11344689759705c8f58bf2 [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>
9#include <asm/arch/system_manager.h>
Siew Chin Limc1888b02021-03-24 17:16:50 +080010#include <asm/global_data.h>
11#include <asm/io.h>
Ley Foon Tane5b6a662018-05-24 00:17:25 +080012#include <asm/secure.h>
Chee Hong Ang6aa10582020-12-24 18:21:08 +080013#include <asm/system.h>
Siew Chin Limc1888b02021-03-24 17:16:50 +080014#include <hang.h>
15#include <wait_bit.h>
Ley Foon Tane5b6a662018-05-24 00:17:25 +080016
17DECLARE_GLOBAL_DATA_PTR;
18
19#define MBOX_READL(reg) \
20 readl(SOCFPGA_MAILBOX_ADDRESS + (reg))
21
22#define MBOX_WRITEL(data, reg) \
23 writel(data, SOCFPGA_MAILBOX_ADDRESS + (reg))
24
25#define MBOX_READ_RESP_BUF(rout) \
26 MBOX_READL(MBOX_RESP_BUF + ((rout) * sizeof(u32)))
27
28#define MBOX_WRITE_CMD_BUF(data, cin) \
29 MBOX_WRITEL(data, MBOX_CMD_BUF + ((cin) * sizeof(u32)))
30
31static __always_inline int mbox_polling_resp(u32 rout)
32{
33 u32 rin;
Chee Hong Ang0cc64c82020-08-12 09:56:21 +080034 unsigned long i = 2000;
Ley Foon Tane5b6a662018-05-24 00:17:25 +080035
36 while (i) {
37 rin = MBOX_READL(MBOX_RIN);
38 if (rout != rin)
39 return 0;
40
Chee Hong Ang0cc64c82020-08-12 09:56:21 +080041 udelay(1000);
Ley Foon Tane5b6a662018-05-24 00:17:25 +080042 i--;
43 }
44
45 return -ETIMEDOUT;
46}
47
Chee Hong Ange4805862020-08-12 09:56:23 +080048static __always_inline int mbox_is_cmdbuf_full(u32 cin)
49{
50 return (((cin + 1) % MBOX_CMD_BUFFER_SIZE) == MBOX_READL(MBOX_COUT));
51}
52
53static __always_inline int mbox_is_cmdbuf_empty(u32 cin)
54{
55 return (((MBOX_READL(MBOX_COUT) + 1) % MBOX_CMD_BUFFER_SIZE) == cin);
56}
57
58static __always_inline int mbox_wait_for_cmdbuf_empty(u32 cin)
59{
60 int timeout = 2000;
61
62 while (timeout) {
63 if (mbox_is_cmdbuf_empty(cin))
64 return 0;
65 udelay(1000);
66 timeout--;
67 }
68
69 return -ETIMEDOUT;
70}
71
72static __always_inline int mbox_write_cmd_buffer(u32 *cin, u32 data,
73 int *is_cmdbuf_overflow)
74{
75 int timeout = 1000;
76
77 while (timeout) {
78 if (mbox_is_cmdbuf_full(*cin)) {
79 if (is_cmdbuf_overflow &&
80 *is_cmdbuf_overflow == 0) {
81 /* Trigger SDM doorbell */
82 MBOX_WRITEL(1, MBOX_DOORBELL_TO_SDM);
83 *is_cmdbuf_overflow = 1;
84 }
85 udelay(1000);
86 } else {
87 /* write header to circular buffer */
88 MBOX_WRITE_CMD_BUF(data, (*cin)++);
89 *cin %= MBOX_CMD_BUFFER_SIZE;
90 MBOX_WRITEL(*cin, MBOX_CIN);
Alif Zakuan Yuslaimi39c68c52025-01-22 10:21:02 +080091 if (is_cmdbuf_overflow)
92 *is_cmdbuf_overflow = 0;
Chee Hong Ange4805862020-08-12 09:56:23 +080093 break;
94 }
95 timeout--;
96 }
97
98 if (!timeout)
99 return -ETIMEDOUT;
100
Chee Hong Ange4805862020-08-12 09:56:23 +0800101 return 0;
102}
103
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800104/* Check for available slot and write to circular buffer.
105 * It also update command valid offset (cin) register.
106 */
107static __always_inline int mbox_fill_cmd_circular_buff(u32 header, u32 len,
108 u32 *arg)
109{
Chee Hong Ange4805862020-08-12 09:56:23 +0800110 int i, ret;
111 int is_cmdbuf_overflow = 0;
112 u32 cin = MBOX_READL(MBOX_CIN) % MBOX_CMD_BUFFER_SIZE;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800113
Chee Hong Ange4805862020-08-12 09:56:23 +0800114 ret = mbox_write_cmd_buffer(&cin, header, &is_cmdbuf_overflow);
115 if (ret)
116 return ret;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800117
118 /* write arguments */
119 for (i = 0; i < len; i++) {
Chee Hong Ange4805862020-08-12 09:56:23 +0800120 is_cmdbuf_overflow = 0;
121 ret = mbox_write_cmd_buffer(&cin, arg[i], &is_cmdbuf_overflow);
122 if (ret)
123 return ret;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800124 }
125
Alif Zakuan Yuslaimi39c68c52025-01-22 10:21:02 +0800126 /*
127 * Always trigger the SDM doorbell at the end to ensure SDM able to read
128 * the remaining data.
Chee Hong Ange4805862020-08-12 09:56:23 +0800129 */
Alif Zakuan Yuslaimi39c68c52025-01-22 10:21:02 +0800130 MBOX_WRITEL(1, MBOX_DOORBELL_TO_SDM);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800131
132 return 0;
133}
134
135/* Check the command and fill it into circular buffer */
136static __always_inline int mbox_prepare_cmd_only(u8 id, u32 cmd,
137 u8 is_indirect, u32 len,
138 u32 *arg)
139{
140 u32 header;
141 int ret;
142
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800143 if (cmd > MBOX_MAX_CMD_INDEX)
144 return -EINVAL;
145
146 header = MBOX_CMD_HEADER(MBOX_CLIENT_ID_UBOOT, id, len,
147 (is_indirect) ? 1 : 0, cmd);
148
149 ret = mbox_fill_cmd_circular_buff(header, len, arg);
150
151 return ret;
152}
153
154/* Send command only without waiting for responses from SDM */
155static __always_inline int mbox_send_cmd_only_common(u8 id, u32 cmd,
156 u8 is_indirect, u32 len,
157 u32 *arg)
158{
Chee Hong Ange4805862020-08-12 09:56:23 +0800159 return mbox_prepare_cmd_only(id, cmd, is_indirect, len, arg);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800160}
161
162/* Return number of responses received in buffer */
163static __always_inline int __mbox_rcv_resp(u32 *resp_buf, u32 resp_buf_max_len)
164{
165 u32 rin;
166 u32 rout;
167 u32 resp_len = 0;
168
169 /* clear doorbell from SDM if it was SET */
170 if (MBOX_READL(MBOX_DOORBELL_FROM_SDM) & 1)
171 MBOX_WRITEL(0, MBOX_DOORBELL_FROM_SDM);
172
173 /* read current response offset */
174 rout = MBOX_READL(MBOX_ROUT);
175 /* read response valid offset */
176 rin = MBOX_READL(MBOX_RIN);
177
178 while (rin != rout && (resp_len < resp_buf_max_len)) {
179 /* Response received */
180 if (resp_buf)
181 resp_buf[resp_len++] = MBOX_READ_RESP_BUF(rout);
182
183 rout++;
184 /* wrapping around when it reach the buffer size */
185 rout %= MBOX_RESP_BUFFER_SIZE;
186 /* update next ROUT */
187 MBOX_WRITEL(rout, MBOX_ROUT);
188 }
189
190 return resp_len;
191}
192
193/* Support one command and up to 31 words argument length only */
194static __always_inline int mbox_send_cmd_common(u8 id, u32 cmd, u8 is_indirect,
195 u32 len, u32 *arg, u8 urgent,
196 u32 *resp_buf_len,
197 u32 *resp_buf)
198{
199 u32 rin;
200 u32 resp;
201 u32 rout;
202 u32 status;
203 u32 resp_len;
204 u32 buf_len;
205 int ret;
206
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800207 if (urgent) {
208 /* Read status because it is toggled */
209 status = MBOX_READL(MBOX_STATUS) & MBOX_STATUS_UA_MSK;
Ley Foon Tanff756cc2018-08-17 16:22:03 +0800210 /* Write urgent command to urgent register */
211 MBOX_WRITEL(cmd, MBOX_URG);
Chee Hong Ange4805862020-08-12 09:56:23 +0800212 /* write doorbell */
213 MBOX_WRITEL(1, MBOX_DOORBELL_TO_SDM);
Ley Foon Tanff756cc2018-08-17 16:22:03 +0800214 } else {
215 ret = mbox_prepare_cmd_only(id, cmd, is_indirect, len, arg);
216 if (ret)
217 return ret;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800218 }
219
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800220 while (1) {
Chee Hong Ang0cc64c82020-08-12 09:56:21 +0800221 ret = 1000;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800222
223 /* Wait for doorbell from SDM */
Chee Hong Ang0cc64c82020-08-12 09:56:21 +0800224 do {
225 if (MBOX_READL(MBOX_DOORBELL_FROM_SDM))
226 break;
227 udelay(1000);
228 } while (--ret);
229
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800230 if (!ret)
231 return -ETIMEDOUT;
232
233 /* clear interrupt */
234 MBOX_WRITEL(0, MBOX_DOORBELL_FROM_SDM);
235
236 if (urgent) {
237 u32 new_status = MBOX_READL(MBOX_STATUS);
Ley Foon Tanff756cc2018-08-17 16:22:03 +0800238
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800239 /* Urgent ACK is toggled */
240 if ((new_status & MBOX_STATUS_UA_MSK) ^ status)
241 return 0;
242
243 return -ECOMM;
244 }
245
246 /* read current response offset */
247 rout = MBOX_READL(MBOX_ROUT);
248
249 /* read response valid offset */
250 rin = MBOX_READL(MBOX_RIN);
251
252 if (rout != rin) {
253 /* Response received */
254 resp = MBOX_READ_RESP_BUF(rout);
255 rout++;
256 /* wrapping around when it reach the buffer size */
257 rout %= MBOX_RESP_BUFFER_SIZE;
258 /* update next ROUT */
259 MBOX_WRITEL(rout, MBOX_ROUT);
260
261 /* check client ID and ID */
262 if ((MBOX_RESP_CLIENT_GET(resp) ==
263 MBOX_CLIENT_ID_UBOOT) &&
264 (MBOX_RESP_ID_GET(resp) == id)) {
Chee Hong Angfe33ea32020-08-12 09:56:22 +0800265 int resp_err = MBOX_RESP_ERR_GET(resp);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800266
267 if (resp_buf_len) {
268 buf_len = *resp_buf_len;
269 *resp_buf_len = 0;
270 } else {
271 buf_len = 0;
272 }
273
274 resp_len = MBOX_RESP_LEN_GET(resp);
275 while (resp_len) {
276 ret = mbox_polling_resp(rout);
277 if (ret)
278 return ret;
279 /* we need to process response buffer
280 * even caller doesn't need it
281 */
282 resp = MBOX_READ_RESP_BUF(rout);
283 rout++;
284 resp_len--;
285 rout %= MBOX_RESP_BUFFER_SIZE;
286 MBOX_WRITEL(rout, MBOX_ROUT);
287 if (buf_len) {
288 /* copy response to buffer */
289 resp_buf[*resp_buf_len] = resp;
290 (*resp_buf_len)++;
291 buf_len--;
292 }
293 }
Chee Hong Angfe33ea32020-08-12 09:56:22 +0800294 return resp_err;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800295 }
296 }
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800297 }
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800298
299 return -EIO;
300}
301
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800302static __always_inline int mbox_send_cmd_common_retry(u8 id, u32 cmd,
303 u8 is_indirect,
304 u32 len, u32 *arg,
305 u8 urgent,
306 u32 *resp_buf_len,
307 u32 *resp_buf)
308{
309 int ret;
310 int i;
311
312 for (i = 0; i < 3; i++) {
313 ret = mbox_send_cmd_common(id, cmd, is_indirect, len, arg,
314 urgent, resp_buf_len, resp_buf);
315 if (ret == MBOX_RESP_TIMEOUT || ret == MBOX_RESP_DEVICE_BUSY)
316 udelay(2000); /* wait for 2ms before resend */
317 else
318 break;
319 }
320
321 return ret;
322}
323
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800324int mbox_init(void)
325{
326 int ret;
327
328 /* enable mailbox interrupts */
329 MBOX_WRITEL(MBOX_ALL_INTRS, MBOX_FLAGS);
330
331 /* Ensure urgent request is cleared */
332 MBOX_WRITEL(0, MBOX_URG);
333
334 /* Ensure the Doorbell Interrupt is cleared */
335 MBOX_WRITEL(0, MBOX_DOORBELL_FROM_SDM);
336
337 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_RESTART, MBOX_CMD_DIRECT, 0,
338 NULL, 1, 0, NULL);
339 if (ret)
340 return ret;
341
342 /* Renable mailbox interrupts after MBOX_RESTART */
343 MBOX_WRITEL(MBOX_ALL_INTRS, MBOX_FLAGS);
344
345 return 0;
346}
347
348#ifdef CONFIG_CADENCE_QSPI
349int mbox_qspi_close(void)
350{
351 return mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_CLOSE, MBOX_CMD_DIRECT,
352 0, NULL, 0, 0, NULL);
353}
354
355int mbox_qspi_open(void)
356{
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800357 int ret;
358 u32 resp_buf[1];
359 u32 resp_buf_len;
360
361 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_OPEN, MBOX_CMD_DIRECT,
362 0, NULL, 0, 0, NULL);
363 if (ret) {
364 /* retry again by closing and reopen the QSPI again */
365 ret = mbox_qspi_close();
366 if (ret)
367 return ret;
368
369 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_OPEN,
370 MBOX_CMD_DIRECT, 0, NULL, 0, 0, NULL);
371 if (ret)
372 return ret;
373 }
374
375 /* HPS will directly control the QSPI controller, no longer mailbox */
376 resp_buf_len = 1;
377 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_DIRECT, MBOX_CMD_DIRECT,
378 0, NULL, 0, (u32 *)&resp_buf_len,
379 (u32 *)&resp_buf);
380 if (ret)
381 goto error;
382
Siew Chin Limc1888b02021-03-24 17:16:50 +0800383 /* Store QSPI controller ref clock frequency */
384 ret = cm_set_qspi_controller_clk_hz(resp_buf[0]);
385 if (ret)
386 goto error;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800387
388 return 0;
389
390error:
391 mbox_qspi_close();
392
393 return ret;
394}
395#endif /* CONFIG_CADENCE_QSPI */
396
397int mbox_reset_cold(void)
398{
Simon Glass85ed77d2024-09-29 19:49:46 -0600399#if !defined(CONFIG_XPL_BUILD) && defined(CONFIG_SPL_ATF)
Chee Hong Ang6aa10582020-12-24 18:21:08 +0800400 psci_system_reset();
401#else
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800402 int ret;
403
404 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_REBOOT_HPS, MBOX_CMD_DIRECT,
405 0, NULL, 0, 0, NULL);
406 if (ret) {
407 /* mailbox sent failure, wait for watchdog to kick in */
408 hang();
409 }
Chee Hong Ang6aa10582020-12-24 18:21:08 +0800410#endif
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800411 return 0;
412}
413
Ang, Chee Hong31b79632018-12-19 18:35:12 -0800414/* Accepted commands: CONFIG_STATUS or RECONFIG_STATUS */
415static __always_inline int mbox_get_fpga_config_status_common(u32 cmd)
416{
417 u32 reconfig_status_resp_len;
418 u32 reconfig_status_resp[RECONFIG_STATUS_RESPONSE_LEN];
419 int ret;
420
421 reconfig_status_resp_len = RECONFIG_STATUS_RESPONSE_LEN;
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800422 ret = mbox_send_cmd_common_retry(MBOX_ID_UBOOT, cmd,
423 MBOX_CMD_DIRECT, 0, NULL, 0,
424 &reconfig_status_resp_len,
425 reconfig_status_resp);
Ang, Chee Hong31b79632018-12-19 18:35:12 -0800426
427 if (ret)
428 return ret;
429
430 /* Check for any error */
431 ret = reconfig_status_resp[RECONFIG_STATUS_STATE];
432 if (ret && ret != MBOX_CFGSTAT_STATE_CONFIG)
433 return ret;
434
435 /* Make sure nStatus is not 0 */
436 ret = reconfig_status_resp[RECONFIG_STATUS_PIN_STATUS];
437 if (!(ret & RCF_PIN_STATUS_NSTATUS))
438 return MBOX_CFGSTAT_STATE_ERROR_HARDWARE;
439
440 ret = reconfig_status_resp[RECONFIG_STATUS_SOFTFUNC_STATUS];
441 if (ret & RCF_SOFTFUNC_STATUS_SEU_ERROR)
442 return MBOX_CFGSTAT_STATE_ERROR_HARDWARE;
443
444 if ((ret & RCF_SOFTFUNC_STATUS_CONF_DONE) &&
445 (ret & RCF_SOFTFUNC_STATUS_INIT_DONE) &&
446 !reconfig_status_resp[RECONFIG_STATUS_STATE])
447 return 0; /* configuration success */
448
449 return MBOX_CFGSTAT_STATE_CONFIG;
450}
451
452int mbox_get_fpga_config_status(u32 cmd)
453{
454 return mbox_get_fpga_config_status_common(cmd);
455}
456
457int __secure mbox_get_fpga_config_status_psci(u32 cmd)
458{
459 return mbox_get_fpga_config_status_common(cmd);
460}
461
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800462int mbox_send_cmd(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg,
463 u8 urgent, u32 *resp_buf_len, u32 *resp_buf)
464{
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800465 return mbox_send_cmd_common_retry(id, cmd, is_indirect, len, arg,
466 urgent, resp_buf_len, resp_buf);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800467}
468
469int __secure mbox_send_cmd_psci(u8 id, u32 cmd, u8 is_indirect, u32 len,
470 u32 *arg, u8 urgent, u32 *resp_buf_len,
471 u32 *resp_buf)
472{
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800473 return mbox_send_cmd_common_retry(id, cmd, is_indirect, len, arg,
474 urgent, resp_buf_len, resp_buf);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800475}
476
477int mbox_send_cmd_only(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg)
478{
479 return mbox_send_cmd_only_common(id, cmd, is_indirect, len, arg);
480}
481
482int __secure mbox_send_cmd_only_psci(u8 id, u32 cmd, u8 is_indirect, u32 len,
483 u32 *arg)
484{
485 return mbox_send_cmd_only_common(id, cmd, is_indirect, len, arg);
486}
487
488int mbox_rcv_resp(u32 *resp_buf, u32 resp_buf_max_len)
489{
490 return __mbox_rcv_resp(resp_buf, resp_buf_max_len);
491}
492
493int __secure mbox_rcv_resp_psci(u32 *resp_buf, u32 resp_buf_max_len)
494{
495 return __mbox_rcv_resp(resp_buf, resp_buf_max_len);
496}