blob: a9ec81849297c2f8b3449ed2b8e7b974ff9f0b40 [file] [log] [blame]
Ley Foon Tana280e9d2018-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 Glassdb41d652019-12-28 10:45:07 -07008#include <hang.h>
Ley Foon Tana280e9d2018-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 Ange3fca502020-08-12 09:56:21 +080032 unsigned long i = 2000;
Ley Foon Tana280e9d2018-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 Ange3fca502020-08-12 09:56:21 +080039 udelay(1000);
Ley Foon Tana280e9d2018-05-24 00:17:25 +080040 i--;
41 }
42
43 return -ETIMEDOUT;
44}
45
Chee Hong Angf6dcf402020-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 Tana280e9d2018-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 Angf6dcf402020-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 Tana280e9d2018-05-24 00:17:25 +0800113
Chee Hong Angf6dcf402020-08-12 09:56:23 +0800114 ret = mbox_write_cmd_buffer(&cin, header, &is_cmdbuf_overflow);
115 if (ret)
116 return ret;
Ley Foon Tana280e9d2018-05-24 00:17:25 +0800117
118 /* write arguments */
119 for (i = 0; i < len; i++) {
Chee Hong Angf6dcf402020-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 Tana280e9d2018-05-24 00:17:25 +0800124 }
125
Chee Hong Angf6dcf402020-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 Tana280e9d2018-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 Tana280e9d2018-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 Angf6dcf402020-08-12 09:56:23 +0800161 return mbox_prepare_cmd_only(id, cmd, is_indirect, len, arg);
Ley Foon Tana280e9d2018-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 Tana280e9d2018-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 Tan8497cb92018-08-17 16:22:03 +0800212 /* Write urgent command to urgent register */
213 MBOX_WRITEL(cmd, MBOX_URG);
Chee Hong Angf6dcf402020-08-12 09:56:23 +0800214 /* write doorbell */
215 MBOX_WRITEL(1, MBOX_DOORBELL_TO_SDM);
Ley Foon Tan8497cb92018-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 Tana280e9d2018-05-24 00:17:25 +0800220 }
221
Ley Foon Tana280e9d2018-05-24 00:17:25 +0800222 while (1) {
Chee Hong Ange3fca502020-08-12 09:56:21 +0800223 ret = 1000;
Ley Foon Tana280e9d2018-05-24 00:17:25 +0800224
225 /* Wait for doorbell from SDM */
Chee Hong Ange3fca502020-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 Tana280e9d2018-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 Tan8497cb92018-08-17 16:22:03 +0800240
Ley Foon Tana280e9d2018-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 Ang833230e2020-08-12 09:56:22 +0800267 int resp_err = MBOX_RESP_ERR_GET(resp);
Ley Foon Tana280e9d2018-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 Ang833230e2020-08-12 09:56:22 +0800296 return resp_err;
Ley Foon Tana280e9d2018-05-24 00:17:25 +0800297 }
298 }
299 };
300
301 return -EIO;
302}
303
304int mbox_init(void)
305{
306 int ret;
307
308 /* enable mailbox interrupts */
309 MBOX_WRITEL(MBOX_ALL_INTRS, MBOX_FLAGS);
310
311 /* Ensure urgent request is cleared */
312 MBOX_WRITEL(0, MBOX_URG);
313
314 /* Ensure the Doorbell Interrupt is cleared */
315 MBOX_WRITEL(0, MBOX_DOORBELL_FROM_SDM);
316
317 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_RESTART, MBOX_CMD_DIRECT, 0,
318 NULL, 1, 0, NULL);
319 if (ret)
320 return ret;
321
322 /* Renable mailbox interrupts after MBOX_RESTART */
323 MBOX_WRITEL(MBOX_ALL_INTRS, MBOX_FLAGS);
324
325 return 0;
326}
327
328#ifdef CONFIG_CADENCE_QSPI
329int mbox_qspi_close(void)
330{
331 return mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_CLOSE, MBOX_CMD_DIRECT,
332 0, NULL, 0, 0, NULL);
333}
334
335int mbox_qspi_open(void)
336{
Ley Foon Tana280e9d2018-05-24 00:17:25 +0800337 int ret;
338 u32 resp_buf[1];
339 u32 resp_buf_len;
340
341 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_OPEN, MBOX_CMD_DIRECT,
342 0, NULL, 0, 0, NULL);
343 if (ret) {
344 /* retry again by closing and reopen the QSPI again */
345 ret = mbox_qspi_close();
346 if (ret)
347 return ret;
348
349 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_OPEN,
350 MBOX_CMD_DIRECT, 0, NULL, 0, 0, NULL);
351 if (ret)
352 return ret;
353 }
354
355 /* HPS will directly control the QSPI controller, no longer mailbox */
356 resp_buf_len = 1;
357 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_DIRECT, MBOX_CMD_DIRECT,
358 0, NULL, 0, (u32 *)&resp_buf_len,
359 (u32 *)&resp_buf);
360 if (ret)
361 goto error;
362
363 /* We are getting QSPI ref clock and set into sysmgr boot register */
364 printf("QSPI: Reference clock at %d Hz\n", resp_buf[0]);
Ley Foon Tandb5741f2019-11-08 10:38:20 +0800365 writel(resp_buf[0],
Ley Foon Tan2fd1dc52019-11-27 15:55:18 +0800366 socfpga_get_sysmgr_addr() + SYSMGR_SOC64_BOOT_SCRATCH_COLD0);
Ley Foon Tana280e9d2018-05-24 00:17:25 +0800367
368 return 0;
369
370error:
371 mbox_qspi_close();
372
373 return ret;
374}
375#endif /* CONFIG_CADENCE_QSPI */
376
377int mbox_reset_cold(void)
378{
379 int ret;
380
381 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_REBOOT_HPS, MBOX_CMD_DIRECT,
382 0, NULL, 0, 0, NULL);
383 if (ret) {
384 /* mailbox sent failure, wait for watchdog to kick in */
385 hang();
386 }
387 return 0;
388}
389
Ang, Chee Hongd99f1e92018-12-19 18:35:12 -0800390/* Accepted commands: CONFIG_STATUS or RECONFIG_STATUS */
391static __always_inline int mbox_get_fpga_config_status_common(u32 cmd)
392{
393 u32 reconfig_status_resp_len;
394 u32 reconfig_status_resp[RECONFIG_STATUS_RESPONSE_LEN];
395 int ret;
396
397 reconfig_status_resp_len = RECONFIG_STATUS_RESPONSE_LEN;
398 ret = mbox_send_cmd_common(MBOX_ID_UBOOT, cmd,
399 MBOX_CMD_DIRECT, 0, NULL, 0,
400 &reconfig_status_resp_len,
401 reconfig_status_resp);
402
403 if (ret)
404 return ret;
405
406 /* Check for any error */
407 ret = reconfig_status_resp[RECONFIG_STATUS_STATE];
408 if (ret && ret != MBOX_CFGSTAT_STATE_CONFIG)
409 return ret;
410
411 /* Make sure nStatus is not 0 */
412 ret = reconfig_status_resp[RECONFIG_STATUS_PIN_STATUS];
413 if (!(ret & RCF_PIN_STATUS_NSTATUS))
414 return MBOX_CFGSTAT_STATE_ERROR_HARDWARE;
415
416 ret = reconfig_status_resp[RECONFIG_STATUS_SOFTFUNC_STATUS];
417 if (ret & RCF_SOFTFUNC_STATUS_SEU_ERROR)
418 return MBOX_CFGSTAT_STATE_ERROR_HARDWARE;
419
420 if ((ret & RCF_SOFTFUNC_STATUS_CONF_DONE) &&
421 (ret & RCF_SOFTFUNC_STATUS_INIT_DONE) &&
422 !reconfig_status_resp[RECONFIG_STATUS_STATE])
423 return 0; /* configuration success */
424
425 return MBOX_CFGSTAT_STATE_CONFIG;
426}
427
428int mbox_get_fpga_config_status(u32 cmd)
429{
430 return mbox_get_fpga_config_status_common(cmd);
431}
432
433int __secure mbox_get_fpga_config_status_psci(u32 cmd)
434{
435 return mbox_get_fpga_config_status_common(cmd);
436}
437
Ley Foon Tana280e9d2018-05-24 00:17:25 +0800438int mbox_send_cmd(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg,
439 u8 urgent, u32 *resp_buf_len, u32 *resp_buf)
440{
441 return mbox_send_cmd_common(id, cmd, is_indirect, len, arg, urgent,
442 resp_buf_len, resp_buf);
443}
444
445int __secure mbox_send_cmd_psci(u8 id, u32 cmd, u8 is_indirect, u32 len,
446 u32 *arg, u8 urgent, u32 *resp_buf_len,
447 u32 *resp_buf)
448{
449 return mbox_send_cmd_common(id, cmd, is_indirect, len, arg, urgent,
450 resp_buf_len, resp_buf);
451}
452
453int mbox_send_cmd_only(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg)
454{
455 return mbox_send_cmd_only_common(id, cmd, is_indirect, len, arg);
456}
457
458int __secure mbox_send_cmd_only_psci(u8 id, u32 cmd, u8 is_indirect, u32 len,
459 u32 *arg)
460{
461 return mbox_send_cmd_only_common(id, cmd, is_indirect, len, arg);
462}
463
464int mbox_rcv_resp(u32 *resp_buf, u32 resp_buf_max_len)
465{
466 return __mbox_rcv_resp(resp_buf, resp_buf_max_len);
467}
468
469int __secure mbox_rcv_resp_psci(u32 *resp_buf, u32 resp_buf_max_len)
470{
471 return __mbox_rcv_resp(resp_buf, resp_buf_max_len);
472}