blob: 45caef4f5c108e86ae35bcca3692692aae622403 [file] [log] [blame]
Ang, Chee Hongdcc3bb62018-12-19 18:35:14 -08001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2018 Intel Corporation <www.intel.com>
4 */
5
Ang, Chee Hongdcc3bb62018-12-19 18:35:14 -08006#include <altera.h>
Simon Glass0f2af882020-05-10 11:40:05 -06007#include <log.h>
Tom Rini10d301b2024-05-01 19:30:47 -06008#include <time.h>
Chee Hong Ang4e87fcd2020-08-07 11:50:04 +08009#include <watchdog.h>
Ang, Chee Hongdcc3bb62018-12-19 18:35:14 -080010#include <asm/arch/mailbox_s10.h>
Chee Hong Angec4c6792020-12-24 18:21:07 +080011#include <asm/arch/smc_api.h>
Simon Glassdbd79542020-05-10 11:40:11 -060012#include <linux/delay.h>
Tom Rini10d301b2024-05-01 19:30:47 -060013#include <linux/errno.h>
Chee Hong Angec4c6792020-12-24 18:21:07 +080014#include <linux/intel-smc.h>
Tom Rini10d301b2024-05-01 19:30:47 -060015#include <linux/string.h>
Ang, Chee Hongdcc3bb62018-12-19 18:35:14 -080016
17#define RECONFIG_STATUS_POLL_RESP_TIMEOUT_MS 60000
18#define RECONFIG_STATUS_INTERVAL_DELAY_US 1000000
19
Chee Hong Angec4c6792020-12-24 18:21:07 +080020#if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_ATF)
21
22#define BITSTREAM_CHUNK_SIZE 0xFFFF0
23#define RECONFIG_STATUS_POLL_RETRY_MAX 100
24
25/*
26 * Polling the FPGA configuration status.
27 * Return 0 for success, non-zero for error.
28 */
29static int reconfig_status_polling_resp(void)
30{
31 int ret;
32 unsigned long start = get_timer(0);
33
34 while (1) {
35 ret = invoke_smc(INTEL_SIP_SMC_FPGA_CONFIG_ISDONE, NULL, 0,
36 NULL, 0);
37
38 if (!ret)
39 return 0; /* configuration success */
40
41 if (ret != INTEL_SIP_SMC_STATUS_BUSY)
42 return ret;
43
44 if (get_timer(start) > RECONFIG_STATUS_POLL_RESP_TIMEOUT_MS)
45 return -ETIMEDOUT; /* time out */
46
47 puts(".");
48 udelay(RECONFIG_STATUS_INTERVAL_DELAY_US);
Stefan Roese80877fa2022-09-02 14:10:46 +020049 schedule();
Chee Hong Angec4c6792020-12-24 18:21:07 +080050 }
51
52 return -ETIMEDOUT;
53}
54
55static int send_bitstream(const void *rbf_data, size_t rbf_size)
56{
57 int i;
58 u64 res_buf[3];
59 u64 args[2];
60 u32 xfer_count = 0;
61 int ret, wr_ret = 0, retry = 0;
62 size_t buf_size = (rbf_size > BITSTREAM_CHUNK_SIZE) ?
63 BITSTREAM_CHUNK_SIZE : rbf_size;
64
65 while (rbf_size || xfer_count) {
66 if (!wr_ret && rbf_size) {
67 args[0] = (u64)rbf_data;
68 args[1] = buf_size;
69 wr_ret = invoke_smc(INTEL_SIP_SMC_FPGA_CONFIG_WRITE,
70 args, 2, NULL, 0);
71
72 debug("wr_ret = %d, rbf_data = %p, buf_size = %08lx\n",
73 wr_ret, rbf_data, buf_size);
74
75 if (wr_ret)
76 continue;
77
78 rbf_size -= buf_size;
79 rbf_data += buf_size;
80
81 if (buf_size >= rbf_size)
82 buf_size = rbf_size;
83
84 xfer_count++;
85 puts(".");
86 } else {
87 ret = invoke_smc(
88 INTEL_SIP_SMC_FPGA_CONFIG_COMPLETED_WRITE,
89 NULL, 0, res_buf, ARRAY_SIZE(res_buf));
90 if (!ret) {
91 for (i = 0; i < ARRAY_SIZE(res_buf); i++) {
92 if (!res_buf[i])
93 break;
94 xfer_count--;
95 wr_ret = 0;
96 retry = 0;
97 }
98 } else if (ret !=
99 INTEL_SIP_SMC_STATUS_BUSY)
100 return ret;
101 else if (!xfer_count)
102 return INTEL_SIP_SMC_STATUS_ERROR;
103
104 if (++retry >= RECONFIG_STATUS_POLL_RETRY_MAX)
105 return -ETIMEDOUT;
106
107 udelay(20000);
108 }
Stefan Roese80877fa2022-09-02 14:10:46 +0200109 schedule();
Chee Hong Angec4c6792020-12-24 18:21:07 +0800110 }
111
112 return 0;
113}
114
115/*
116 * This is the interface used by FPGA driver.
117 * Return 0 for success, non-zero for error.
118 */
119int intel_sdm_mb_load(Altera_desc *desc, const void *rbf_data, size_t rbf_size)
120{
121 int ret;
122 u64 arg = 1;
123
124 debug("Invoking FPGA_CONFIG_START...\n");
125
126 ret = invoke_smc(INTEL_SIP_SMC_FPGA_CONFIG_START, &arg, 1, NULL, 0);
127
128 if (ret) {
129 puts("Failure in RECONFIG mailbox command!\n");
130 return ret;
131 }
132
133 ret = send_bitstream(rbf_data, rbf_size);
134 if (ret) {
135 puts("Error sending bitstream!\n");
136 return ret;
137 }
138
139 /* Make sure we don't send MBOX_RECONFIG_STATUS too fast */
140 udelay(RECONFIG_STATUS_INTERVAL_DELAY_US);
141
142 debug("Polling with MBOX_RECONFIG_STATUS...\n");
143 ret = reconfig_status_polling_resp();
144 if (ret) {
145 puts("FPGA reconfiguration failed!");
146 return ret;
147 }
148
149 puts("FPGA reconfiguration OK!\n");
150
151 return ret;
152}
153
154#else
155
Ang, Chee Hongdcc3bb62018-12-19 18:35:14 -0800156static const struct mbox_cfgstat_state {
157 int err_no;
158 const char *error_name;
159} mbox_cfgstat_state[] = {
160 {MBOX_CFGSTAT_STATE_IDLE, "FPGA in idle mode."},
161 {MBOX_CFGSTAT_STATE_CONFIG, "FPGA in config mode."},
162 {MBOX_CFGSTAT_STATE_FAILACK, "Acknowledgment failed!"},
163 {MBOX_CFGSTAT_STATE_ERROR_INVALID, "Invalid bitstream!"},
164 {MBOX_CFGSTAT_STATE_ERROR_CORRUPT, "Corrupted bitstream!"},
165 {MBOX_CFGSTAT_STATE_ERROR_AUTH, "Authentication failed!"},
166 {MBOX_CFGSTAT_STATE_ERROR_CORE_IO, "I/O error!"},
167 {MBOX_CFGSTAT_STATE_ERROR_HARDWARE, "Hardware error!"},
168 {MBOX_CFGSTAT_STATE_ERROR_FAKE, "Fake error!"},
169 {MBOX_CFGSTAT_STATE_ERROR_BOOT_INFO, "Error in boot info!"},
170 {MBOX_CFGSTAT_STATE_ERROR_QSPI_ERROR, "Error in QSPI!"},
171 {MBOX_RESP_ERROR, "Mailbox general error!"},
172 {-ETIMEDOUT, "I/O timeout error"},
173 {-1, "Unknown error!"}
174};
175
176#define MBOX_CFGSTAT_MAX ARRAY_SIZE(mbox_cfgstat_state)
177
178static const char *mbox_cfgstat_to_str(int err)
179{
180 int i;
181
182 for (i = 0; i < MBOX_CFGSTAT_MAX - 1; i++) {
183 if (mbox_cfgstat_state[i].err_no == err)
184 return mbox_cfgstat_state[i].error_name;
185 }
186
187 return mbox_cfgstat_state[MBOX_CFGSTAT_MAX - 1].error_name;
188}
189
190/*
191 * Add the ongoing transaction's command ID into pending list and return
192 * the command ID for next transfer.
193 */
194static u8 add_transfer(u32 *xfer_pending_list, size_t list_size, u8 id)
195{
196 int i;
197
198 for (i = 0; i < list_size; i++) {
199 if (xfer_pending_list[i])
200 continue;
201 xfer_pending_list[i] = id;
202 debug("ID(%d) added to transaction pending list\n", id);
203 /*
204 * Increment command ID for next transaction.
205 * Valid command ID (4 bits) is from 1 to 15.
206 */
207 id = (id % 15) + 1;
208 break;
209 }
210
211 return id;
212}
213
214/*
215 * Check whether response ID match the command ID in the transfer
216 * pending list. If a match is found in the transfer pending list,
217 * it clears the transfer pending list and return the matched
218 * command ID.
219 */
220static int get_and_clr_transfer(u32 *xfer_pending_list, size_t list_size,
221 u8 id)
222{
223 int i;
224
225 for (i = 0; i < list_size; i++) {
226 if (id != xfer_pending_list[i])
227 continue;
228 xfer_pending_list[i] = 0;
229 return id;
230 }
231
232 return 0;
233}
234
235/*
236 * Polling the FPGA configuration status.
237 * Return 0 for success, non-zero for error.
238 */
239static int reconfig_status_polling_resp(void)
240{
241 int ret;
242 unsigned long start = get_timer(0);
243
244 while (1) {
245 ret = mbox_get_fpga_config_status(MBOX_RECONFIG_STATUS);
246 if (!ret)
247 return 0; /* configuration success */
248
249 if (ret != MBOX_CFGSTAT_STATE_CONFIG)
250 return ret;
251
252 if (get_timer(start) > RECONFIG_STATUS_POLL_RESP_TIMEOUT_MS)
253 break; /* time out */
254
255 puts(".");
256 udelay(RECONFIG_STATUS_INTERVAL_DELAY_US);
Stefan Roese80877fa2022-09-02 14:10:46 +0200257 schedule();
Ang, Chee Hongdcc3bb62018-12-19 18:35:14 -0800258 }
259
260 return -ETIMEDOUT;
261}
262
263static u32 get_resp_hdr(u32 *r_index, u32 *w_index, u32 *resp_count,
264 u32 *resp_buf, u32 buf_size, u32 client_id)
265{
266 u32 buf[MBOX_RESP_BUFFER_SIZE];
267 u32 mbox_hdr;
268 u32 resp_len;
269 u32 hdr_len;
270 u32 i;
271
272 if (*resp_count < buf_size) {
273 u32 rcv_len_max = buf_size - *resp_count;
274
275 if (rcv_len_max > MBOX_RESP_BUFFER_SIZE)
276 rcv_len_max = MBOX_RESP_BUFFER_SIZE;
277 resp_len = mbox_rcv_resp(buf, rcv_len_max);
278
279 for (i = 0; i < resp_len; i++) {
280 resp_buf[(*w_index)++] = buf[i];
281 *w_index %= buf_size;
282 (*resp_count)++;
283 }
284 }
285
286 /* No response in buffer */
287 if (*resp_count == 0)
288 return 0;
289
290 mbox_hdr = resp_buf[*r_index];
291
292 hdr_len = MBOX_RESP_LEN_GET(mbox_hdr);
293
294 /* Insufficient header length to return a mailbox header */
295 if ((*resp_count - 1) < hdr_len)
296 return 0;
297
298 *r_index += (hdr_len + 1);
299 *r_index %= buf_size;
300 *resp_count -= (hdr_len + 1);
301
302 /* Make sure response belongs to us */
303 if (MBOX_RESP_CLIENT_GET(mbox_hdr) != client_id)
304 return 0;
305
306 return mbox_hdr;
307}
308
309/* Send bit stream data to SDM via RECONFIG_DATA mailbox command */
310static int send_reconfig_data(const void *rbf_data, size_t rbf_size,
311 u32 xfer_max, u32 buf_size_max)
312{
313 u32 response_buffer[MBOX_RESP_BUFFER_SIZE];
314 u32 xfer_pending[MBOX_RESP_BUFFER_SIZE];
315 u32 resp_rindex = 0;
316 u32 resp_windex = 0;
317 u32 resp_count = 0;
318 u32 xfer_count = 0;
Ang, Chee Hongc4192f72019-02-17 20:07:50 -0800319 int resp_err = 0;
Ang, Chee Hongdcc3bb62018-12-19 18:35:14 -0800320 u8 cmd_id = 1;
321 u32 args[3];
322 int ret;
323
324 debug("SDM xfer_max = %d\n", xfer_max);
325 debug("SDM buf_size_max = %x\n\n", buf_size_max);
326
327 memset(xfer_pending, 0, sizeof(xfer_pending));
328
329 while (rbf_size || xfer_count) {
330 if (!resp_err && rbf_size && xfer_count < xfer_max) {
331 args[0] = MBOX_ARG_DESC_COUNT(1);
332 args[1] = (u64)rbf_data;
333 if (rbf_size >= buf_size_max) {
334 args[2] = buf_size_max;
335 rbf_size -= buf_size_max;
336 rbf_data += buf_size_max;
337 } else {
338 args[2] = (u64)rbf_size;
339 rbf_size = 0;
340 }
341
Ang, Chee Hongc4192f72019-02-17 20:07:50 -0800342 resp_err = mbox_send_cmd_only(cmd_id, MBOX_RECONFIG_DATA,
Ang, Chee Hongdcc3bb62018-12-19 18:35:14 -0800343 MBOX_CMD_INDIRECT, 3, args);
Ang, Chee Hongc4192f72019-02-17 20:07:50 -0800344 if (!resp_err) {
Ang, Chee Hongdcc3bb62018-12-19 18:35:14 -0800345 xfer_count++;
346 cmd_id = add_transfer(xfer_pending,
347 MBOX_RESP_BUFFER_SIZE,
348 cmd_id);
349 }
350 puts(".");
351 } else {
352 u32 resp_hdr = get_resp_hdr(&resp_rindex, &resp_windex,
353 &resp_count,
354 response_buffer,
355 MBOX_RESP_BUFFER_SIZE,
356 MBOX_CLIENT_ID_UBOOT);
357
358 /*
359 * If no valid response header found or
360 * non-zero length from RECONFIG_DATA
361 */
362 if (!resp_hdr || MBOX_RESP_LEN_GET(resp_hdr))
363 continue;
364
365 /* Check for response's status */
366 if (!resp_err) {
Ang, Chee Hongc4192f72019-02-17 20:07:50 -0800367 resp_err = MBOX_RESP_ERR_GET(resp_hdr);
368 debug("Response error code: %08x\n", resp_err);
Ang, Chee Hongdcc3bb62018-12-19 18:35:14 -0800369 }
370
371 ret = get_and_clr_transfer(xfer_pending,
372 MBOX_RESP_BUFFER_SIZE,
373 MBOX_RESP_ID_GET(resp_hdr));
374 if (ret) {
375 /* Claim and reuse the ID */
376 cmd_id = (u8)ret;
377 xfer_count--;
378 }
379
380 if (resp_err && !xfer_count)
Ang, Chee Hongc4192f72019-02-17 20:07:50 -0800381 return resp_err;
Ang, Chee Hongdcc3bb62018-12-19 18:35:14 -0800382 }
Stefan Roese80877fa2022-09-02 14:10:46 +0200383 schedule();
Ang, Chee Hongdcc3bb62018-12-19 18:35:14 -0800384 }
385
386 return 0;
387}
388
389/*
390 * This is the interface used by FPGA driver.
391 * Return 0 for success, non-zero for error.
392 */
Chee Hong Ang14192452020-08-07 11:50:03 +0800393int intel_sdm_mb_load(Altera_desc *desc, const void *rbf_data, size_t rbf_size)
Ang, Chee Hongdcc3bb62018-12-19 18:35:14 -0800394{
395 int ret;
396 u32 resp_len = 2;
397 u32 resp_buf[2];
398
399 debug("Sending MBOX_RECONFIG...\n");
400 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_RECONFIG, MBOX_CMD_DIRECT, 0,
401 NULL, 0, &resp_len, resp_buf);
402 if (ret) {
403 puts("Failure in RECONFIG mailbox command!\n");
404 return ret;
405 }
406
407 ret = send_reconfig_data(rbf_data, rbf_size, resp_buf[0], resp_buf[1]);
408 if (ret) {
409 printf("RECONFIG_DATA error: %08x, %s\n", ret,
410 mbox_cfgstat_to_str(ret));
411 return ret;
412 }
413
414 /* Make sure we don't send MBOX_RECONFIG_STATUS too fast */
415 udelay(RECONFIG_STATUS_INTERVAL_DELAY_US);
416
417 debug("Polling with MBOX_RECONFIG_STATUS...\n");
418 ret = reconfig_status_polling_resp();
419 if (ret) {
420 printf("RECONFIG_STATUS Error: %08x, %s\n", ret,
421 mbox_cfgstat_to_str(ret));
422 return ret;
423 }
424
425 puts("FPGA reconfiguration OK!\n");
426
427 return ret;
428}
Chee Hong Angec4c6792020-12-24 18:21:07 +0800429#endif