]>
Commit | Line | Data |
---|---|---|
a280e9db LFT |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (C) 2017-2018 Intel Corporation <www.intel.com> | |
4 | * | |
5 | */ | |
6 | ||
7 | #include <common.h> | |
db41d65a | 8 | #include <hang.h> |
a280e9db LFT |
9 | #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 | ||
15 | DECLARE_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 | ||
29 | static __always_inline int mbox_polling_resp(u32 rout) | |
30 | { | |
31 | u32 rin; | |
e3fca507 | 32 | unsigned long i = 2000; |
a280e9db LFT |
33 | |
34 | while (i) { | |
35 | rin = MBOX_READL(MBOX_RIN); | |
36 | if (rout != rin) | |
37 | return 0; | |
38 | ||
e3fca507 | 39 | udelay(1000); |
a280e9db LFT |
40 | i--; |
41 | } | |
42 | ||
43 | return -ETIMEDOUT; | |
44 | } | |
45 | ||
f6dcf407 CHA |
46 | static __always_inline int mbox_is_cmdbuf_full(u32 cin) |
47 | { | |
48 | return (((cin + 1) % MBOX_CMD_BUFFER_SIZE) == MBOX_READL(MBOX_COUT)); | |
49 | } | |
50 | ||
51 | static __always_inline int mbox_is_cmdbuf_empty(u32 cin) | |
52 | { | |
53 | return (((MBOX_READL(MBOX_COUT) + 1) % MBOX_CMD_BUFFER_SIZE) == cin); | |
54 | } | |
55 | ||
56 | static __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 | ||
70 | static __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 | ||
a280e9db LFT |
104 | /* Check for available slot and write to circular buffer. |
105 | * It also update command valid offset (cin) register. | |
106 | */ | |
107 | static __always_inline int mbox_fill_cmd_circular_buff(u32 header, u32 len, | |
108 | u32 *arg) | |
109 | { | |
f6dcf407 CHA |
110 | int i, ret; |
111 | int is_cmdbuf_overflow = 0; | |
112 | u32 cin = MBOX_READL(MBOX_CIN) % MBOX_CMD_BUFFER_SIZE; | |
a280e9db | 113 | |
f6dcf407 CHA |
114 | ret = mbox_write_cmd_buffer(&cin, header, &is_cmdbuf_overflow); |
115 | if (ret) | |
116 | return ret; | |
a280e9db LFT |
117 | |
118 | /* write arguments */ | |
119 | for (i = 0; i < len; i++) { | |
f6dcf407 CHA |
120 | is_cmdbuf_overflow = 0; |
121 | ret = mbox_write_cmd_buffer(&cin, arg[i], &is_cmdbuf_overflow); | |
122 | if (ret) | |
123 | return ret; | |
a280e9db LFT |
124 | } |
125 | ||
f6dcf407 CHA |
126 | /* 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); | |
a280e9db LFT |
133 | |
134 | return 0; | |
135 | } | |
136 | ||
137 | /* Check the command and fill it into circular buffer */ | |
138 | static __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 | ||
a280e9db LFT |
145 | 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 */ | |
157 | static __always_inline int mbox_send_cmd_only_common(u8 id, u32 cmd, | |
158 | u8 is_indirect, u32 len, | |
159 | u32 *arg) | |
160 | { | |
f6dcf407 | 161 | return mbox_prepare_cmd_only(id, cmd, is_indirect, len, arg); |
a280e9db LFT |
162 | } |
163 | ||
164 | /* Return number of responses received in buffer */ | |
165 | static __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 */ | |
196 | static __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 | ||
a280e9db LFT |
209 | if (urgent) { |
210 | /* Read status because it is toggled */ | |
211 | status = MBOX_READL(MBOX_STATUS) & MBOX_STATUS_UA_MSK; | |
8497cb9b LFT |
212 | /* Write urgent command to urgent register */ |
213 | MBOX_WRITEL(cmd, MBOX_URG); | |
f6dcf407 CHA |
214 | /* write doorbell */ |
215 | MBOX_WRITEL(1, MBOX_DOORBELL_TO_SDM); | |
8497cb9b LFT |
216 | } else { |
217 | ret = mbox_prepare_cmd_only(id, cmd, is_indirect, len, arg); | |
218 | if (ret) | |
219 | return ret; | |
a280e9db LFT |
220 | } |
221 | ||
a280e9db | 222 | while (1) { |
e3fca507 | 223 | ret = 1000; |
a280e9db LFT |
224 | |
225 | /* Wait for doorbell from SDM */ | |
e3fca507 CHA |
226 | do { |
227 | if (MBOX_READL(MBOX_DOORBELL_FROM_SDM)) | |
228 | break; | |
229 | udelay(1000); | |
230 | } while (--ret); | |
231 | ||
a280e9db LFT |
232 | 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); | |
8497cb9b | 240 | |
a280e9db LFT |
241 | /* 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)) { | |
833230ed | 267 | int resp_err = MBOX_RESP_ERR_GET(resp); |
a280e9db LFT |
268 | |
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 | } | |
833230ed | 296 | return resp_err; |
a280e9db LFT |
297 | } |
298 | } | |
de84e2d8 | 299 | } |
a280e9db LFT |
300 | |
301 | return -EIO; | |
302 | } | |
303 | ||
de84e2d8 LFT |
304 | static __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 | ||
a280e9db LFT |
326 | int 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 | |
351 | int 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 | ||
357 | int mbox_qspi_open(void) | |
358 | { | |
a280e9db LFT |
359 | 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]); | |
db5741f7 | 387 | writel(resp_buf[0], |
2fd1dc55 | 388 | socfpga_get_sysmgr_addr() + SYSMGR_SOC64_BOOT_SCRATCH_COLD0); |
a280e9db LFT |
389 | |
390 | return 0; | |
391 | ||
392 | error: | |
393 | mbox_qspi_close(); | |
394 | ||
395 | return ret; | |
396 | } | |
397 | #endif /* CONFIG_CADENCE_QSPI */ | |
398 | ||
399 | int 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 | ||
d99f1e92 ACH |
412 | /* Accepted commands: CONFIG_STATUS or RECONFIG_STATUS */ |
413 | static __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; | |
de84e2d8 LFT |
420 | 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); | |
d99f1e92 ACH |
424 | |
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 | ||
450 | int mbox_get_fpga_config_status(u32 cmd) | |
451 | { | |
452 | return mbox_get_fpga_config_status_common(cmd); | |
453 | } | |
454 | ||
455 | int __secure mbox_get_fpga_config_status_psci(u32 cmd) | |
456 | { | |
457 | return mbox_get_fpga_config_status_common(cmd); | |
458 | } | |
459 | ||
a280e9db LFT |
460 | int 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 | { | |
de84e2d8 LFT |
463 | return mbox_send_cmd_common_retry(id, cmd, is_indirect, len, arg, |
464 | urgent, resp_buf_len, resp_buf); | |
a280e9db LFT |
465 | } |
466 | ||
467 | int __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 | { | |
de84e2d8 LFT |
471 | return mbox_send_cmd_common_retry(id, cmd, is_indirect, len, arg, |
472 | urgent, resp_buf_len, resp_buf); | |
a280e9db LFT |
473 | } |
474 | ||
475 | int 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 | ||
480 | int __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 | ||
486 | int 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 | ||
491 | int __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 | } |