blob: 4c86f1e99170c098eb4b5259a2e6a9e861a4b702 [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);
91 break;
92 }
93 timeout--;
94 }
95
96 if (!timeout)
97 return -ETIMEDOUT;
98
99 /* Wait for the SDM to drain the FIFO command buffer */
100 if (is_cmdbuf_overflow && *is_cmdbuf_overflow)
101 return mbox_wait_for_cmdbuf_empty(*cin);
102
103 return 0;
104}
105
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800106/* Check for available slot and write to circular buffer.
107 * It also update command valid offset (cin) register.
108 */
109static __always_inline int mbox_fill_cmd_circular_buff(u32 header, u32 len,
110 u32 *arg)
111{
Chee Hong Ange4805862020-08-12 09:56:23 +0800112 int i, ret;
113 int is_cmdbuf_overflow = 0;
114 u32 cin = MBOX_READL(MBOX_CIN) % MBOX_CMD_BUFFER_SIZE;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800115
Chee Hong Ange4805862020-08-12 09:56:23 +0800116 ret = mbox_write_cmd_buffer(&cin, header, &is_cmdbuf_overflow);
117 if (ret)
118 return ret;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800119
120 /* write arguments */
121 for (i = 0; i < len; i++) {
Chee Hong Ange4805862020-08-12 09:56:23 +0800122 is_cmdbuf_overflow = 0;
123 ret = mbox_write_cmd_buffer(&cin, arg[i], &is_cmdbuf_overflow);
124 if (ret)
125 return ret;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800126 }
127
Chee Hong Ange4805862020-08-12 09:56:23 +0800128 /* If SDM doorbell is not triggered after the last data is
129 * written into mailbox FIFO command buffer, trigger the
130 * SDM doorbell again to ensure SDM able to read the remaining
131 * data.
132 */
133 if (!is_cmdbuf_overflow)
134 MBOX_WRITEL(1, MBOX_DOORBELL_TO_SDM);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800135
136 return 0;
137}
138
139/* Check the command and fill it into circular buffer */
140static __always_inline int mbox_prepare_cmd_only(u8 id, u32 cmd,
141 u8 is_indirect, u32 len,
142 u32 *arg)
143{
144 u32 header;
145 int ret;
146
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800147 if (cmd > MBOX_MAX_CMD_INDEX)
148 return -EINVAL;
149
150 header = MBOX_CMD_HEADER(MBOX_CLIENT_ID_UBOOT, id, len,
151 (is_indirect) ? 1 : 0, cmd);
152
153 ret = mbox_fill_cmd_circular_buff(header, len, arg);
154
155 return ret;
156}
157
158/* Send command only without waiting for responses from SDM */
159static __always_inline int mbox_send_cmd_only_common(u8 id, u32 cmd,
160 u8 is_indirect, u32 len,
161 u32 *arg)
162{
Chee Hong Ange4805862020-08-12 09:56:23 +0800163 return mbox_prepare_cmd_only(id, cmd, is_indirect, len, arg);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800164}
165
166/* Return number of responses received in buffer */
167static __always_inline int __mbox_rcv_resp(u32 *resp_buf, u32 resp_buf_max_len)
168{
169 u32 rin;
170 u32 rout;
171 u32 resp_len = 0;
172
173 /* clear doorbell from SDM if it was SET */
174 if (MBOX_READL(MBOX_DOORBELL_FROM_SDM) & 1)
175 MBOX_WRITEL(0, MBOX_DOORBELL_FROM_SDM);
176
177 /* read current response offset */
178 rout = MBOX_READL(MBOX_ROUT);
179 /* read response valid offset */
180 rin = MBOX_READL(MBOX_RIN);
181
182 while (rin != rout && (resp_len < resp_buf_max_len)) {
183 /* Response received */
184 if (resp_buf)
185 resp_buf[resp_len++] = MBOX_READ_RESP_BUF(rout);
186
187 rout++;
188 /* wrapping around when it reach the buffer size */
189 rout %= MBOX_RESP_BUFFER_SIZE;
190 /* update next ROUT */
191 MBOX_WRITEL(rout, MBOX_ROUT);
192 }
193
194 return resp_len;
195}
196
197/* Support one command and up to 31 words argument length only */
198static __always_inline int mbox_send_cmd_common(u8 id, u32 cmd, u8 is_indirect,
199 u32 len, u32 *arg, u8 urgent,
200 u32 *resp_buf_len,
201 u32 *resp_buf)
202{
203 u32 rin;
204 u32 resp;
205 u32 rout;
206 u32 status;
207 u32 resp_len;
208 u32 buf_len;
209 int ret;
210
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800211 if (urgent) {
212 /* Read status because it is toggled */
213 status = MBOX_READL(MBOX_STATUS) & MBOX_STATUS_UA_MSK;
Ley Foon Tanff756cc2018-08-17 16:22:03 +0800214 /* Write urgent command to urgent register */
215 MBOX_WRITEL(cmd, MBOX_URG);
Chee Hong Ange4805862020-08-12 09:56:23 +0800216 /* write doorbell */
217 MBOX_WRITEL(1, MBOX_DOORBELL_TO_SDM);
Ley Foon Tanff756cc2018-08-17 16:22:03 +0800218 } else {
219 ret = mbox_prepare_cmd_only(id, cmd, is_indirect, len, arg);
220 if (ret)
221 return ret;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800222 }
223
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800224 while (1) {
Chee Hong Ang0cc64c82020-08-12 09:56:21 +0800225 ret = 1000;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800226
227 /* Wait for doorbell from SDM */
Chee Hong Ang0cc64c82020-08-12 09:56:21 +0800228 do {
229 if (MBOX_READL(MBOX_DOORBELL_FROM_SDM))
230 break;
231 udelay(1000);
232 } while (--ret);
233
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800234 if (!ret)
235 return -ETIMEDOUT;
236
237 /* clear interrupt */
238 MBOX_WRITEL(0, MBOX_DOORBELL_FROM_SDM);
239
240 if (urgent) {
241 u32 new_status = MBOX_READL(MBOX_STATUS);
Ley Foon Tanff756cc2018-08-17 16:22:03 +0800242
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800243 /* Urgent ACK is toggled */
244 if ((new_status & MBOX_STATUS_UA_MSK) ^ status)
245 return 0;
246
247 return -ECOMM;
248 }
249
250 /* read current response offset */
251 rout = MBOX_READL(MBOX_ROUT);
252
253 /* read response valid offset */
254 rin = MBOX_READL(MBOX_RIN);
255
256 if (rout != rin) {
257 /* Response received */
258 resp = MBOX_READ_RESP_BUF(rout);
259 rout++;
260 /* wrapping around when it reach the buffer size */
261 rout %= MBOX_RESP_BUFFER_SIZE;
262 /* update next ROUT */
263 MBOX_WRITEL(rout, MBOX_ROUT);
264
265 /* check client ID and ID */
266 if ((MBOX_RESP_CLIENT_GET(resp) ==
267 MBOX_CLIENT_ID_UBOOT) &&
268 (MBOX_RESP_ID_GET(resp) == id)) {
Chee Hong Angfe33ea32020-08-12 09:56:22 +0800269 int resp_err = MBOX_RESP_ERR_GET(resp);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800270
271 if (resp_buf_len) {
272 buf_len = *resp_buf_len;
273 *resp_buf_len = 0;
274 } else {
275 buf_len = 0;
276 }
277
278 resp_len = MBOX_RESP_LEN_GET(resp);
279 while (resp_len) {
280 ret = mbox_polling_resp(rout);
281 if (ret)
282 return ret;
283 /* we need to process response buffer
284 * even caller doesn't need it
285 */
286 resp = MBOX_READ_RESP_BUF(rout);
287 rout++;
288 resp_len--;
289 rout %= MBOX_RESP_BUFFER_SIZE;
290 MBOX_WRITEL(rout, MBOX_ROUT);
291 if (buf_len) {
292 /* copy response to buffer */
293 resp_buf[*resp_buf_len] = resp;
294 (*resp_buf_len)++;
295 buf_len--;
296 }
297 }
Chee Hong Angfe33ea32020-08-12 09:56:22 +0800298 return resp_err;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800299 }
300 }
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800301 }
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800302
303 return -EIO;
304}
305
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800306static __always_inline int mbox_send_cmd_common_retry(u8 id, u32 cmd,
307 u8 is_indirect,
308 u32 len, u32 *arg,
309 u8 urgent,
310 u32 *resp_buf_len,
311 u32 *resp_buf)
312{
313 int ret;
314 int i;
315
316 for (i = 0; i < 3; i++) {
317 ret = mbox_send_cmd_common(id, cmd, is_indirect, len, arg,
318 urgent, resp_buf_len, resp_buf);
319 if (ret == MBOX_RESP_TIMEOUT || ret == MBOX_RESP_DEVICE_BUSY)
320 udelay(2000); /* wait for 2ms before resend */
321 else
322 break;
323 }
324
325 return ret;
326}
327
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800328int mbox_init(void)
329{
330 int ret;
331
332 /* enable mailbox interrupts */
333 MBOX_WRITEL(MBOX_ALL_INTRS, MBOX_FLAGS);
334
335 /* Ensure urgent request is cleared */
336 MBOX_WRITEL(0, MBOX_URG);
337
338 /* Ensure the Doorbell Interrupt is cleared */
339 MBOX_WRITEL(0, MBOX_DOORBELL_FROM_SDM);
340
341 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_RESTART, MBOX_CMD_DIRECT, 0,
342 NULL, 1, 0, NULL);
343 if (ret)
344 return ret;
345
346 /* Renable mailbox interrupts after MBOX_RESTART */
347 MBOX_WRITEL(MBOX_ALL_INTRS, MBOX_FLAGS);
348
349 return 0;
350}
351
352#ifdef CONFIG_CADENCE_QSPI
353int mbox_qspi_close(void)
354{
355 return mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_CLOSE, MBOX_CMD_DIRECT,
356 0, NULL, 0, 0, NULL);
357}
358
359int mbox_qspi_open(void)
360{
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800361 int ret;
362 u32 resp_buf[1];
363 u32 resp_buf_len;
364
365 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_OPEN, MBOX_CMD_DIRECT,
366 0, NULL, 0, 0, NULL);
367 if (ret) {
368 /* retry again by closing and reopen the QSPI again */
369 ret = mbox_qspi_close();
370 if (ret)
371 return ret;
372
373 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_OPEN,
374 MBOX_CMD_DIRECT, 0, NULL, 0, 0, NULL);
375 if (ret)
376 return ret;
377 }
378
379 /* HPS will directly control the QSPI controller, no longer mailbox */
380 resp_buf_len = 1;
381 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_DIRECT, MBOX_CMD_DIRECT,
382 0, NULL, 0, (u32 *)&resp_buf_len,
383 (u32 *)&resp_buf);
384 if (ret)
385 goto error;
386
Siew Chin Limc1888b02021-03-24 17:16:50 +0800387 /* Store QSPI controller ref clock frequency */
388 ret = cm_set_qspi_controller_clk_hz(resp_buf[0]);
389 if (ret)
390 goto error;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800391
392 return 0;
393
394error:
395 mbox_qspi_close();
396
397 return ret;
398}
399#endif /* CONFIG_CADENCE_QSPI */
400
401int mbox_reset_cold(void)
402{
Chee Hong Ang6aa10582020-12-24 18:21:08 +0800403#if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_ATF)
404 psci_system_reset();
405#else
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800406 int ret;
407
408 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_REBOOT_HPS, MBOX_CMD_DIRECT,
409 0, NULL, 0, 0, NULL);
410 if (ret) {
411 /* mailbox sent failure, wait for watchdog to kick in */
412 hang();
413 }
Chee Hong Ang6aa10582020-12-24 18:21:08 +0800414#endif
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800415 return 0;
416}
417
Ang, Chee Hong31b79632018-12-19 18:35:12 -0800418/* Accepted commands: CONFIG_STATUS or RECONFIG_STATUS */
419static __always_inline int mbox_get_fpga_config_status_common(u32 cmd)
420{
421 u32 reconfig_status_resp_len;
422 u32 reconfig_status_resp[RECONFIG_STATUS_RESPONSE_LEN];
423 int ret;
424
425 reconfig_status_resp_len = RECONFIG_STATUS_RESPONSE_LEN;
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800426 ret = mbox_send_cmd_common_retry(MBOX_ID_UBOOT, cmd,
427 MBOX_CMD_DIRECT, 0, NULL, 0,
428 &reconfig_status_resp_len,
429 reconfig_status_resp);
Ang, Chee Hong31b79632018-12-19 18:35:12 -0800430
431 if (ret)
432 return ret;
433
434 /* Check for any error */
435 ret = reconfig_status_resp[RECONFIG_STATUS_STATE];
436 if (ret && ret != MBOX_CFGSTAT_STATE_CONFIG)
437 return ret;
438
439 /* Make sure nStatus is not 0 */
440 ret = reconfig_status_resp[RECONFIG_STATUS_PIN_STATUS];
441 if (!(ret & RCF_PIN_STATUS_NSTATUS))
442 return MBOX_CFGSTAT_STATE_ERROR_HARDWARE;
443
444 ret = reconfig_status_resp[RECONFIG_STATUS_SOFTFUNC_STATUS];
445 if (ret & RCF_SOFTFUNC_STATUS_SEU_ERROR)
446 return MBOX_CFGSTAT_STATE_ERROR_HARDWARE;
447
448 if ((ret & RCF_SOFTFUNC_STATUS_CONF_DONE) &&
449 (ret & RCF_SOFTFUNC_STATUS_INIT_DONE) &&
450 !reconfig_status_resp[RECONFIG_STATUS_STATE])
451 return 0; /* configuration success */
452
453 return MBOX_CFGSTAT_STATE_CONFIG;
454}
455
456int mbox_get_fpga_config_status(u32 cmd)
457{
458 return mbox_get_fpga_config_status_common(cmd);
459}
460
461int __secure mbox_get_fpga_config_status_psci(u32 cmd)
462{
463 return mbox_get_fpga_config_status_common(cmd);
464}
465
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800466int mbox_send_cmd(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg,
467 u8 urgent, u32 *resp_buf_len, u32 *resp_buf)
468{
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800469 return mbox_send_cmd_common_retry(id, cmd, is_indirect, len, arg,
470 urgent, resp_buf_len, resp_buf);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800471}
472
473int __secure mbox_send_cmd_psci(u8 id, u32 cmd, u8 is_indirect, u32 len,
474 u32 *arg, u8 urgent, u32 *resp_buf_len,
475 u32 *resp_buf)
476{
Ley Foon Tan916d2ba2020-08-12 09:56:25 +0800477 return mbox_send_cmd_common_retry(id, cmd, is_indirect, len, arg,
478 urgent, resp_buf_len, resp_buf);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800479}
480
481int mbox_send_cmd_only(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg)
482{
483 return mbox_send_cmd_only_common(id, cmd, is_indirect, len, arg);
484}
485
486int __secure mbox_send_cmd_only_psci(u8 id, u32 cmd, u8 is_indirect, u32 len,
487 u32 *arg)
488{
489 return mbox_send_cmd_only_common(id, cmd, is_indirect, len, arg);
490}
491
492int mbox_rcv_resp(u32 *resp_buf, u32 resp_buf_max_len)
493{
494 return __mbox_rcv_resp(resp_buf, resp_buf_max_len);
495}
496
497int __secure mbox_rcv_resp_psci(u32 *resp_buf, u32 resp_buf_max_len)
498{
499 return __mbox_rcv_resp(resp_buf, resp_buf_max_len);
500}