// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2018 Intel Corporation */ #include #include #include #define RECONFIG_STATUS_POLL_RESP_TIMEOUT_MS 60000 #define RECONFIG_STATUS_INTERVAL_DELAY_US 1000000 static const struct mbox_cfgstat_state { int err_no; const char *error_name; } mbox_cfgstat_state[] = { {MBOX_CFGSTAT_STATE_IDLE, "FPGA in idle mode."}, {MBOX_CFGSTAT_STATE_CONFIG, "FPGA in config mode."}, {MBOX_CFGSTAT_STATE_FAILACK, "Acknowledgment failed!"}, {MBOX_CFGSTAT_STATE_ERROR_INVALID, "Invalid bitstream!"}, {MBOX_CFGSTAT_STATE_ERROR_CORRUPT, "Corrupted bitstream!"}, {MBOX_CFGSTAT_STATE_ERROR_AUTH, "Authentication failed!"}, {MBOX_CFGSTAT_STATE_ERROR_CORE_IO, "I/O error!"}, {MBOX_CFGSTAT_STATE_ERROR_HARDWARE, "Hardware error!"}, {MBOX_CFGSTAT_STATE_ERROR_FAKE, "Fake error!"}, {MBOX_CFGSTAT_STATE_ERROR_BOOT_INFO, "Error in boot info!"}, {MBOX_CFGSTAT_STATE_ERROR_QSPI_ERROR, "Error in QSPI!"}, {MBOX_RESP_ERROR, "Mailbox general error!"}, {-ETIMEDOUT, "I/O timeout error"}, {-1, "Unknown error!"} }; #define MBOX_CFGSTAT_MAX ARRAY_SIZE(mbox_cfgstat_state) static const char *mbox_cfgstat_to_str(int err) { int i; for (i = 0; i < MBOX_CFGSTAT_MAX - 1; i++) { if (mbox_cfgstat_state[i].err_no == err) return mbox_cfgstat_state[i].error_name; } return mbox_cfgstat_state[MBOX_CFGSTAT_MAX - 1].error_name; } /* * Add the ongoing transaction's command ID into pending list and return * the command ID for next transfer. */ static u8 add_transfer(u32 *xfer_pending_list, size_t list_size, u8 id) { int i; for (i = 0; i < list_size; i++) { if (xfer_pending_list[i]) continue; xfer_pending_list[i] = id; debug("ID(%d) added to transaction pending list\n", id); /* * Increment command ID for next transaction. * Valid command ID (4 bits) is from 1 to 15. */ id = (id % 15) + 1; break; } return id; } /* * Check whether response ID match the command ID in the transfer * pending list. If a match is found in the transfer pending list, * it clears the transfer pending list and return the matched * command ID. */ static int get_and_clr_transfer(u32 *xfer_pending_list, size_t list_size, u8 id) { int i; for (i = 0; i < list_size; i++) { if (id != xfer_pending_list[i]) continue; xfer_pending_list[i] = 0; return id; } return 0; } /* * Polling the FPGA configuration status. * Return 0 for success, non-zero for error. */ static int reconfig_status_polling_resp(void) { int ret; unsigned long start = get_timer(0); while (1) { ret = mbox_get_fpga_config_status(MBOX_RECONFIG_STATUS); if (!ret) return 0; /* configuration success */ if (ret != MBOX_CFGSTAT_STATE_CONFIG) return ret; if (get_timer(start) > RECONFIG_STATUS_POLL_RESP_TIMEOUT_MS) break; /* time out */ puts("."); udelay(RECONFIG_STATUS_INTERVAL_DELAY_US); } return -ETIMEDOUT; } static u32 get_resp_hdr(u32 *r_index, u32 *w_index, u32 *resp_count, u32 *resp_buf, u32 buf_size, u32 client_id) { u32 buf[MBOX_RESP_BUFFER_SIZE]; u32 mbox_hdr; u32 resp_len; u32 hdr_len; u32 i; if (*resp_count < buf_size) { u32 rcv_len_max = buf_size - *resp_count; if (rcv_len_max > MBOX_RESP_BUFFER_SIZE) rcv_len_max = MBOX_RESP_BUFFER_SIZE; resp_len = mbox_rcv_resp(buf, rcv_len_max); for (i = 0; i < resp_len; i++) { resp_buf[(*w_index)++] = buf[i]; *w_index %= buf_size; (*resp_count)++; } } /* No response in buffer */ if (*resp_count == 0) return 0; mbox_hdr = resp_buf[*r_index]; hdr_len = MBOX_RESP_LEN_GET(mbox_hdr); /* Insufficient header length to return a mailbox header */ if ((*resp_count - 1) < hdr_len) return 0; *r_index += (hdr_len + 1); *r_index %= buf_size; *resp_count -= (hdr_len + 1); /* Make sure response belongs to us */ if (MBOX_RESP_CLIENT_GET(mbox_hdr) != client_id) return 0; return mbox_hdr; } /* Send bit stream data to SDM via RECONFIG_DATA mailbox command */ static int send_reconfig_data(const void *rbf_data, size_t rbf_size, u32 xfer_max, u32 buf_size_max) { u32 response_buffer[MBOX_RESP_BUFFER_SIZE]; u32 xfer_pending[MBOX_RESP_BUFFER_SIZE]; u32 resp_rindex = 0; u32 resp_windex = 0; u32 resp_count = 0; u32 xfer_count = 0; int resp_err = 0; u8 cmd_id = 1; u32 args[3]; int ret; debug("SDM xfer_max = %d\n", xfer_max); debug("SDM buf_size_max = %x\n\n", buf_size_max); memset(xfer_pending, 0, sizeof(xfer_pending)); while (rbf_size || xfer_count) { if (!resp_err && rbf_size && xfer_count < xfer_max) { args[0] = MBOX_ARG_DESC_COUNT(1); args[1] = (u64)rbf_data; if (rbf_size >= buf_size_max) { args[2] = buf_size_max; rbf_size -= buf_size_max; rbf_data += buf_size_max; } else { args[2] = (u64)rbf_size; rbf_size = 0; } resp_err = mbox_send_cmd_only(cmd_id, MBOX_RECONFIG_DATA, MBOX_CMD_INDIRECT, 3, args); if (!resp_err) { xfer_count++; cmd_id = add_transfer(xfer_pending, MBOX_RESP_BUFFER_SIZE, cmd_id); } puts("."); } else { u32 resp_hdr = get_resp_hdr(&resp_rindex, &resp_windex, &resp_count, response_buffer, MBOX_RESP_BUFFER_SIZE, MBOX_CLIENT_ID_UBOOT); /* * If no valid response header found or * non-zero length from RECONFIG_DATA */ if (!resp_hdr || MBOX_RESP_LEN_GET(resp_hdr)) continue; /* Check for response's status */ if (!resp_err) { resp_err = MBOX_RESP_ERR_GET(resp_hdr); debug("Response error code: %08x\n", resp_err); } ret = get_and_clr_transfer(xfer_pending, MBOX_RESP_BUFFER_SIZE, MBOX_RESP_ID_GET(resp_hdr)); if (ret) { /* Claim and reuse the ID */ cmd_id = (u8)ret; xfer_count--; } if (resp_err && !xfer_count) return resp_err; } } return 0; } /* * This is the interface used by FPGA driver. * Return 0 for success, non-zero for error. */ int stratix10_load(Altera_desc *desc, const void *rbf_data, size_t rbf_size) { int ret; u32 resp_len = 2; u32 resp_buf[2]; debug("Sending MBOX_RECONFIG...\n"); ret = mbox_send_cmd(MBOX_ID_UBOOT, MBOX_RECONFIG, MBOX_CMD_DIRECT, 0, NULL, 0, &resp_len, resp_buf); if (ret) { puts("Failure in RECONFIG mailbox command!\n"); return ret; } ret = send_reconfig_data(rbf_data, rbf_size, resp_buf[0], resp_buf[1]); if (ret) { printf("RECONFIG_DATA error: %08x, %s\n", ret, mbox_cfgstat_to_str(ret)); return ret; } /* Make sure we don't send MBOX_RECONFIG_STATUS too fast */ udelay(RECONFIG_STATUS_INTERVAL_DELAY_US); debug("Polling with MBOX_RECONFIG_STATUS...\n"); ret = reconfig_status_polling_resp(); if (ret) { printf("RECONFIG_STATUS Error: %08x, %s\n", ret, mbox_cfgstat_to_str(ret)); return ret; } puts("FPGA reconfiguration OK!\n"); return ret; }