blob: 101af2385529c38c14b1376f2d95bd5b8f210366 [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>
Siew Chin Limc1888b02021-03-24 17:16:50 +08008#include <asm/arch/clock_manager.h>
Ley Foon Tane5b6a662018-05-24 00:17:25 +08009#include <asm/arch/mailbox_s10.h>
10#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);
92 break;
93 }
94 timeout--;
95 }
96
97 if (!timeout)
98 return -ETIMEDOUT;
99
100 /* Wait for the SDM to drain the FIFO command buffer */
101 if (is_cmdbuf_overflow && *is_cmdbuf_overflow)
102 return mbox_wait_for_cmdbuf_empty(*cin);
103
104 return 0;
105}
106
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800107/* Check for available slot and write to circular buffer.
108 * It also update command valid offset (cin) register.
109 */
110static __always_inline int mbox_fill_cmd_circular_buff(u32 header, u32 len,
111 u32 *arg)
112{
Chee Hong Ange4805862020-08-12 09:56:23 +0800113 int i, ret;
114 int is_cmdbuf_overflow = 0;
115 u32 cin = MBOX_READL(MBOX_CIN) % MBOX_CMD_BUFFER_SIZE;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800116
Chee Hong Ange4805862020-08-12 09:56:23 +0800117 ret = mbox_write_cmd_buffer(&cin, header, &is_cmdbuf_overflow);
118 if (ret)
119 return ret;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800120
121 /* write arguments */
122 for (i = 0; i < len; i++) {
Chee Hong Ange4805862020-08-12 09:56:23 +0800123 is_cmdbuf_overflow = 0;
124 ret = mbox_write_cmd_buffer(&cin, arg[i], &is_cmdbuf_overflow);
125 if (ret)
126 return ret;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800127 }
128
Chee Hong Ange4805862020-08-12 09:56:23 +0800129 /* If SDM doorbell is not triggered after the last data is
130 * written into mailbox FIFO command buffer, trigger the
131 * SDM doorbell again to ensure SDM able to read the remaining
132 * data.
133 */
134 if (!is_cmdbuf_overflow)
135 MBOX_WRITEL(1, MBOX_DOORBELL_TO_SDM);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800136
137 return 0;
138}
139
140/* Check the command and fill it into circular buffer */
141static __always_inline int mbox_prepare_cmd_only(u8 id, u32 cmd,
142 u8 is_indirect, u32 len,
143 u32 *arg)
144{
145 u32 header;
146 int ret;
147
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800148 if (cmd > MBOX_MAX_CMD_INDEX)
149 return -EINVAL;
150
151 header = MBOX_CMD_HEADER(MBOX_CLIENT_ID_UBOOT, id, len,
152 (is_indirect) ? 1 : 0, cmd);
153
154 ret = mbox_fill_cmd_circular_buff(header, len, arg);
155
156 return ret;
157}
158
159/* Send command only without waiting for responses from SDM */
160static __always_inline int mbox_send_cmd_only_common(u8 id, u32 cmd,
161 u8 is_indirect, u32 len,
162 u32 *arg)
163{
Chee Hong Ange4805862020-08-12 09:56:23 +0800164 return mbox_prepare_cmd_only(id, cmd, is_indirect, len, arg);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800165}
166
167/* Return number of responses received in buffer */
168static __always_inline int __mbox_rcv_resp(u32 *resp_buf, u32 resp_buf_max_len)
169{
170 u32 rin;
171 u32 rout;
172 u32 resp_len = 0;
173
174 /* clear doorbell from SDM if it was SET */
175 if (MBOX_READL(MBOX_DOORBELL_FROM_SDM) & 1)
176 MBOX_WRITEL(0, MBOX_DOORBELL_FROM_SDM);
177
178 /* read current response offset */
179 rout = MBOX_READL(MBOX_ROUT);
180 /* read response valid offset */
181 rin = MBOX_READL(MBOX_RIN);
182
183 while (rin != rout && (resp_len < resp_buf_max_len)) {
184 /* Response received */
185 if (resp_buf)
186 resp_buf[resp_len++] = MBOX_READ_RESP_BUF(rout);
187
188 rout++;
189 /* wrapping around when it reach the buffer size */
190 rout %= MBOX_RESP_BUFFER_SIZE;
191 /* update next ROUT */
192 MBOX_WRITEL(rout, MBOX_ROUT);
193 }
194
195 return resp_len;
196}
197
198/* Support one command and up to 31 words argument length only */
199static __always_inline int mbox_send_cmd_common(u8 id, u32 cmd, u8 is_indirect,
200 u32 len, u32 *arg, u8 urgent,
201 u32 *resp_buf_len,
202 u32 *resp_buf)
203{
204 u32 rin;
205 u32 resp;
206 u32 rout;
207 u32 status;
208 u32 resp_len;
209 u32 buf_len;
210 int ret;
211
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800212 if (urgent) {
213 /* Read status because it is toggled */
214 status = MBOX_READL(MBOX_STATUS) & MBOX_STATUS_UA_MSK;
Ley Foon Tanff756cc2018-08-17 16:22:03 +0800215 /* Write urgent command to urgent register */
216 MBOX_WRITEL(cmd, MBOX_URG);
Chee Hong Ange4805862020-08-12 09:56:23 +0800217 /* write doorbell */
218 MBOX_WRITEL(1, MBOX_DOORBELL_TO_SDM);
Ley Foon Tanff756cc2018-08-17 16:22:03 +0800219 } else {
220 ret = mbox_prepare_cmd_only(id, cmd, is_indirect, len, arg);
221 if (ret)
222 return ret;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800223 }
224
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800225 while (1) {
Chee Hong Ang0cc64c82020-08-12 09:56:21 +0800226 ret = 1000;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800227
228 /* Wait for doorbell from SDM */
Chee Hong Ang0cc64c82020-08-12 09:56:21 +0800229 do {
230 if (MBOX_READL(MBOX_DOORBELL_FROM_SDM))
231 break;
232 udelay(1000);
233 } while (--ret);
234
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800235 if (!ret)
236 return -ETIMEDOUT;
237
238 /* clear interrupt */
239 MBOX_WRITEL(0, MBOX_DOORBELL_FROM_SDM);
240
241 if (urgent) {
242 u32 new_status = MBOX_READL(MBOX_STATUS);
Ley Foon Tanff756cc2018-08-17 16:22:03 +0800243
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800244 /* Urgent ACK is toggled */
245 if ((new_status & MBOX_STATUS_UA_MSK) ^ status)
246 return 0;
247
248 return -ECOMM;
249 }
250
251 /* read current response offset */
252 rout = MBOX_READL(MBOX_ROUT);
253
254 /* read response valid offset */
255 rin = MBOX_READL(MBOX_RIN);
256
257 if (rout != rin) {
258 /* Response received */
259 resp = MBOX_READ_RESP_BUF(rout);
260 rout++;
261 /* wrapping around when it reach the buffer size */
262 rout %= MBOX_RESP_BUFFER_SIZE;
263 /* update next ROUT */
264 MBOX_WRITEL(rout, MBOX_ROUT);
265
266 /* check client ID and ID */
267 if ((MBOX_RESP_CLIENT_GET(resp) ==
268 MBOX_CLIENT_ID_UBOOT) &&
269 (MBOX_RESP_ID_GET(resp) == id)) {
Chee Hong Angfe33ea32020-08-12 09:56:22 +0800270 int resp_err = MBOX_RESP_ERR_GET(resp);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800271
272 if (resp_buf_len) {
273 buf_len = *resp_buf_len;
274 *resp_buf_len = 0;
275 } else {
276 buf_len = 0;
277 }
278
279 resp_len = MBOX_RESP_LEN_GET(resp);
280 while (resp_len) {
281 ret = mbox_polling_resp(rout);
282 if (ret)
283 return ret;
284 /* we need to process response buffer
285 * even caller doesn't need it
286 */
287 resp = MBOX_READ_RESP_BUF(rout);
288 rout++;
289 resp_len--;
290 rout %= MBOX_RESP_BUFFER_SIZE;
291 MBOX_WRITEL(rout, MBOX_ROUT);
292 if (buf_len) {
293 /* copy response to buffer */
294 resp_buf[*resp_buf_len] = resp;
295 (*resp_buf_len)++;
296 buf_len--;
297 }
298 }
Chee Hong Angfe33ea32020-08-12 09:56:22 +0800299 return resp_err;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800300 }
301 }
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800302 }
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800303
304 return -EIO;
305}
306
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800307static __always_inline int mbox_send_cmd_common_retry(u8 id, u32 cmd,
308 u8 is_indirect,
309 u32 len, u32 *arg,
310 u8 urgent,
311 u32 *resp_buf_len,
312 u32 *resp_buf)
313{
314 int ret;
315 int i;
316
317 for (i = 0; i < 3; i++) {
318 ret = mbox_send_cmd_common(id, cmd, is_indirect, len, arg,
319 urgent, resp_buf_len, resp_buf);
320 if (ret == MBOX_RESP_TIMEOUT || ret == MBOX_RESP_DEVICE_BUSY)
321 udelay(2000); /* wait for 2ms before resend */
322 else
323 break;
324 }
325
326 return ret;
327}
328
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800329int mbox_init(void)
330{
331 int ret;
332
333 /* enable mailbox interrupts */
334 MBOX_WRITEL(MBOX_ALL_INTRS, MBOX_FLAGS);
335
336 /* Ensure urgent request is cleared */
337 MBOX_WRITEL(0, MBOX_URG);
338
339 /* Ensure the Doorbell Interrupt is cleared */
340 MBOX_WRITEL(0, MBOX_DOORBELL_FROM_SDM);
341
342 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_RESTART, MBOX_CMD_DIRECT, 0,
343 NULL, 1, 0, NULL);
344 if (ret)
345 return ret;
346
347 /* Renable mailbox interrupts after MBOX_RESTART */
348 MBOX_WRITEL(MBOX_ALL_INTRS, MBOX_FLAGS);
349
350 return 0;
351}
352
353#ifdef CONFIG_CADENCE_QSPI
354int mbox_qspi_close(void)
355{
356 return mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_CLOSE, MBOX_CMD_DIRECT,
357 0, NULL, 0, 0, NULL);
358}
359
360int mbox_qspi_open(void)
361{
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800362 int ret;
363 u32 resp_buf[1];
364 u32 resp_buf_len;
365
366 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_OPEN, MBOX_CMD_DIRECT,
367 0, NULL, 0, 0, NULL);
368 if (ret) {
369 /* retry again by closing and reopen the QSPI again */
370 ret = mbox_qspi_close();
371 if (ret)
372 return ret;
373
374 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_OPEN,
375 MBOX_CMD_DIRECT, 0, NULL, 0, 0, NULL);
376 if (ret)
377 return ret;
378 }
379
380 /* HPS will directly control the QSPI controller, no longer mailbox */
381 resp_buf_len = 1;
382 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_DIRECT, MBOX_CMD_DIRECT,
383 0, NULL, 0, (u32 *)&resp_buf_len,
384 (u32 *)&resp_buf);
385 if (ret)
386 goto error;
387
Siew Chin Limc1888b02021-03-24 17:16:50 +0800388 /* Store QSPI controller ref clock frequency */
389 ret = cm_set_qspi_controller_clk_hz(resp_buf[0]);
390 if (ret)
391 goto error;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800392
393 return 0;
394
395error:
396 mbox_qspi_close();
397
398 return ret;
399}
400#endif /* CONFIG_CADENCE_QSPI */
401
402int mbox_reset_cold(void)
403{
Chee Hong Ang6aa10582020-12-24 18:21:08 +0800404#if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_ATF)
405 psci_system_reset();
406#else
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800407 int ret;
408
409 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_REBOOT_HPS, MBOX_CMD_DIRECT,
410 0, NULL, 0, 0, NULL);
411 if (ret) {
412 /* mailbox sent failure, wait for watchdog to kick in */
413 hang();
414 }
Chee Hong Ang6aa10582020-12-24 18:21:08 +0800415#endif
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800416 return 0;
417}
418
Ang, Chee Hong31b79632018-12-19 18:35:12 -0800419/* Accepted commands: CONFIG_STATUS or RECONFIG_STATUS */
420static __always_inline int mbox_get_fpga_config_status_common(u32 cmd)
421{
422 u32 reconfig_status_resp_len;
423 u32 reconfig_status_resp[RECONFIG_STATUS_RESPONSE_LEN];
424 int ret;
425
426 reconfig_status_resp_len = RECONFIG_STATUS_RESPONSE_LEN;
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800427 ret = mbox_send_cmd_common_retry(MBOX_ID_UBOOT, cmd,
428 MBOX_CMD_DIRECT, 0, NULL, 0,
429 &reconfig_status_resp_len,
430 reconfig_status_resp);
Ang, Chee Hong31b79632018-12-19 18:35:12 -0800431
432 if (ret)
433 return ret;
434
435 /* Check for any error */
436 ret = reconfig_status_resp[RECONFIG_STATUS_STATE];
437 if (ret && ret != MBOX_CFGSTAT_STATE_CONFIG)
438 return ret;
439
440 /* Make sure nStatus is not 0 */
441 ret = reconfig_status_resp[RECONFIG_STATUS_PIN_STATUS];
442 if (!(ret & RCF_PIN_STATUS_NSTATUS))
443 return MBOX_CFGSTAT_STATE_ERROR_HARDWARE;
444
445 ret = reconfig_status_resp[RECONFIG_STATUS_SOFTFUNC_STATUS];
446 if (ret & RCF_SOFTFUNC_STATUS_SEU_ERROR)
447 return MBOX_CFGSTAT_STATE_ERROR_HARDWARE;
448
449 if ((ret & RCF_SOFTFUNC_STATUS_CONF_DONE) &&
450 (ret & RCF_SOFTFUNC_STATUS_INIT_DONE) &&
451 !reconfig_status_resp[RECONFIG_STATUS_STATE])
452 return 0; /* configuration success */
453
454 return MBOX_CFGSTAT_STATE_CONFIG;
455}
456
457int mbox_get_fpga_config_status(u32 cmd)
458{
459 return mbox_get_fpga_config_status_common(cmd);
460}
461
462int __secure mbox_get_fpga_config_status_psci(u32 cmd)
463{
464 return mbox_get_fpga_config_status_common(cmd);
465}
466
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800467int mbox_send_cmd(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg,
468 u8 urgent, u32 *resp_buf_len, u32 *resp_buf)
469{
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800470 return mbox_send_cmd_common_retry(id, cmd, is_indirect, len, arg,
471 urgent, resp_buf_len, resp_buf);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800472}
473
474int __secure mbox_send_cmd_psci(u8 id, u32 cmd, u8 is_indirect, u32 len,
475 u32 *arg, u8 urgent, u32 *resp_buf_len,
476 u32 *resp_buf)
477{
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800478 return mbox_send_cmd_common_retry(id, cmd, is_indirect, len, arg,
479 urgent, resp_buf_len, resp_buf);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800480}
481
482int mbox_send_cmd_only(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg)
483{
484 return mbox_send_cmd_only_common(id, cmd, is_indirect, len, arg);
485}
486
487int __secure mbox_send_cmd_only_psci(u8 id, u32 cmd, u8 is_indirect, u32 len,
488 u32 *arg)
489{
490 return mbox_send_cmd_only_common(id, cmd, is_indirect, len, arg);
491}
492
493int mbox_rcv_resp(u32 *resp_buf, u32 resp_buf_max_len)
494{
495 return __mbox_rcv_resp(resp_buf, resp_buf_max_len);
496}
497
498int __secure mbox_rcv_resp_psci(u32 *resp_buf, u32 resp_buf_max_len)
499{
500 return __mbox_rcv_resp(resp_buf, resp_buf_max_len);
501}