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