blob: f30e7f80a2940cfa0ac6a8cb3dc3e235a4f8e322 [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;
32 unsigned long i = ~0;
33
34 while (i) {
35 rin = MBOX_READL(MBOX_RIN);
36 if (rout != rin)
37 return 0;
38
39 i--;
40 }
41
42 return -ETIMEDOUT;
43}
44
45/* Check for available slot and write to circular buffer.
46 * It also update command valid offset (cin) register.
47 */
48static __always_inline int mbox_fill_cmd_circular_buff(u32 header, u32 len,
49 u32 *arg)
50{
51 u32 cin;
52 u32 cout;
53 u32 i;
54
55 cin = MBOX_READL(MBOX_CIN) % MBOX_CMD_BUFFER_SIZE;
56 cout = MBOX_READL(MBOX_COUT) % MBOX_CMD_BUFFER_SIZE;
57
58 /* if command buffer is full or not enough free space
Ley Foon Tan374c2302019-04-24 13:21:47 +080059 * to fit the data. Note, len is in u32 unit.
Ley Foon Tane5b6a662018-05-24 00:17:25 +080060 */
61 if (((cin + 1) % MBOX_CMD_BUFFER_SIZE) == cout ||
62 ((MBOX_CMD_BUFFER_SIZE - cin + cout - 1) %
Ley Foon Tan374c2302019-04-24 13:21:47 +080063 MBOX_CMD_BUFFER_SIZE) < (len + 1))
Ley Foon Tane5b6a662018-05-24 00:17:25 +080064 return -ENOMEM;
65
66 /* write header to circular buffer */
67 MBOX_WRITE_CMD_BUF(header, cin++);
68 /* wrapping around when it reach the buffer size */
69 cin %= MBOX_CMD_BUFFER_SIZE;
70
71 /* write arguments */
72 for (i = 0; i < len; i++) {
73 MBOX_WRITE_CMD_BUF(arg[i], cin++);
74 /* wrapping around when it reach the buffer size */
75 cin %= MBOX_CMD_BUFFER_SIZE;
76 }
77
78 /* write command valid offset */
79 MBOX_WRITEL(cin, MBOX_CIN);
80
81 return 0;
82}
83
84/* Check the command and fill it into circular buffer */
85static __always_inline int mbox_prepare_cmd_only(u8 id, u32 cmd,
86 u8 is_indirect, u32 len,
87 u32 *arg)
88{
89 u32 header;
90 int ret;
91
92 /* Total length is command + argument length */
93 if ((len + 1) > MBOX_CMD_BUFFER_SIZE)
94 return -EINVAL;
95
96 if (cmd > MBOX_MAX_CMD_INDEX)
97 return -EINVAL;
98
99 header = MBOX_CMD_HEADER(MBOX_CLIENT_ID_UBOOT, id, len,
100 (is_indirect) ? 1 : 0, cmd);
101
102 ret = mbox_fill_cmd_circular_buff(header, len, arg);
103
104 return ret;
105}
106
107/* Send command only without waiting for responses from SDM */
108static __always_inline int mbox_send_cmd_only_common(u8 id, u32 cmd,
109 u8 is_indirect, u32 len,
110 u32 *arg)
111{
112 int ret = mbox_prepare_cmd_only(id, cmd, is_indirect, len, arg);
113 /* write doorbell */
114 MBOX_WRITEL(1, MBOX_DOORBELL_TO_SDM);
115
116 return ret;
117}
118
119/* Return number of responses received in buffer */
120static __always_inline int __mbox_rcv_resp(u32 *resp_buf, u32 resp_buf_max_len)
121{
122 u32 rin;
123 u32 rout;
124 u32 resp_len = 0;
125
126 /* clear doorbell from SDM if it was SET */
127 if (MBOX_READL(MBOX_DOORBELL_FROM_SDM) & 1)
128 MBOX_WRITEL(0, MBOX_DOORBELL_FROM_SDM);
129
130 /* read current response offset */
131 rout = MBOX_READL(MBOX_ROUT);
132 /* read response valid offset */
133 rin = MBOX_READL(MBOX_RIN);
134
135 while (rin != rout && (resp_len < resp_buf_max_len)) {
136 /* Response received */
137 if (resp_buf)
138 resp_buf[resp_len++] = MBOX_READ_RESP_BUF(rout);
139
140 rout++;
141 /* wrapping around when it reach the buffer size */
142 rout %= MBOX_RESP_BUFFER_SIZE;
143 /* update next ROUT */
144 MBOX_WRITEL(rout, MBOX_ROUT);
145 }
146
147 return resp_len;
148}
149
150/* Support one command and up to 31 words argument length only */
151static __always_inline int mbox_send_cmd_common(u8 id, u32 cmd, u8 is_indirect,
152 u32 len, u32 *arg, u8 urgent,
153 u32 *resp_buf_len,
154 u32 *resp_buf)
155{
156 u32 rin;
157 u32 resp;
158 u32 rout;
159 u32 status;
160 u32 resp_len;
161 u32 buf_len;
162 int ret;
163
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800164 if (urgent) {
165 /* Read status because it is toggled */
166 status = MBOX_READL(MBOX_STATUS) & MBOX_STATUS_UA_MSK;
Ley Foon Tanff756cc2018-08-17 16:22:03 +0800167 /* Write urgent command to urgent register */
168 MBOX_WRITEL(cmd, MBOX_URG);
169 } else {
170 ret = mbox_prepare_cmd_only(id, cmd, is_indirect, len, arg);
171 if (ret)
172 return ret;
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800173 }
174
175 /* write doorbell */
176 MBOX_WRITEL(1, MBOX_DOORBELL_TO_SDM);
177
178 while (1) {
179 ret = ~0;
180
181 /* Wait for doorbell from SDM */
182 while (!MBOX_READL(MBOX_DOORBELL_FROM_SDM) && ret--)
183 ;
184 if (!ret)
185 return -ETIMEDOUT;
186
187 /* clear interrupt */
188 MBOX_WRITEL(0, MBOX_DOORBELL_FROM_SDM);
189
190 if (urgent) {
191 u32 new_status = MBOX_READL(MBOX_STATUS);
Ley Foon Tanff756cc2018-08-17 16:22:03 +0800192
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800193 /* Urgent ACK is toggled */
194 if ((new_status & MBOX_STATUS_UA_MSK) ^ status)
195 return 0;
196
197 return -ECOMM;
198 }
199
200 /* read current response offset */
201 rout = MBOX_READL(MBOX_ROUT);
202
203 /* read response valid offset */
204 rin = MBOX_READL(MBOX_RIN);
205
206 if (rout != rin) {
207 /* Response received */
208 resp = MBOX_READ_RESP_BUF(rout);
209 rout++;
210 /* wrapping around when it reach the buffer size */
211 rout %= MBOX_RESP_BUFFER_SIZE;
212 /* update next ROUT */
213 MBOX_WRITEL(rout, MBOX_ROUT);
214
215 /* check client ID and ID */
216 if ((MBOX_RESP_CLIENT_GET(resp) ==
217 MBOX_CLIENT_ID_UBOOT) &&
218 (MBOX_RESP_ID_GET(resp) == id)) {
219 ret = MBOX_RESP_ERR_GET(resp);
220 if (ret)
221 return ret;
222
223 if (resp_buf_len) {
224 buf_len = *resp_buf_len;
225 *resp_buf_len = 0;
226 } else {
227 buf_len = 0;
228 }
229
230 resp_len = MBOX_RESP_LEN_GET(resp);
231 while (resp_len) {
232 ret = mbox_polling_resp(rout);
233 if (ret)
234 return ret;
235 /* we need to process response buffer
236 * even caller doesn't need it
237 */
238 resp = MBOX_READ_RESP_BUF(rout);
239 rout++;
240 resp_len--;
241 rout %= MBOX_RESP_BUFFER_SIZE;
242 MBOX_WRITEL(rout, MBOX_ROUT);
243 if (buf_len) {
244 /* copy response to buffer */
245 resp_buf[*resp_buf_len] = resp;
246 (*resp_buf_len)++;
247 buf_len--;
248 }
249 }
250 return ret;
251 }
252 }
253 };
254
255 return -EIO;
256}
257
258int mbox_init(void)
259{
260 int ret;
261
262 /* enable mailbox interrupts */
263 MBOX_WRITEL(MBOX_ALL_INTRS, MBOX_FLAGS);
264
265 /* Ensure urgent request is cleared */
266 MBOX_WRITEL(0, MBOX_URG);
267
268 /* Ensure the Doorbell Interrupt is cleared */
269 MBOX_WRITEL(0, MBOX_DOORBELL_FROM_SDM);
270
271 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_RESTART, MBOX_CMD_DIRECT, 0,
272 NULL, 1, 0, NULL);
273 if (ret)
274 return ret;
275
276 /* Renable mailbox interrupts after MBOX_RESTART */
277 MBOX_WRITEL(MBOX_ALL_INTRS, MBOX_FLAGS);
278
279 return 0;
280}
281
282#ifdef CONFIG_CADENCE_QSPI
283int mbox_qspi_close(void)
284{
285 return mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_CLOSE, MBOX_CMD_DIRECT,
286 0, NULL, 0, 0, NULL);
287}
288
289int mbox_qspi_open(void)
290{
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800291 int ret;
292 u32 resp_buf[1];
293 u32 resp_buf_len;
294
295 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_OPEN, MBOX_CMD_DIRECT,
296 0, NULL, 0, 0, NULL);
297 if (ret) {
298 /* retry again by closing and reopen the QSPI again */
299 ret = mbox_qspi_close();
300 if (ret)
301 return ret;
302
303 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_OPEN,
304 MBOX_CMD_DIRECT, 0, NULL, 0, 0, NULL);
305 if (ret)
306 return ret;
307 }
308
309 /* HPS will directly control the QSPI controller, no longer mailbox */
310 resp_buf_len = 1;
311 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_DIRECT, MBOX_CMD_DIRECT,
312 0, NULL, 0, (u32 *)&resp_buf_len,
313 (u32 *)&resp_buf);
314 if (ret)
315 goto error;
316
317 /* We are getting QSPI ref clock and set into sysmgr boot register */
318 printf("QSPI: Reference clock at %d Hz\n", resp_buf[0]);
Ley Foon Tan3d3a8602019-11-08 10:38:20 +0800319 writel(resp_buf[0],
Ley Foon Tan0b1680e2019-11-27 15:55:18 +0800320 socfpga_get_sysmgr_addr() + SYSMGR_SOC64_BOOT_SCRATCH_COLD0);
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800321
322 return 0;
323
324error:
325 mbox_qspi_close();
326
327 return ret;
328}
329#endif /* CONFIG_CADENCE_QSPI */
330
331int mbox_reset_cold(void)
332{
333 int ret;
334
335 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_REBOOT_HPS, MBOX_CMD_DIRECT,
336 0, NULL, 0, 0, NULL);
337 if (ret) {
338 /* mailbox sent failure, wait for watchdog to kick in */
339 hang();
340 }
341 return 0;
342}
343
Ang, Chee Hong31b79632018-12-19 18:35:12 -0800344/* Accepted commands: CONFIG_STATUS or RECONFIG_STATUS */
345static __always_inline int mbox_get_fpga_config_status_common(u32 cmd)
346{
347 u32 reconfig_status_resp_len;
348 u32 reconfig_status_resp[RECONFIG_STATUS_RESPONSE_LEN];
349 int ret;
350
351 reconfig_status_resp_len = RECONFIG_STATUS_RESPONSE_LEN;
352 ret = mbox_send_cmd_common(MBOX_ID_UBOOT, cmd,
353 MBOX_CMD_DIRECT, 0, NULL, 0,
354 &reconfig_status_resp_len,
355 reconfig_status_resp);
356
357 if (ret)
358 return ret;
359
360 /* Check for any error */
361 ret = reconfig_status_resp[RECONFIG_STATUS_STATE];
362 if (ret && ret != MBOX_CFGSTAT_STATE_CONFIG)
363 return ret;
364
365 /* Make sure nStatus is not 0 */
366 ret = reconfig_status_resp[RECONFIG_STATUS_PIN_STATUS];
367 if (!(ret & RCF_PIN_STATUS_NSTATUS))
368 return MBOX_CFGSTAT_STATE_ERROR_HARDWARE;
369
370 ret = reconfig_status_resp[RECONFIG_STATUS_SOFTFUNC_STATUS];
371 if (ret & RCF_SOFTFUNC_STATUS_SEU_ERROR)
372 return MBOX_CFGSTAT_STATE_ERROR_HARDWARE;
373
374 if ((ret & RCF_SOFTFUNC_STATUS_CONF_DONE) &&
375 (ret & RCF_SOFTFUNC_STATUS_INIT_DONE) &&
376 !reconfig_status_resp[RECONFIG_STATUS_STATE])
377 return 0; /* configuration success */
378
379 return MBOX_CFGSTAT_STATE_CONFIG;
380}
381
382int mbox_get_fpga_config_status(u32 cmd)
383{
384 return mbox_get_fpga_config_status_common(cmd);
385}
386
387int __secure mbox_get_fpga_config_status_psci(u32 cmd)
388{
389 return mbox_get_fpga_config_status_common(cmd);
390}
391
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800392int mbox_send_cmd(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg,
393 u8 urgent, u32 *resp_buf_len, u32 *resp_buf)
394{
395 return mbox_send_cmd_common(id, cmd, is_indirect, len, arg, urgent,
396 resp_buf_len, resp_buf);
397}
398
399int __secure mbox_send_cmd_psci(u8 id, u32 cmd, u8 is_indirect, u32 len,
400 u32 *arg, u8 urgent, u32 *resp_buf_len,
401 u32 *resp_buf)
402{
403 return mbox_send_cmd_common(id, cmd, is_indirect, len, arg, urgent,
404 resp_buf_len, resp_buf);
405}
406
407int mbox_send_cmd_only(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg)
408{
409 return mbox_send_cmd_only_common(id, cmd, is_indirect, len, arg);
410}
411
412int __secure mbox_send_cmd_only_psci(u8 id, u32 cmd, u8 is_indirect, u32 len,
413 u32 *arg)
414{
415 return mbox_send_cmd_only_common(id, cmd, is_indirect, len, arg);
416}
417
418int mbox_rcv_resp(u32 *resp_buf, u32 resp_buf_max_len)
419{
420 return __mbox_rcv_resp(resp_buf, resp_buf_max_len);
421}
422
423int __secure mbox_rcv_resp_psci(u32 *resp_buf, u32 resp_buf_max_len)
424{
425 return __mbox_rcv_resp(resp_buf, resp_buf_max_len);
426}