blob: 3c332239361d1c27dda4624b91ab875d83d58229 [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
58 * to fit the data
59 */
60 if (((cin + 1) % MBOX_CMD_BUFFER_SIZE) == cout ||
61 ((MBOX_CMD_BUFFER_SIZE - cin + cout - 1) %
62 MBOX_CMD_BUFFER_SIZE) < len)
63 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{
290 static const struct socfpga_system_manager *sysmgr_regs =
291 (struct socfpga_system_manager *)SOCFPGA_SYSMGR_ADDRESS;
292
293 int ret;
294 u32 resp_buf[1];
295 u32 resp_buf_len;
296
297 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_OPEN, MBOX_CMD_DIRECT,
298 0, NULL, 0, 0, NULL);
299 if (ret) {
300 /* retry again by closing and reopen the QSPI again */
301 ret = mbox_qspi_close();
302 if (ret)
303 return ret;
304
305 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_OPEN,
306 MBOX_CMD_DIRECT, 0, NULL, 0, 0, NULL);
307 if (ret)
308 return ret;
309 }
310
311 /* HPS will directly control the QSPI controller, no longer mailbox */
312 resp_buf_len = 1;
313 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_QSPI_DIRECT, MBOX_CMD_DIRECT,
314 0, NULL, 0, (u32 *)&resp_buf_len,
315 (u32 *)&resp_buf);
316 if (ret)
317 goto error;
318
319 /* We are getting QSPI ref clock and set into sysmgr boot register */
320 printf("QSPI: Reference clock at %d Hz\n", resp_buf[0]);
321 writel(resp_buf[0], &sysmgr_regs->boot_scratch_cold0);
322
323 return 0;
324
325error:
326 mbox_qspi_close();
327
328 return ret;
329}
330#endif /* CONFIG_CADENCE_QSPI */
331
332int mbox_reset_cold(void)
333{
334 int ret;
335
336 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_REBOOT_HPS, MBOX_CMD_DIRECT,
337 0, NULL, 0, 0, NULL);
338 if (ret) {
339 /* mailbox sent failure, wait for watchdog to kick in */
340 hang();
341 }
342 return 0;
343}
344
Ang, Chee Hong31b79632018-12-19 18:35:12 -0800345/* Accepted commands: CONFIG_STATUS or RECONFIG_STATUS */
346static __always_inline int mbox_get_fpga_config_status_common(u32 cmd)
347{
348 u32 reconfig_status_resp_len;
349 u32 reconfig_status_resp[RECONFIG_STATUS_RESPONSE_LEN];
350 int ret;
351
352 reconfig_status_resp_len = RECONFIG_STATUS_RESPONSE_LEN;
353 ret = mbox_send_cmd_common(MBOX_ID_UBOOT, cmd,
354 MBOX_CMD_DIRECT, 0, NULL, 0,
355 &reconfig_status_resp_len,
356 reconfig_status_resp);
357
358 if (ret)
359 return ret;
360
361 /* Check for any error */
362 ret = reconfig_status_resp[RECONFIG_STATUS_STATE];
363 if (ret && ret != MBOX_CFGSTAT_STATE_CONFIG)
364 return ret;
365
366 /* Make sure nStatus is not 0 */
367 ret = reconfig_status_resp[RECONFIG_STATUS_PIN_STATUS];
368 if (!(ret & RCF_PIN_STATUS_NSTATUS))
369 return MBOX_CFGSTAT_STATE_ERROR_HARDWARE;
370
371 ret = reconfig_status_resp[RECONFIG_STATUS_SOFTFUNC_STATUS];
372 if (ret & RCF_SOFTFUNC_STATUS_SEU_ERROR)
373 return MBOX_CFGSTAT_STATE_ERROR_HARDWARE;
374
375 if ((ret & RCF_SOFTFUNC_STATUS_CONF_DONE) &&
376 (ret & RCF_SOFTFUNC_STATUS_INIT_DONE) &&
377 !reconfig_status_resp[RECONFIG_STATUS_STATE])
378 return 0; /* configuration success */
379
380 return MBOX_CFGSTAT_STATE_CONFIG;
381}
382
383int mbox_get_fpga_config_status(u32 cmd)
384{
385 return mbox_get_fpga_config_status_common(cmd);
386}
387
388int __secure mbox_get_fpga_config_status_psci(u32 cmd)
389{
390 return mbox_get_fpga_config_status_common(cmd);
391}
392
Ley Foon Tane5b6a662018-05-24 00:17:25 +0800393int mbox_send_cmd(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg,
394 u8 urgent, u32 *resp_buf_len, u32 *resp_buf)
395{
396 return mbox_send_cmd_common(id, cmd, is_indirect, len, arg, urgent,
397 resp_buf_len, resp_buf);
398}
399
400int __secure mbox_send_cmd_psci(u8 id, u32 cmd, u8 is_indirect, u32 len,
401 u32 *arg, u8 urgent, u32 *resp_buf_len,
402 u32 *resp_buf)
403{
404 return mbox_send_cmd_common(id, cmd, is_indirect, len, arg, urgent,
405 resp_buf_len, resp_buf);
406}
407
408int mbox_send_cmd_only(u8 id, u32 cmd, u8 is_indirect, u32 len, u32 *arg)
409{
410 return mbox_send_cmd_only_common(id, cmd, is_indirect, len, arg);
411}
412
413int __secure mbox_send_cmd_only_psci(u8 id, u32 cmd, u8 is_indirect, u32 len,
414 u32 *arg)
415{
416 return mbox_send_cmd_only_common(id, cmd, is_indirect, len, arg);
417}
418
419int mbox_rcv_resp(u32 *resp_buf, u32 resp_buf_max_len)
420{
421 return __mbox_rcv_resp(resp_buf, resp_buf_max_len);
422}
423
424int __secure mbox_rcv_resp_psci(u32 *resp_buf, u32 resp_buf_max_len)
425{
426 return __mbox_rcv_resp(resp_buf, resp_buf_max_len);
427}