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