blob: 9a1dc2c0c83175f80b0ba06be063a5a3d9109bde [file] [log] [blame]
Ang, Chee Hongc41e6602018-12-19 18:35:14 -08001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2018 Intel Corporation <www.intel.com>
4 */
5
6#include <common.h>
7#include <altera.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -06008#include <log.h>
Chee Hong Ang9a623cd2020-08-07 11:50:04 +08009#include <watchdog.h>
Ang, Chee Hongc41e6602018-12-19 18:35:14 -080010#include <asm/arch/mailbox_s10.h>
Simon Glassc05ed002020-05-10 11:40:11 -060011#include <linux/delay.h>
Ang, Chee Hongc41e6602018-12-19 18:35:14 -080012
13#define RECONFIG_STATUS_POLL_RESP_TIMEOUT_MS 60000
14#define RECONFIG_STATUS_INTERVAL_DELAY_US 1000000
15
16static const struct mbox_cfgstat_state {
17 int err_no;
18 const char *error_name;
19} mbox_cfgstat_state[] = {
20 {MBOX_CFGSTAT_STATE_IDLE, "FPGA in idle mode."},
21 {MBOX_CFGSTAT_STATE_CONFIG, "FPGA in config mode."},
22 {MBOX_CFGSTAT_STATE_FAILACK, "Acknowledgment failed!"},
23 {MBOX_CFGSTAT_STATE_ERROR_INVALID, "Invalid bitstream!"},
24 {MBOX_CFGSTAT_STATE_ERROR_CORRUPT, "Corrupted bitstream!"},
25 {MBOX_CFGSTAT_STATE_ERROR_AUTH, "Authentication failed!"},
26 {MBOX_CFGSTAT_STATE_ERROR_CORE_IO, "I/O error!"},
27 {MBOX_CFGSTAT_STATE_ERROR_HARDWARE, "Hardware error!"},
28 {MBOX_CFGSTAT_STATE_ERROR_FAKE, "Fake error!"},
29 {MBOX_CFGSTAT_STATE_ERROR_BOOT_INFO, "Error in boot info!"},
30 {MBOX_CFGSTAT_STATE_ERROR_QSPI_ERROR, "Error in QSPI!"},
31 {MBOX_RESP_ERROR, "Mailbox general error!"},
32 {-ETIMEDOUT, "I/O timeout error"},
33 {-1, "Unknown error!"}
34};
35
36#define MBOX_CFGSTAT_MAX ARRAY_SIZE(mbox_cfgstat_state)
37
38static const char *mbox_cfgstat_to_str(int err)
39{
40 int i;
41
42 for (i = 0; i < MBOX_CFGSTAT_MAX - 1; i++) {
43 if (mbox_cfgstat_state[i].err_no == err)
44 return mbox_cfgstat_state[i].error_name;
45 }
46
47 return mbox_cfgstat_state[MBOX_CFGSTAT_MAX - 1].error_name;
48}
49
50/*
51 * Add the ongoing transaction's command ID into pending list and return
52 * the command ID for next transfer.
53 */
54static u8 add_transfer(u32 *xfer_pending_list, size_t list_size, u8 id)
55{
56 int i;
57
58 for (i = 0; i < list_size; i++) {
59 if (xfer_pending_list[i])
60 continue;
61 xfer_pending_list[i] = id;
62 debug("ID(%d) added to transaction pending list\n", id);
63 /*
64 * Increment command ID for next transaction.
65 * Valid command ID (4 bits) is from 1 to 15.
66 */
67 id = (id % 15) + 1;
68 break;
69 }
70
71 return id;
72}
73
74/*
75 * Check whether response ID match the command ID in the transfer
76 * pending list. If a match is found in the transfer pending list,
77 * it clears the transfer pending list and return the matched
78 * command ID.
79 */
80static int get_and_clr_transfer(u32 *xfer_pending_list, size_t list_size,
81 u8 id)
82{
83 int i;
84
85 for (i = 0; i < list_size; i++) {
86 if (id != xfer_pending_list[i])
87 continue;
88 xfer_pending_list[i] = 0;
89 return id;
90 }
91
92 return 0;
93}
94
95/*
96 * Polling the FPGA configuration status.
97 * Return 0 for success, non-zero for error.
98 */
99static int reconfig_status_polling_resp(void)
100{
101 int ret;
102 unsigned long start = get_timer(0);
103
104 while (1) {
105 ret = mbox_get_fpga_config_status(MBOX_RECONFIG_STATUS);
106 if (!ret)
107 return 0; /* configuration success */
108
109 if (ret != MBOX_CFGSTAT_STATE_CONFIG)
110 return ret;
111
112 if (get_timer(start) > RECONFIG_STATUS_POLL_RESP_TIMEOUT_MS)
113 break; /* time out */
114
115 puts(".");
116 udelay(RECONFIG_STATUS_INTERVAL_DELAY_US);
Chee Hong Ang9a623cd2020-08-07 11:50:04 +0800117 WATCHDOG_RESET();
Ang, Chee Hongc41e6602018-12-19 18:35:14 -0800118 }
119
120 return -ETIMEDOUT;
121}
122
123static u32 get_resp_hdr(u32 *r_index, u32 *w_index, u32 *resp_count,
124 u32 *resp_buf, u32 buf_size, u32 client_id)
125{
126 u32 buf[MBOX_RESP_BUFFER_SIZE];
127 u32 mbox_hdr;
128 u32 resp_len;
129 u32 hdr_len;
130 u32 i;
131
132 if (*resp_count < buf_size) {
133 u32 rcv_len_max = buf_size - *resp_count;
134
135 if (rcv_len_max > MBOX_RESP_BUFFER_SIZE)
136 rcv_len_max = MBOX_RESP_BUFFER_SIZE;
137 resp_len = mbox_rcv_resp(buf, rcv_len_max);
138
139 for (i = 0; i < resp_len; i++) {
140 resp_buf[(*w_index)++] = buf[i];
141 *w_index %= buf_size;
142 (*resp_count)++;
143 }
144 }
145
146 /* No response in buffer */
147 if (*resp_count == 0)
148 return 0;
149
150 mbox_hdr = resp_buf[*r_index];
151
152 hdr_len = MBOX_RESP_LEN_GET(mbox_hdr);
153
154 /* Insufficient header length to return a mailbox header */
155 if ((*resp_count - 1) < hdr_len)
156 return 0;
157
158 *r_index += (hdr_len + 1);
159 *r_index %= buf_size;
160 *resp_count -= (hdr_len + 1);
161
162 /* Make sure response belongs to us */
163 if (MBOX_RESP_CLIENT_GET(mbox_hdr) != client_id)
164 return 0;
165
166 return mbox_hdr;
167}
168
169/* Send bit stream data to SDM via RECONFIG_DATA mailbox command */
170static int send_reconfig_data(const void *rbf_data, size_t rbf_size,
171 u32 xfer_max, u32 buf_size_max)
172{
173 u32 response_buffer[MBOX_RESP_BUFFER_SIZE];
174 u32 xfer_pending[MBOX_RESP_BUFFER_SIZE];
175 u32 resp_rindex = 0;
176 u32 resp_windex = 0;
177 u32 resp_count = 0;
178 u32 xfer_count = 0;
Ang, Chee Hong5097ba62019-02-17 20:07:50 -0800179 int resp_err = 0;
Ang, Chee Hongc41e6602018-12-19 18:35:14 -0800180 u8 cmd_id = 1;
181 u32 args[3];
182 int ret;
183
184 debug("SDM xfer_max = %d\n", xfer_max);
185 debug("SDM buf_size_max = %x\n\n", buf_size_max);
186
187 memset(xfer_pending, 0, sizeof(xfer_pending));
188
189 while (rbf_size || xfer_count) {
190 if (!resp_err && rbf_size && xfer_count < xfer_max) {
191 args[0] = MBOX_ARG_DESC_COUNT(1);
192 args[1] = (u64)rbf_data;
193 if (rbf_size >= buf_size_max) {
194 args[2] = buf_size_max;
195 rbf_size -= buf_size_max;
196 rbf_data += buf_size_max;
197 } else {
198 args[2] = (u64)rbf_size;
199 rbf_size = 0;
200 }
201
Ang, Chee Hong5097ba62019-02-17 20:07:50 -0800202 resp_err = mbox_send_cmd_only(cmd_id, MBOX_RECONFIG_DATA,
Ang, Chee Hongc41e6602018-12-19 18:35:14 -0800203 MBOX_CMD_INDIRECT, 3, args);
Ang, Chee Hong5097ba62019-02-17 20:07:50 -0800204 if (!resp_err) {
Ang, Chee Hongc41e6602018-12-19 18:35:14 -0800205 xfer_count++;
206 cmd_id = add_transfer(xfer_pending,
207 MBOX_RESP_BUFFER_SIZE,
208 cmd_id);
209 }
210 puts(".");
211 } else {
212 u32 resp_hdr = get_resp_hdr(&resp_rindex, &resp_windex,
213 &resp_count,
214 response_buffer,
215 MBOX_RESP_BUFFER_SIZE,
216 MBOX_CLIENT_ID_UBOOT);
217
218 /*
219 * If no valid response header found or
220 * non-zero length from RECONFIG_DATA
221 */
222 if (!resp_hdr || MBOX_RESP_LEN_GET(resp_hdr))
223 continue;
224
225 /* Check for response's status */
226 if (!resp_err) {
Ang, Chee Hong5097ba62019-02-17 20:07:50 -0800227 resp_err = MBOX_RESP_ERR_GET(resp_hdr);
228 debug("Response error code: %08x\n", resp_err);
Ang, Chee Hongc41e6602018-12-19 18:35:14 -0800229 }
230
231 ret = get_and_clr_transfer(xfer_pending,
232 MBOX_RESP_BUFFER_SIZE,
233 MBOX_RESP_ID_GET(resp_hdr));
234 if (ret) {
235 /* Claim and reuse the ID */
236 cmd_id = (u8)ret;
237 xfer_count--;
238 }
239
240 if (resp_err && !xfer_count)
Ang, Chee Hong5097ba62019-02-17 20:07:50 -0800241 return resp_err;
Ang, Chee Hongc41e6602018-12-19 18:35:14 -0800242 }
Chee Hong Ang9a623cd2020-08-07 11:50:04 +0800243 WATCHDOG_RESET();
Ang, Chee Hongc41e6602018-12-19 18:35:14 -0800244 }
245
246 return 0;
247}
248
249/*
250 * This is the interface used by FPGA driver.
251 * Return 0 for success, non-zero for error.
252 */
Chee Hong Angd2170162020-08-07 11:50:03 +0800253int intel_sdm_mb_load(Altera_desc *desc, const void *rbf_data, size_t rbf_size)
Ang, Chee Hongc41e6602018-12-19 18:35:14 -0800254{
255 int ret;
256 u32 resp_len = 2;
257 u32 resp_buf[2];
258
259 debug("Sending MBOX_RECONFIG...\n");
260 ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_RECONFIG, MBOX_CMD_DIRECT, 0,
261 NULL, 0, &resp_len, resp_buf);
262 if (ret) {
263 puts("Failure in RECONFIG mailbox command!\n");
264 return ret;
265 }
266
267 ret = send_reconfig_data(rbf_data, rbf_size, resp_buf[0], resp_buf[1]);
268 if (ret) {
269 printf("RECONFIG_DATA error: %08x, %s\n", ret,
270 mbox_cfgstat_to_str(ret));
271 return ret;
272 }
273
274 /* Make sure we don't send MBOX_RECONFIG_STATUS too fast */
275 udelay(RECONFIG_STATUS_INTERVAL_DELAY_US);
276
277 debug("Polling with MBOX_RECONFIG_STATUS...\n");
278 ret = reconfig_status_polling_resp();
279 if (ret) {
280 printf("RECONFIG_STATUS Error: %08x, %s\n", ret,
281 mbox_cfgstat_to_str(ret));
282 return ret;
283 }
284
285 puts("FPGA reconfiguration OK!\n");
286
287 return ret;
288}