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