blob: 429444f06940cf31439732bd567bc4d30c7daf96 [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
7#include <common.h>
Simon Glassf11478f2019-12-28 10:45:07 -07008#include <hang.h>
Ley Foon Tane5b6a662018-05-24 00:17:25 +08009#include <wait_bit.h>
10#include <asm/io.h>
11#include <asm/arch/mailbox_s10.h>
12#include <asm/arch/system_manager.h>
13#include <asm/secure.h>
Chee Hong Ang6aa10582020-12-24 18:21:08 +080014#include <asm/system.h>
Ley Foon Tane5b6a662018-05-24 00:17:25 +080015
16DECLARE_GLOBAL_DATA_PTR;
17
18#define MBOX_READL(reg) \
19 readl(SOCFPGA_MAILBOX_ADDRESS + (reg))
20
21#define MBOX_WRITEL(data, reg) \
22 writel(data, SOCFPGA_MAILBOX_ADDRESS + (reg))
23
24#define MBOX_READ_RESP_BUF(rout) \
25 MBOX_READL(MBOX_RESP_BUF + ((rout) * sizeof(u32)))
26
27#define MBOX_WRITE_CMD_BUF(data, cin) \
28 MBOX_WRITEL(data, MBOX_CMD_BUF + ((cin) * sizeof(u32)))
29
30static __always_inline int mbox_polling_resp(u32 rout)
31{
32 u32 rin;
Chee Hong Ang0cc64c82020-08-12 09:56:21 +080033 unsigned long i = 2000;
Ley Foon Tane5b6a662018-05-24 00:17:25 +080034
35 while (i) {
36 rin = MBOX_READL(MBOX_RIN);
37 if (rout != rin)
38 return 0;
39
Chee Hong Ang0cc64c82020-08-12 09:56:21 +080040 udelay(1000);
Ley Foon Tane5b6a662018-05-24 00:17:25 +080041 i--;
42 }
43
44 return -ETIMEDOUT;
45}
46
Chee Hong Ange4805862020-08-12 09:56:23 +080047static __always_inline int mbox_is_cmdbuf_full(u32 cin)
48{
49 return (((cin + 1) % MBOX_CMD_BUFFER_SIZE) == MBOX_READL(MBOX_COUT));
50}
51
52static __always_inline int mbox_is_cmdbuf_empty(u32 cin)
53{
54 return (((MBOX_READL(MBOX_COUT) + 1) % MBOX_CMD_BUFFER_SIZE) == cin);
55}
56
57static __always_inline int mbox_wait_for_cmdbuf_empty(u32 cin)
58{
59 int timeout = 2000;
60
61 while (timeout) {
62 if (mbox_is_cmdbuf_empty(cin))
63 return 0;
64 udelay(1000);
65 timeout--;
66 }
67
68 return -ETIMEDOUT;
69}
70
71static __always_inline int mbox_write_cmd_buffer(u32 *cin, u32 data,
72 int *is_cmdbuf_overflow)
73{
74 int timeout = 1000;
75
76 while (timeout) {
77 if (mbox_is_cmdbuf_full(*cin)) {
78 if (is_cmdbuf_overflow &&
79 *is_cmdbuf_overflow == 0) {
80 /* Trigger SDM doorbell */
81 MBOX_WRITEL(1, MBOX_DOORBELL_TO_SDM);
82 *is_cmdbuf_overflow = 1;
83 }
84 udelay(1000);
85 } else {
86 /* write header to circular buffer */
87 MBOX_WRITE_CMD_BUF(data, (*cin)++);
88 *cin %= MBOX_CMD_BUFFER_SIZE;
89 MBOX_WRITEL(*cin, MBOX_CIN);
90 break;
91 }
92 timeout--;
93 }
94
95 if (!timeout)
96 return -ETIMEDOUT;
97
98 /* Wait for the SDM to drain the FIFO command buffer */
99 if (is_cmdbuf_overflow && *is_cmdbuf_overflow)
100 return mbox_wait_for_cmdbuf_empty(*cin);
101
102 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
Chee Hong Ange4805862020-08-12 09:56:23 +0800127 /* If SDM doorbell is not triggered after the last data is
128 * written into mailbox FIFO command buffer, trigger the
129 * SDM doorbell again to ensure SDM able to read the remaining
130 * data.
131 */
132 if (!is_cmdbuf_overflow)
133 MBOX_WRITEL(1, MBOX_DOORBELL_TO_SDM);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800134
135 return 0;
136}
137
138/* Check the command and fill it into circular buffer */
139static __always_inline int mbox_prepare_cmd_only(u8 id, u32 cmd,
140 u8 is_indirect, u32 len,
141 u32 *arg)
142{
143 u32 header;
144 int ret;
145
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800146 if (cmd > MBOX_MAX_CMD_INDEX)
147 return -EINVAL;
148
149 header = MBOX_CMD_HEADER(MBOX_CLIENT_ID_UBOOT, id, len,
150 (is_indirect) ? 1 : 0, cmd);
151
152 ret = mbox_fill_cmd_circular_buff(header, len, arg);
153
154 return ret;
155}
156
157/* Send command only without waiting for responses from SDM */
158static __always_inline int mbox_send_cmd_only_common(u8 id, u32 cmd,
159 u8 is_indirect, u32 len,
160 u32 *arg)
161{
Chee Hong Ange4805862020-08-12 09:56:23 +0800162 return mbox_prepare_cmd_only(id, cmd, is_indirect, len, arg);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800163}
164
165/* Return number of responses received in buffer */
166static __always_inline int __mbox_rcv_resp(u32 *resp_buf, u32 resp_buf_max_len)
167{
168 u32 rin;
169 u32 rout;
170 u32 resp_len = 0;
171
172 /* clear doorbell from SDM if it was SET */
173 if (MBOX_READL(MBOX_DOORBELL_FROM_SDM) & 1)
174 MBOX_WRITEL(0, MBOX_DOORBELL_FROM_SDM);
175
176 /* read current response offset */
177 rout = MBOX_READL(MBOX_ROUT);
178 /* read response valid offset */
179 rin = MBOX_READL(MBOX_RIN);
180
181 while (rin != rout && (resp_len < resp_buf_max_len)) {
182 /* Response received */
183 if (resp_buf)
184 resp_buf[resp_len++] = MBOX_READ_RESP_BUF(rout);
185
186 rout++;
187 /* wrapping around when it reach the buffer size */
188 rout %= MBOX_RESP_BUFFER_SIZE;
189 /* update next ROUT */
190 MBOX_WRITEL(rout, MBOX_ROUT);
191 }
192
193 return resp_len;
194}
195
196/* Support one command and up to 31 words argument length only */
197static __always_inline int mbox_send_cmd_common(u8 id, u32 cmd, u8 is_indirect,
198 u32 len, u32 *arg, u8 urgent,
199 u32 *resp_buf_len,
200 u32 *resp_buf)
201{
202 u32 rin;
203 u32 resp;
204 u32 rout;
205 u32 status;
206 u32 resp_len;
207 u32 buf_len;
208 int ret;
209
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800210 if (urgent) {
211 /* Read status because it is toggled */
212 status = MBOX_READL(MBOX_STATUS) & MBOX_STATUS_UA_MSK;
Ley Foon Tanff756cc2018-08-17 16:22:03 +0800213 /* Write urgent command to urgent register */
214 MBOX_WRITEL(cmd, MBOX_URG);
Chee Hong Ange4805862020-08-12 09:56:23 +0800215 /* write doorbell */
216 MBOX_WRITEL(1, MBOX_DOORBELL_TO_SDM);
Ley Foon Tanff756cc2018-08-17 16:22:03 +0800217 } else {
218 ret = mbox_prepare_cmd_only(id, cmd, is_indirect, len, arg);
219 if (ret)
220 return ret;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800221 }
222
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800223 while (1) {
Chee Hong Ang0cc64c82020-08-12 09:56:21 +0800224 ret = 1000;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800225
226 /* Wait for doorbell from SDM */
Chee Hong Ang0cc64c82020-08-12 09:56:21 +0800227 do {
228 if (MBOX_READL(MBOX_DOORBELL_FROM_SDM))
229 break;
230 udelay(1000);
231 } while (--ret);
232
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800233 if (!ret)
234 return -ETIMEDOUT;
235
236 /* clear interrupt */
237 MBOX_WRITEL(0, MBOX_DOORBELL_FROM_SDM);
238
239 if (urgent) {
240 u32 new_status = MBOX_READL(MBOX_STATUS);
Ley Foon Tanff756cc2018-08-17 16:22:03 +0800241
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800242 /* Urgent ACK is toggled */
243 if ((new_status & MBOX_STATUS_UA_MSK) ^ status)
244 return 0;
245
246 return -ECOMM;
247 }
248
249 /* read current response offset */
250 rout = MBOX_READL(MBOX_ROUT);
251
252 /* read response valid offset */
253 rin = MBOX_READL(MBOX_RIN);
254
255 if (rout != rin) {
256 /* Response received */
257 resp = MBOX_READ_RESP_BUF(rout);
258 rout++;
259 /* wrapping around when it reach the buffer size */
260 rout %= MBOX_RESP_BUFFER_SIZE;
261 /* update next ROUT */
262 MBOX_WRITEL(rout, MBOX_ROUT);
263
264 /* check client ID and ID */
265 if ((MBOX_RESP_CLIENT_GET(resp) ==
266 MBOX_CLIENT_ID_UBOOT) &&
267 (MBOX_RESP_ID_GET(resp) == id)) {
Chee Hong Angfe33ea32020-08-12 09:56:22 +0800268 int resp_err = MBOX_RESP_ERR_GET(resp);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800269
270 if (resp_buf_len) {
271 buf_len = *resp_buf_len;
272 *resp_buf_len = 0;
273 } else {
274 buf_len = 0;
275 }
276
277 resp_len = MBOX_RESP_LEN_GET(resp);
278 while (resp_len) {
279 ret = mbox_polling_resp(rout);
280 if (ret)
281 return ret;
282 /* we need to process response buffer
283 * even caller doesn't need it
284 */
285 resp = MBOX_READ_RESP_BUF(rout);
286 rout++;
287 resp_len--;
288 rout %= MBOX_RESP_BUFFER_SIZE;
289 MBOX_WRITEL(rout, MBOX_ROUT);
290 if (buf_len) {
291 /* copy response to buffer */
292 resp_buf[*resp_buf_len] = resp;
293 (*resp_buf_len)++;
294 buf_len--;
295 }
296 }
Chee Hong Angfe33ea32020-08-12 09:56:22 +0800297 return resp_err;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800298 }
299 }
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800300 }
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800301
302 return -EIO;
303}
304
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800305static __always_inline int mbox_send_cmd_common_retry(u8 id, u32 cmd,
306 u8 is_indirect,
307 u32 len, u32 *arg,
308 u8 urgent,
309 u32 *resp_buf_len,
310 u32 *resp_buf)
311{
312 int ret;
313 int i;
314
315 for (i = 0; i < 3; i++) {
316 ret = mbox_send_cmd_common(id, cmd, is_indirect, len, arg,
317 urgent, resp_buf_len, resp_buf);
318 if (ret == MBOX_RESP_TIMEOUT || ret == MBOX_RESP_DEVICE_BUSY)
319 udelay(2000); /* wait for 2ms before resend */
320 else
321 break;
322 }
323
324 return ret;
325}
326
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800327int mbox_init(void)
328{
329 int ret;
330
331 /* enable mailbox interrupts */
332 MBOX_WRITEL(MBOX_ALL_INTRS, MBOX_FLAGS);
333
334 /* Ensure urgent request is cleared */
335 MBOX_WRITEL(0, MBOX_URG);
336
337 /* Ensure the Doorbell Interrupt is cleared */
338 MBOX_WRITEL(0, MBOX_DOORBELL_FROM_SDM);
339
340 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_RESTART, MBOX_CMD_DIRECT, 0,
341 NULL, 1, 0, NULL);
342 if (ret)
343 return ret;
344
345 /* Renable mailbox interrupts after MBOX_RESTART */
346 MBOX_WRITEL(MBOX_ALL_INTRS, MBOX_FLAGS);
347
348 return 0;
349}
350
351#ifdef CONFIG_CADENCE_QSPI
352int mbox_qspi_close(void)
353{
354 return mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_CLOSE, MBOX_CMD_DIRECT,
355 0, NULL, 0, 0, NULL);
356}
357
358int mbox_qspi_open(void)
359{
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800360 int ret;
361 u32 resp_buf[1];
362 u32 resp_buf_len;
363
364 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_OPEN, MBOX_CMD_DIRECT,
365 0, NULL, 0, 0, NULL);
366 if (ret) {
367 /* retry again by closing and reopen the QSPI again */
368 ret = mbox_qspi_close();
369 if (ret)
370 return ret;
371
372 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_OPEN,
373 MBOX_CMD_DIRECT, 0, NULL, 0, 0, NULL);
374 if (ret)
375 return ret;
376 }
377
378 /* HPS will directly control the QSPI controller, no longer mailbox */
379 resp_buf_len = 1;
380 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_DIRECT, MBOX_CMD_DIRECT,
381 0, NULL, 0, (u32 *)&resp_buf_len,
382 (u32 *)&resp_buf);
383 if (ret)
384 goto error;
385
386 /* We are getting QSPI ref clock and set into sysmgr boot register */
387 printf("QSPI: Reference clock at %d Hz\n", resp_buf[0]);
Ley Foon Tan3d3a8602019-11-08 10:38:20 +0800388 writel(resp_buf[0],
Ley Foon Tan0b1680e2019-11-27 15:55:18 +0800389 socfpga_get_sysmgr_addr() + SYSMGR_SOC64_BOOT_SCRATCH_COLD0);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800390
391 return 0;
392
393error:
394 mbox_qspi_close();
395
396 return ret;
397}
398#endif /* CONFIG_CADENCE_QSPI */
399
400int mbox_reset_cold(void)
401{
Chee Hong Ang6aa10582020-12-24 18:21:08 +0800402#if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_ATF)
403 psci_system_reset();
404#else
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800405 int ret;
406
407 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_REBOOT_HPS, MBOX_CMD_DIRECT,
408 0, NULL, 0, 0, NULL);
409 if (ret) {
410 /* mailbox sent failure, wait for watchdog to kick in */
411 hang();
412 }
Chee Hong Ang6aa10582020-12-24 18:21:08 +0800413#endif
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800414 return 0;
415}
416
Ang, Chee Hong31b79632018-12-19 18:35:12 -0800417/* Accepted commands: CONFIG_STATUS or RECONFIG_STATUS */
418static __always_inline int mbox_get_fpga_config_status_common(u32 cmd)
419{
420 u32 reconfig_status_resp_len;
421 u32 reconfig_status_resp[RECONFIG_STATUS_RESPONSE_LEN];
422 int ret;
423
424 reconfig_status_resp_len = RECONFIG_STATUS_RESPONSE_LEN;
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800425 ret = mbox_send_cmd_common_retry(MBOX_ID_UBOOT, cmd,
426 MBOX_CMD_DIRECT, 0, NULL, 0,
427 &reconfig_status_resp_len,
428 reconfig_status_resp);
Ang, Chee Hong31b79632018-12-19 18:35:12 -0800429
430 if (ret)
431 return ret;
432
433 /* Check for any error */
434 ret = reconfig_status_resp[RECONFIG_STATUS_STATE];
435 if (ret && ret != MBOX_CFGSTAT_STATE_CONFIG)
436 return ret;
437
438 /* Make sure nStatus is not 0 */
439 ret = reconfig_status_resp[RECONFIG_STATUS_PIN_STATUS];
440 if (!(ret & RCF_PIN_STATUS_NSTATUS))
441 return MBOX_CFGSTAT_STATE_ERROR_HARDWARE;
442
443 ret = reconfig_status_resp[RECONFIG_STATUS_SOFTFUNC_STATUS];
444 if (ret & RCF_SOFTFUNC_STATUS_SEU_ERROR)
445 return MBOX_CFGSTAT_STATE_ERROR_HARDWARE;
446
447 if ((ret & RCF_SOFTFUNC_STATUS_CONF_DONE) &&
448 (ret & RCF_SOFTFUNC_STATUS_INIT_DONE) &&
449 !reconfig_status_resp[RECONFIG_STATUS_STATE])
450 return 0; /* configuration success */
451
452 return MBOX_CFGSTAT_STATE_CONFIG;
453}
454
455int mbox_get_fpga_config_status(u32 cmd)
456{
457 return mbox_get_fpga_config_status_common(cmd);
458}
459
460int __secure mbox_get_fpga_config_status_psci(u32 cmd)
461{
462 return mbox_get_fpga_config_status_common(cmd);
463}
464
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800465int mbox_send_cmd(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg,
466 u8 urgent, u32 *resp_buf_len, u32 *resp_buf)
467{
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800468 return mbox_send_cmd_common_retry(id, cmd, is_indirect, len, arg,
469 urgent, resp_buf_len, resp_buf);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800470}
471
472int __secure mbox_send_cmd_psci(u8 id, u32 cmd, u8 is_indirect, u32 len,
473 u32 *arg, u8 urgent, u32 *resp_buf_len,
474 u32 *resp_buf)
475{
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800476 return mbox_send_cmd_common_retry(id, cmd, is_indirect, len, arg,
477 urgent, resp_buf_len, resp_buf);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800478}
479
480int mbox_send_cmd_only(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg)
481{
482 return mbox_send_cmd_only_common(id, cmd, is_indirect, len, arg);
483}
484
485int __secure mbox_send_cmd_only_psci(u8 id, u32 cmd, u8 is_indirect, u32 len,
486 u32 *arg)
487{
488 return mbox_send_cmd_only_common(id, cmd, is_indirect, len, arg);
489}
490
491int mbox_rcv_resp(u32 *resp_buf, u32 resp_buf_max_len)
492{
493 return __mbox_rcv_resp(resp_buf, resp_buf_max_len);
494}
495
496int __secure mbox_rcv_resp_psci(u32 *resp_buf, u32 resp_buf_max_len)
497{
498 return __mbox_rcv_resp(resp_buf, resp_buf_max_len);
499}