18c2ecf20Sopenharmony_ci 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/drivers/scsi/esas2r/esas2r_flash.c 48c2ecf20Sopenharmony_ci * For use with ATTO ExpressSAS R6xx SAS/SATA RAID controllers 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2001-2013 ATTO Technology, Inc. 78c2ecf20Sopenharmony_ci * (mailto:linuxdrivers@attotech.com) 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 108c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License 118c2ecf20Sopenharmony_ci * as published by the Free Software Foundation; either version 2 128c2ecf20Sopenharmony_ci * of the License, or (at your option) any later version. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, 158c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 168c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 178c2ecf20Sopenharmony_ci * GNU General Public License for more details. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * NO WARRANTY 208c2ecf20Sopenharmony_ci * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR 218c2ecf20Sopenharmony_ci * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT 228c2ecf20Sopenharmony_ci * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, 238c2ecf20Sopenharmony_ci * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is 248c2ecf20Sopenharmony_ci * solely responsible for determining the appropriateness of using and 258c2ecf20Sopenharmony_ci * distributing the Program and assumes all risks associated with its 268c2ecf20Sopenharmony_ci * exercise of rights under this Agreement, including but not limited to 278c2ecf20Sopenharmony_ci * the risks and costs of program errors, damage to or loss of data, 288c2ecf20Sopenharmony_ci * programs or equipment, and unavailability or interruption of operations. 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * DISCLAIMER OF LIABILITY 318c2ecf20Sopenharmony_ci * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY 328c2ecf20Sopenharmony_ci * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 338c2ecf20Sopenharmony_ci * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND 348c2ecf20Sopenharmony_ci * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 358c2ecf20Sopenharmony_ci * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 368c2ecf20Sopenharmony_ci * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED 378c2ecf20Sopenharmony_ci * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License 408c2ecf20Sopenharmony_ci * along with this program; if not, write to the Free Software 418c2ecf20Sopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 428c2ecf20Sopenharmony_ci * USA. 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#include "esas2r.h" 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* local macro defs */ 488c2ecf20Sopenharmony_ci#define esas2r_nvramcalc_cksum(n) \ 498c2ecf20Sopenharmony_ci (esas2r_calc_byte_cksum((u8 *)(n), sizeof(struct esas2r_sas_nvram), \ 508c2ecf20Sopenharmony_ci SASNVR_CKSUM_SEED)) 518c2ecf20Sopenharmony_ci#define esas2r_nvramcalc_xor_cksum(n) \ 528c2ecf20Sopenharmony_ci (esas2r_calc_byte_xor_cksum((u8 *)(n), \ 538c2ecf20Sopenharmony_ci sizeof(struct esas2r_sas_nvram), 0)) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define ESAS2R_FS_DRVR_VER 2 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic struct esas2r_sas_nvram default_sas_nvram = { 588c2ecf20Sopenharmony_ci { 'E', 'S', 'A', 'S' }, /* signature */ 598c2ecf20Sopenharmony_ci SASNVR_VERSION, /* version */ 608c2ecf20Sopenharmony_ci 0, /* checksum */ 618c2ecf20Sopenharmony_ci 31, /* max_lun_for_target */ 628c2ecf20Sopenharmony_ci SASNVR_PCILAT_MAX, /* pci_latency */ 638c2ecf20Sopenharmony_ci SASNVR1_BOOT_DRVR, /* options1 */ 648c2ecf20Sopenharmony_ci SASNVR2_HEARTBEAT | SASNVR2_SINGLE_BUS /* options2 */ 658c2ecf20Sopenharmony_ci | SASNVR2_SW_MUX_CTRL, 668c2ecf20Sopenharmony_ci SASNVR_COAL_DIS, /* int_coalescing */ 678c2ecf20Sopenharmony_ci SASNVR_CMDTHR_NONE, /* cmd_throttle */ 688c2ecf20Sopenharmony_ci 3, /* dev_wait_time */ 698c2ecf20Sopenharmony_ci 1, /* dev_wait_count */ 708c2ecf20Sopenharmony_ci 0, /* spin_up_delay */ 718c2ecf20Sopenharmony_ci 0, /* ssp_align_rate */ 728c2ecf20Sopenharmony_ci { 0x50, 0x01, 0x08, 0x60, /* sas_addr */ 738c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0x00 }, 748c2ecf20Sopenharmony_ci { SASNVR_SPEED_AUTO }, /* phy_speed */ 758c2ecf20Sopenharmony_ci { SASNVR_MUX_DISABLED }, /* SAS multiplexing */ 768c2ecf20Sopenharmony_ci { 0 }, /* phy_flags */ 778c2ecf20Sopenharmony_ci SASNVR_SORT_SAS_ADDR, /* sort_type */ 788c2ecf20Sopenharmony_ci 3, /* dpm_reqcmd_lmt */ 798c2ecf20Sopenharmony_ci 3, /* dpm_stndby_time */ 808c2ecf20Sopenharmony_ci 0, /* dpm_active_time */ 818c2ecf20Sopenharmony_ci { 0 }, /* phy_target_id */ 828c2ecf20Sopenharmony_ci SASNVR_VSMH_DISABLED, /* virt_ses_mode */ 838c2ecf20Sopenharmony_ci SASNVR_RWM_DEFAULT, /* read_write_mode */ 848c2ecf20Sopenharmony_ci 0, /* link down timeout */ 858c2ecf20Sopenharmony_ci { 0 } /* reserved */ 868c2ecf20Sopenharmony_ci}; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic u8 cmd_to_fls_func[] = { 898c2ecf20Sopenharmony_ci 0xFF, 908c2ecf20Sopenharmony_ci VDA_FLASH_READ, 918c2ecf20Sopenharmony_ci VDA_FLASH_BEGINW, 928c2ecf20Sopenharmony_ci VDA_FLASH_WRITE, 938c2ecf20Sopenharmony_ci VDA_FLASH_COMMIT, 948c2ecf20Sopenharmony_ci VDA_FLASH_CANCEL 958c2ecf20Sopenharmony_ci}; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic u8 esas2r_calc_byte_xor_cksum(u8 *addr, u32 len, u8 seed) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci u32 cksum = seed; 1008c2ecf20Sopenharmony_ci u8 *p = (u8 *)&cksum; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci while (len) { 1038c2ecf20Sopenharmony_ci if (((uintptr_t)addr & 3) == 0) 1048c2ecf20Sopenharmony_ci break; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci cksum = cksum ^ *addr; 1078c2ecf20Sopenharmony_ci addr++; 1088c2ecf20Sopenharmony_ci len--; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci while (len >= sizeof(u32)) { 1118c2ecf20Sopenharmony_ci cksum = cksum ^ *(u32 *)addr; 1128c2ecf20Sopenharmony_ci addr += 4; 1138c2ecf20Sopenharmony_ci len -= 4; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci while (len--) { 1168c2ecf20Sopenharmony_ci cksum = cksum ^ *addr; 1178c2ecf20Sopenharmony_ci addr++; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci return p[0] ^ p[1] ^ p[2] ^ p[3]; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic u8 esas2r_calc_byte_cksum(void *addr, u32 len, u8 seed) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci u8 *p = (u8 *)addr; 1258c2ecf20Sopenharmony_ci u8 cksum = seed; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci while (len--) 1288c2ecf20Sopenharmony_ci cksum = cksum + p[len]; 1298c2ecf20Sopenharmony_ci return cksum; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci/* Interrupt callback to process FM API write requests. */ 1338c2ecf20Sopenharmony_cistatic void esas2r_fmapi_callback(struct esas2r_adapter *a, 1348c2ecf20Sopenharmony_ci struct esas2r_request *rq) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct atto_vda_flash_req *vrq = &rq->vrq->flash; 1378c2ecf20Sopenharmony_ci struct esas2r_flash_context *fc = 1388c2ecf20Sopenharmony_ci (struct esas2r_flash_context *)rq->interrupt_cx; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (rq->req_stat == RS_SUCCESS) { 1418c2ecf20Sopenharmony_ci /* Last request was successful. See what to do now. */ 1428c2ecf20Sopenharmony_ci switch (vrq->sub_func) { 1438c2ecf20Sopenharmony_ci case VDA_FLASH_BEGINW: 1448c2ecf20Sopenharmony_ci if (fc->sgc.cur_offset == NULL) 1458c2ecf20Sopenharmony_ci goto commit; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci vrq->sub_func = VDA_FLASH_WRITE; 1488c2ecf20Sopenharmony_ci rq->req_stat = RS_PENDING; 1498c2ecf20Sopenharmony_ci break; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci case VDA_FLASH_WRITE: 1528c2ecf20Sopenharmony_cicommit: 1538c2ecf20Sopenharmony_ci vrq->sub_func = VDA_FLASH_COMMIT; 1548c2ecf20Sopenharmony_ci rq->req_stat = RS_PENDING; 1558c2ecf20Sopenharmony_ci rq->interrupt_cb = fc->interrupt_cb; 1568c2ecf20Sopenharmony_ci break; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci default: 1598c2ecf20Sopenharmony_ci break; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (rq->req_stat != RS_PENDING) 1648c2ecf20Sopenharmony_ci /* 1658c2ecf20Sopenharmony_ci * All done. call the real callback to complete the FM API 1668c2ecf20Sopenharmony_ci * request. We should only get here if a BEGINW or WRITE 1678c2ecf20Sopenharmony_ci * operation failed. 1688c2ecf20Sopenharmony_ci */ 1698c2ecf20Sopenharmony_ci (*fc->interrupt_cb)(a, rq); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci/* 1738c2ecf20Sopenharmony_ci * Build a flash request based on the flash context. The request status 1748c2ecf20Sopenharmony_ci * is filled in on an error. 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_cistatic void build_flash_msg(struct esas2r_adapter *a, 1778c2ecf20Sopenharmony_ci struct esas2r_request *rq) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct esas2r_flash_context *fc = 1808c2ecf20Sopenharmony_ci (struct esas2r_flash_context *)rq->interrupt_cx; 1818c2ecf20Sopenharmony_ci struct esas2r_sg_context *sgc = &fc->sgc; 1828c2ecf20Sopenharmony_ci u8 cksum = 0; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* calculate the checksum */ 1858c2ecf20Sopenharmony_ci if (fc->func == VDA_FLASH_BEGINW) { 1868c2ecf20Sopenharmony_ci if (sgc->cur_offset) 1878c2ecf20Sopenharmony_ci cksum = esas2r_calc_byte_xor_cksum(sgc->cur_offset, 1888c2ecf20Sopenharmony_ci sgc->length, 1898c2ecf20Sopenharmony_ci 0); 1908c2ecf20Sopenharmony_ci rq->interrupt_cb = esas2r_fmapi_callback; 1918c2ecf20Sopenharmony_ci } else { 1928c2ecf20Sopenharmony_ci rq->interrupt_cb = fc->interrupt_cb; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci esas2r_build_flash_req(a, 1958c2ecf20Sopenharmony_ci rq, 1968c2ecf20Sopenharmony_ci fc->func, 1978c2ecf20Sopenharmony_ci cksum, 1988c2ecf20Sopenharmony_ci fc->flsh_addr, 1998c2ecf20Sopenharmony_ci sgc->length); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci esas2r_rq_free_sg_lists(rq, a); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* 2048c2ecf20Sopenharmony_ci * remember the length we asked for. we have to keep track of 2058c2ecf20Sopenharmony_ci * the current amount done so we know how much to compare when 2068c2ecf20Sopenharmony_ci * doing the verification phase. 2078c2ecf20Sopenharmony_ci */ 2088c2ecf20Sopenharmony_ci fc->curr_len = fc->sgc.length; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (sgc->cur_offset) { 2118c2ecf20Sopenharmony_ci /* setup the S/G context to build the S/G table */ 2128c2ecf20Sopenharmony_ci esas2r_sgc_init(sgc, a, rq, &rq->vrq->flash.data.sge[0]); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (!esas2r_build_sg_list(a, rq, sgc)) { 2158c2ecf20Sopenharmony_ci rq->req_stat = RS_BUSY; 2168c2ecf20Sopenharmony_ci return; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci } else { 2198c2ecf20Sopenharmony_ci fc->sgc.length = 0; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* update the flsh_addr to the next one to write to */ 2238c2ecf20Sopenharmony_ci fc->flsh_addr += fc->curr_len; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci/* determine the method to process the flash request */ 2278c2ecf20Sopenharmony_cistatic bool load_image(struct esas2r_adapter *a, struct esas2r_request *rq) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci /* 2308c2ecf20Sopenharmony_ci * assume we have more to do. if we return with the status set to 2318c2ecf20Sopenharmony_ci * RS_PENDING, FM API tasks will continue. 2328c2ecf20Sopenharmony_ci */ 2338c2ecf20Sopenharmony_ci rq->req_stat = RS_PENDING; 2348c2ecf20Sopenharmony_ci if (test_bit(AF_DEGRADED_MODE, &a->flags)) 2358c2ecf20Sopenharmony_ci /* not suppported for now */; 2368c2ecf20Sopenharmony_ci else 2378c2ecf20Sopenharmony_ci build_flash_msg(a, rq); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return rq->req_stat == RS_PENDING; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/* boot image fixer uppers called before downloading the image. */ 2438c2ecf20Sopenharmony_cistatic void fix_bios(struct esas2r_adapter *a, struct esas2r_flash_img *fi) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci struct esas2r_component_header *ch = &fi->cmp_hdr[CH_IT_BIOS]; 2468c2ecf20Sopenharmony_ci struct esas2r_pc_image *pi; 2478c2ecf20Sopenharmony_ci struct esas2r_boot_header *bh; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci pi = (struct esas2r_pc_image *)((u8 *)fi + ch->image_offset); 2508c2ecf20Sopenharmony_ci bh = 2518c2ecf20Sopenharmony_ci (struct esas2r_boot_header *)((u8 *)pi + 2528c2ecf20Sopenharmony_ci le16_to_cpu(pi->header_offset)); 2538c2ecf20Sopenharmony_ci bh->device_id = cpu_to_le16(a->pcid->device); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* Recalculate the checksum in the PNP header if there */ 2568c2ecf20Sopenharmony_ci if (pi->pnp_offset) { 2578c2ecf20Sopenharmony_ci u8 *pnp_header_bytes = 2588c2ecf20Sopenharmony_ci ((u8 *)pi + le16_to_cpu(pi->pnp_offset)); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* Identifier - dword that starts at byte 10 */ 2618c2ecf20Sopenharmony_ci *((u32 *)&pnp_header_bytes[10]) = 2628c2ecf20Sopenharmony_ci cpu_to_le32(MAKEDWORD(a->pcid->subsystem_vendor, 2638c2ecf20Sopenharmony_ci a->pcid->subsystem_device)); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* Checksum - byte 9 */ 2668c2ecf20Sopenharmony_ci pnp_header_bytes[9] -= esas2r_calc_byte_cksum(pnp_header_bytes, 2678c2ecf20Sopenharmony_ci 32, 0); 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* Recalculate the checksum needed by the PC */ 2718c2ecf20Sopenharmony_ci pi->checksum = pi->checksum - 2728c2ecf20Sopenharmony_ci esas2r_calc_byte_cksum((u8 *)pi, ch->length, 0); 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic void fix_efi(struct esas2r_adapter *a, struct esas2r_flash_img *fi) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct esas2r_component_header *ch = &fi->cmp_hdr[CH_IT_EFI]; 2788c2ecf20Sopenharmony_ci u32 len = ch->length; 2798c2ecf20Sopenharmony_ci u32 offset = ch->image_offset; 2808c2ecf20Sopenharmony_ci struct esas2r_efi_image *ei; 2818c2ecf20Sopenharmony_ci struct esas2r_boot_header *bh; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci while (len) { 2848c2ecf20Sopenharmony_ci u32 thislen; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci ei = (struct esas2r_efi_image *)((u8 *)fi + offset); 2878c2ecf20Sopenharmony_ci bh = (struct esas2r_boot_header *)((u8 *)ei + 2888c2ecf20Sopenharmony_ci le16_to_cpu( 2898c2ecf20Sopenharmony_ci ei->header_offset)); 2908c2ecf20Sopenharmony_ci bh->device_id = cpu_to_le16(a->pcid->device); 2918c2ecf20Sopenharmony_ci thislen = (u32)le16_to_cpu(bh->image_length) * 512; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (thislen > len) 2948c2ecf20Sopenharmony_ci break; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci len -= thislen; 2978c2ecf20Sopenharmony_ci offset += thislen; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci/* Complete a FM API request with the specified status. */ 3028c2ecf20Sopenharmony_cistatic bool complete_fmapi_req(struct esas2r_adapter *a, 3038c2ecf20Sopenharmony_ci struct esas2r_request *rq, u8 fi_stat) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct esas2r_flash_context *fc = 3068c2ecf20Sopenharmony_ci (struct esas2r_flash_context *)rq->interrupt_cx; 3078c2ecf20Sopenharmony_ci struct esas2r_flash_img *fi = fc->fi; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci fi->status = fi_stat; 3108c2ecf20Sopenharmony_ci fi->driver_error = rq->req_stat; 3118c2ecf20Sopenharmony_ci rq->interrupt_cb = NULL; 3128c2ecf20Sopenharmony_ci rq->req_stat = RS_SUCCESS; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (fi_stat != FI_STAT_IMG_VER) 3158c2ecf20Sopenharmony_ci memset(fc->scratch, 0, FM_BUF_SZ); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci esas2r_enable_heartbeat(a); 3188c2ecf20Sopenharmony_ci clear_bit(AF_FLASH_LOCK, &a->flags); 3198c2ecf20Sopenharmony_ci return false; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci/* Process each phase of the flash download process. */ 3238c2ecf20Sopenharmony_cistatic void fw_download_proc(struct esas2r_adapter *a, 3248c2ecf20Sopenharmony_ci struct esas2r_request *rq) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct esas2r_flash_context *fc = 3278c2ecf20Sopenharmony_ci (struct esas2r_flash_context *)rq->interrupt_cx; 3288c2ecf20Sopenharmony_ci struct esas2r_flash_img *fi = fc->fi; 3298c2ecf20Sopenharmony_ci struct esas2r_component_header *ch; 3308c2ecf20Sopenharmony_ci u32 len; 3318c2ecf20Sopenharmony_ci u8 *p, *q; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci /* If the previous operation failed, just return. */ 3348c2ecf20Sopenharmony_ci if (rq->req_stat != RS_SUCCESS) 3358c2ecf20Sopenharmony_ci goto error; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* 3388c2ecf20Sopenharmony_ci * If an upload just completed and the compare length is non-zero, 3398c2ecf20Sopenharmony_ci * then we just read back part of the image we just wrote. verify the 3408c2ecf20Sopenharmony_ci * section and continue reading until the entire image is verified. 3418c2ecf20Sopenharmony_ci */ 3428c2ecf20Sopenharmony_ci if (fc->func == VDA_FLASH_READ 3438c2ecf20Sopenharmony_ci && fc->cmp_len) { 3448c2ecf20Sopenharmony_ci ch = &fi->cmp_hdr[fc->comp_typ]; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci p = fc->scratch; 3478c2ecf20Sopenharmony_ci q = (u8 *)fi /* start of the whole gob */ 3488c2ecf20Sopenharmony_ci + ch->image_offset /* start of the current image */ 3498c2ecf20Sopenharmony_ci + ch->length /* end of the current image */ 3508c2ecf20Sopenharmony_ci - fc->cmp_len; /* where we are now */ 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* 3538c2ecf20Sopenharmony_ci * NOTE - curr_len is the exact count of bytes for the read 3548c2ecf20Sopenharmony_ci * even when the end is read and its not a full buffer 3558c2ecf20Sopenharmony_ci */ 3568c2ecf20Sopenharmony_ci for (len = fc->curr_len; len; len--) 3578c2ecf20Sopenharmony_ci if (*p++ != *q++) 3588c2ecf20Sopenharmony_ci goto error; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci fc->cmp_len -= fc->curr_len; /* # left to compare */ 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* Update fc and determine the length for the next upload */ 3638c2ecf20Sopenharmony_ci if (fc->cmp_len > FM_BUF_SZ) 3648c2ecf20Sopenharmony_ci fc->sgc.length = FM_BUF_SZ; 3658c2ecf20Sopenharmony_ci else 3668c2ecf20Sopenharmony_ci fc->sgc.length = fc->cmp_len; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci fc->sgc.cur_offset = fc->sgc_offset + 3698c2ecf20Sopenharmony_ci ((u8 *)fc->scratch - (u8 *)fi); 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* 3738c2ecf20Sopenharmony_ci * This code uses a 'while' statement since the next component may 3748c2ecf20Sopenharmony_ci * have a length = zero. This can happen since some components are 3758c2ecf20Sopenharmony_ci * not required. At the end of this 'while' we set up the length 3768c2ecf20Sopenharmony_ci * for the next request and therefore sgc.length can be = 0. 3778c2ecf20Sopenharmony_ci */ 3788c2ecf20Sopenharmony_ci while (fc->sgc.length == 0) { 3798c2ecf20Sopenharmony_ci ch = &fi->cmp_hdr[fc->comp_typ]; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci switch (fc->task) { 3828c2ecf20Sopenharmony_ci case FMTSK_ERASE_BOOT: 3838c2ecf20Sopenharmony_ci /* the BIOS image is written next */ 3848c2ecf20Sopenharmony_ci ch = &fi->cmp_hdr[CH_IT_BIOS]; 3858c2ecf20Sopenharmony_ci if (ch->length == 0) 3868c2ecf20Sopenharmony_ci goto no_bios; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci fc->task = FMTSK_WRTBIOS; 3898c2ecf20Sopenharmony_ci fc->func = VDA_FLASH_BEGINW; 3908c2ecf20Sopenharmony_ci fc->comp_typ = CH_IT_BIOS; 3918c2ecf20Sopenharmony_ci fc->flsh_addr = FLS_OFFSET_BOOT; 3928c2ecf20Sopenharmony_ci fc->sgc.length = ch->length; 3938c2ecf20Sopenharmony_ci fc->sgc.cur_offset = fc->sgc_offset + 3948c2ecf20Sopenharmony_ci ch->image_offset; 3958c2ecf20Sopenharmony_ci break; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci case FMTSK_WRTBIOS: 3988c2ecf20Sopenharmony_ci /* 3998c2ecf20Sopenharmony_ci * The BIOS image has been written - read it and 4008c2ecf20Sopenharmony_ci * verify it 4018c2ecf20Sopenharmony_ci */ 4028c2ecf20Sopenharmony_ci fc->task = FMTSK_READBIOS; 4038c2ecf20Sopenharmony_ci fc->func = VDA_FLASH_READ; 4048c2ecf20Sopenharmony_ci fc->flsh_addr = FLS_OFFSET_BOOT; 4058c2ecf20Sopenharmony_ci fc->cmp_len = ch->length; 4068c2ecf20Sopenharmony_ci fc->sgc.length = FM_BUF_SZ; 4078c2ecf20Sopenharmony_ci fc->sgc.cur_offset = fc->sgc_offset 4088c2ecf20Sopenharmony_ci + ((u8 *)fc->scratch - 4098c2ecf20Sopenharmony_ci (u8 *)fi); 4108c2ecf20Sopenharmony_ci break; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci case FMTSK_READBIOS: 4138c2ecf20Sopenharmony_cino_bios: 4148c2ecf20Sopenharmony_ci /* 4158c2ecf20Sopenharmony_ci * Mark the component header status for the image 4168c2ecf20Sopenharmony_ci * completed 4178c2ecf20Sopenharmony_ci */ 4188c2ecf20Sopenharmony_ci ch->status = CH_STAT_SUCCESS; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* The MAC image is written next */ 4218c2ecf20Sopenharmony_ci ch = &fi->cmp_hdr[CH_IT_MAC]; 4228c2ecf20Sopenharmony_ci if (ch->length == 0) 4238c2ecf20Sopenharmony_ci goto no_mac; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci fc->task = FMTSK_WRTMAC; 4268c2ecf20Sopenharmony_ci fc->func = VDA_FLASH_BEGINW; 4278c2ecf20Sopenharmony_ci fc->comp_typ = CH_IT_MAC; 4288c2ecf20Sopenharmony_ci fc->flsh_addr = FLS_OFFSET_BOOT 4298c2ecf20Sopenharmony_ci + fi->cmp_hdr[CH_IT_BIOS].length; 4308c2ecf20Sopenharmony_ci fc->sgc.length = ch->length; 4318c2ecf20Sopenharmony_ci fc->sgc.cur_offset = fc->sgc_offset + 4328c2ecf20Sopenharmony_ci ch->image_offset; 4338c2ecf20Sopenharmony_ci break; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci case FMTSK_WRTMAC: 4368c2ecf20Sopenharmony_ci /* The MAC image has been written - read and verify */ 4378c2ecf20Sopenharmony_ci fc->task = FMTSK_READMAC; 4388c2ecf20Sopenharmony_ci fc->func = VDA_FLASH_READ; 4398c2ecf20Sopenharmony_ci fc->flsh_addr -= ch->length; 4408c2ecf20Sopenharmony_ci fc->cmp_len = ch->length; 4418c2ecf20Sopenharmony_ci fc->sgc.length = FM_BUF_SZ; 4428c2ecf20Sopenharmony_ci fc->sgc.cur_offset = fc->sgc_offset 4438c2ecf20Sopenharmony_ci + ((u8 *)fc->scratch - 4448c2ecf20Sopenharmony_ci (u8 *)fi); 4458c2ecf20Sopenharmony_ci break; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci case FMTSK_READMAC: 4488c2ecf20Sopenharmony_cino_mac: 4498c2ecf20Sopenharmony_ci /* 4508c2ecf20Sopenharmony_ci * Mark the component header status for the image 4518c2ecf20Sopenharmony_ci * completed 4528c2ecf20Sopenharmony_ci */ 4538c2ecf20Sopenharmony_ci ch->status = CH_STAT_SUCCESS; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci /* The EFI image is written next */ 4568c2ecf20Sopenharmony_ci ch = &fi->cmp_hdr[CH_IT_EFI]; 4578c2ecf20Sopenharmony_ci if (ch->length == 0) 4588c2ecf20Sopenharmony_ci goto no_efi; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci fc->task = FMTSK_WRTEFI; 4618c2ecf20Sopenharmony_ci fc->func = VDA_FLASH_BEGINW; 4628c2ecf20Sopenharmony_ci fc->comp_typ = CH_IT_EFI; 4638c2ecf20Sopenharmony_ci fc->flsh_addr = FLS_OFFSET_BOOT 4648c2ecf20Sopenharmony_ci + fi->cmp_hdr[CH_IT_BIOS].length 4658c2ecf20Sopenharmony_ci + fi->cmp_hdr[CH_IT_MAC].length; 4668c2ecf20Sopenharmony_ci fc->sgc.length = ch->length; 4678c2ecf20Sopenharmony_ci fc->sgc.cur_offset = fc->sgc_offset + 4688c2ecf20Sopenharmony_ci ch->image_offset; 4698c2ecf20Sopenharmony_ci break; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci case FMTSK_WRTEFI: 4728c2ecf20Sopenharmony_ci /* The EFI image has been written - read and verify */ 4738c2ecf20Sopenharmony_ci fc->task = FMTSK_READEFI; 4748c2ecf20Sopenharmony_ci fc->func = VDA_FLASH_READ; 4758c2ecf20Sopenharmony_ci fc->flsh_addr -= ch->length; 4768c2ecf20Sopenharmony_ci fc->cmp_len = ch->length; 4778c2ecf20Sopenharmony_ci fc->sgc.length = FM_BUF_SZ; 4788c2ecf20Sopenharmony_ci fc->sgc.cur_offset = fc->sgc_offset 4798c2ecf20Sopenharmony_ci + ((u8 *)fc->scratch - 4808c2ecf20Sopenharmony_ci (u8 *)fi); 4818c2ecf20Sopenharmony_ci break; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci case FMTSK_READEFI: 4848c2ecf20Sopenharmony_cino_efi: 4858c2ecf20Sopenharmony_ci /* 4868c2ecf20Sopenharmony_ci * Mark the component header status for the image 4878c2ecf20Sopenharmony_ci * completed 4888c2ecf20Sopenharmony_ci */ 4898c2ecf20Sopenharmony_ci ch->status = CH_STAT_SUCCESS; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* The CFG image is written next */ 4928c2ecf20Sopenharmony_ci ch = &fi->cmp_hdr[CH_IT_CFG]; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (ch->length == 0) 4958c2ecf20Sopenharmony_ci goto no_cfg; 4968c2ecf20Sopenharmony_ci fc->task = FMTSK_WRTCFG; 4978c2ecf20Sopenharmony_ci fc->func = VDA_FLASH_BEGINW; 4988c2ecf20Sopenharmony_ci fc->comp_typ = CH_IT_CFG; 4998c2ecf20Sopenharmony_ci fc->flsh_addr = FLS_OFFSET_CPYR - ch->length; 5008c2ecf20Sopenharmony_ci fc->sgc.length = ch->length; 5018c2ecf20Sopenharmony_ci fc->sgc.cur_offset = fc->sgc_offset + 5028c2ecf20Sopenharmony_ci ch->image_offset; 5038c2ecf20Sopenharmony_ci break; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci case FMTSK_WRTCFG: 5068c2ecf20Sopenharmony_ci /* The CFG image has been written - read and verify */ 5078c2ecf20Sopenharmony_ci fc->task = FMTSK_READCFG; 5088c2ecf20Sopenharmony_ci fc->func = VDA_FLASH_READ; 5098c2ecf20Sopenharmony_ci fc->flsh_addr = FLS_OFFSET_CPYR - ch->length; 5108c2ecf20Sopenharmony_ci fc->cmp_len = ch->length; 5118c2ecf20Sopenharmony_ci fc->sgc.length = FM_BUF_SZ; 5128c2ecf20Sopenharmony_ci fc->sgc.cur_offset = fc->sgc_offset 5138c2ecf20Sopenharmony_ci + ((u8 *)fc->scratch - 5148c2ecf20Sopenharmony_ci (u8 *)fi); 5158c2ecf20Sopenharmony_ci break; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci case FMTSK_READCFG: 5188c2ecf20Sopenharmony_cino_cfg: 5198c2ecf20Sopenharmony_ci /* 5208c2ecf20Sopenharmony_ci * Mark the component header status for the image 5218c2ecf20Sopenharmony_ci * completed 5228c2ecf20Sopenharmony_ci */ 5238c2ecf20Sopenharmony_ci ch->status = CH_STAT_SUCCESS; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* 5268c2ecf20Sopenharmony_ci * The download is complete. If in degraded mode, 5278c2ecf20Sopenharmony_ci * attempt a chip reset. 5288c2ecf20Sopenharmony_ci */ 5298c2ecf20Sopenharmony_ci if (test_bit(AF_DEGRADED_MODE, &a->flags)) 5308c2ecf20Sopenharmony_ci esas2r_local_reset_adapter(a); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci a->flash_ver = fi->cmp_hdr[CH_IT_BIOS].version; 5338c2ecf20Sopenharmony_ci esas2r_print_flash_rev(a); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* Update the type of boot image on the card */ 5368c2ecf20Sopenharmony_ci memcpy(a->image_type, fi->rel_version, 5378c2ecf20Sopenharmony_ci sizeof(fi->rel_version)); 5388c2ecf20Sopenharmony_ci complete_fmapi_req(a, rq, FI_STAT_SUCCESS); 5398c2ecf20Sopenharmony_ci return; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci /* If verifying, don't try reading more than what's there */ 5438c2ecf20Sopenharmony_ci if (fc->func == VDA_FLASH_READ 5448c2ecf20Sopenharmony_ci && fc->sgc.length > fc->cmp_len) 5458c2ecf20Sopenharmony_ci fc->sgc.length = fc->cmp_len; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* Build the request to perform the next action */ 5498c2ecf20Sopenharmony_ci if (!load_image(a, rq)) { 5508c2ecf20Sopenharmony_cierror: 5518c2ecf20Sopenharmony_ci if (fc->comp_typ < fi->num_comps) { 5528c2ecf20Sopenharmony_ci ch = &fi->cmp_hdr[fc->comp_typ]; 5538c2ecf20Sopenharmony_ci ch->status = CH_STAT_FAILED; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci complete_fmapi_req(a, rq, FI_STAT_FAILED); 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci/* Determine the flash image adaptyp for this adapter */ 5618c2ecf20Sopenharmony_cistatic u8 get_fi_adap_type(struct esas2r_adapter *a) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci u8 type; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* use the device ID to get the correct adap_typ for this HBA */ 5668c2ecf20Sopenharmony_ci switch (a->pcid->device) { 5678c2ecf20Sopenharmony_ci case ATTO_DID_INTEL_IOP348: 5688c2ecf20Sopenharmony_ci type = FI_AT_SUN_LAKE; 5698c2ecf20Sopenharmony_ci break; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci case ATTO_DID_MV_88RC9580: 5728c2ecf20Sopenharmony_ci case ATTO_DID_MV_88RC9580TS: 5738c2ecf20Sopenharmony_ci case ATTO_DID_MV_88RC9580TSE: 5748c2ecf20Sopenharmony_ci case ATTO_DID_MV_88RC9580TL: 5758c2ecf20Sopenharmony_ci type = FI_AT_MV_9580; 5768c2ecf20Sopenharmony_ci break; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci default: 5798c2ecf20Sopenharmony_ci type = FI_AT_UNKNWN; 5808c2ecf20Sopenharmony_ci break; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci return type; 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci/* Size of config + copyright + flash_ver images, 0 for failure. */ 5878c2ecf20Sopenharmony_cistatic u32 chk_cfg(u8 *cfg, u32 length, u32 *flash_ver) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci u16 *pw = (u16 *)cfg - 1; 5908c2ecf20Sopenharmony_ci u32 sz = 0; 5918c2ecf20Sopenharmony_ci u32 len = length; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci if (len == 0) 5948c2ecf20Sopenharmony_ci len = FM_BUF_SZ; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci if (flash_ver) 5978c2ecf20Sopenharmony_ci *flash_ver = 0; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci while (true) { 6008c2ecf20Sopenharmony_ci u16 type; 6018c2ecf20Sopenharmony_ci u16 size; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci type = le16_to_cpu(*pw--); 6048c2ecf20Sopenharmony_ci size = le16_to_cpu(*pw--); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci if (type != FBT_CPYR 6078c2ecf20Sopenharmony_ci && type != FBT_SETUP 6088c2ecf20Sopenharmony_ci && type != FBT_FLASH_VER) 6098c2ecf20Sopenharmony_ci break; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (type == FBT_FLASH_VER 6128c2ecf20Sopenharmony_ci && flash_ver) 6138c2ecf20Sopenharmony_ci *flash_ver = le32_to_cpu(*(u32 *)(pw - 1)); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci sz += size + (2 * sizeof(u16)); 6168c2ecf20Sopenharmony_ci pw -= size / sizeof(u16); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci if (sz > len - (2 * sizeof(u16))) 6198c2ecf20Sopenharmony_ci break; 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci /* See if we are comparing the size to the specified length */ 6238c2ecf20Sopenharmony_ci if (length && sz != length) 6248c2ecf20Sopenharmony_ci return 0; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci return sz; 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci/* Verify that the boot image is valid */ 6308c2ecf20Sopenharmony_cistatic u8 chk_boot(u8 *boot_img, u32 length) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci struct esas2r_boot_image *bi = (struct esas2r_boot_image *)boot_img; 6338c2ecf20Sopenharmony_ci u16 hdroffset = le16_to_cpu(bi->header_offset); 6348c2ecf20Sopenharmony_ci struct esas2r_boot_header *bh; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci if (bi->signature != le16_to_cpu(0xaa55) 6378c2ecf20Sopenharmony_ci || (long)hdroffset > 6388c2ecf20Sopenharmony_ci (long)(65536L - sizeof(struct esas2r_boot_header)) 6398c2ecf20Sopenharmony_ci || (hdroffset & 3) 6408c2ecf20Sopenharmony_ci || (hdroffset < sizeof(struct esas2r_boot_image)) 6418c2ecf20Sopenharmony_ci || ((u32)hdroffset + sizeof(struct esas2r_boot_header) > length)) 6428c2ecf20Sopenharmony_ci return 0xff; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci bh = (struct esas2r_boot_header *)((char *)bi + hdroffset); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci if (bh->signature[0] != 'P' 6478c2ecf20Sopenharmony_ci || bh->signature[1] != 'C' 6488c2ecf20Sopenharmony_ci || bh->signature[2] != 'I' 6498c2ecf20Sopenharmony_ci || bh->signature[3] != 'R' 6508c2ecf20Sopenharmony_ci || le16_to_cpu(bh->struct_length) < 6518c2ecf20Sopenharmony_ci (u16)sizeof(struct esas2r_boot_header) 6528c2ecf20Sopenharmony_ci || bh->class_code[2] != 0x01 6538c2ecf20Sopenharmony_ci || bh->class_code[1] != 0x04 6548c2ecf20Sopenharmony_ci || bh->class_code[0] != 0x00 6558c2ecf20Sopenharmony_ci || (bh->code_type != CODE_TYPE_PC 6568c2ecf20Sopenharmony_ci && bh->code_type != CODE_TYPE_OPEN 6578c2ecf20Sopenharmony_ci && bh->code_type != CODE_TYPE_EFI)) 6588c2ecf20Sopenharmony_ci return 0xff; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci return bh->code_type; 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci/* The sum of all the WORDS of the image */ 6648c2ecf20Sopenharmony_cistatic u16 calc_fi_checksum(struct esas2r_flash_context *fc) 6658c2ecf20Sopenharmony_ci{ 6668c2ecf20Sopenharmony_ci struct esas2r_flash_img *fi = fc->fi; 6678c2ecf20Sopenharmony_ci u16 cksum; 6688c2ecf20Sopenharmony_ci u32 len; 6698c2ecf20Sopenharmony_ci u16 *pw; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci for (len = (fi->length - fc->fi_hdr_len) / 2, 6728c2ecf20Sopenharmony_ci pw = (u16 *)((u8 *)fi + fc->fi_hdr_len), 6738c2ecf20Sopenharmony_ci cksum = 0; 6748c2ecf20Sopenharmony_ci len; 6758c2ecf20Sopenharmony_ci len--, pw++) 6768c2ecf20Sopenharmony_ci cksum = cksum + le16_to_cpu(*pw); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci return cksum; 6798c2ecf20Sopenharmony_ci} 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci/* 6828c2ecf20Sopenharmony_ci * Verify the flash image structure. The following verifications will 6838c2ecf20Sopenharmony_ci * be performed: 6848c2ecf20Sopenharmony_ci * 1) verify the fi_version is correct 6858c2ecf20Sopenharmony_ci * 2) verify the checksum of the entire image. 6868c2ecf20Sopenharmony_ci * 3) validate the adap_typ, action and length fields. 6878c2ecf20Sopenharmony_ci * 4) validate each component header. check the img_type and 6888c2ecf20Sopenharmony_ci * length fields 6898c2ecf20Sopenharmony_ci * 5) validate each component image. validate signatures and 6908c2ecf20Sopenharmony_ci * local checksums 6918c2ecf20Sopenharmony_ci */ 6928c2ecf20Sopenharmony_cistatic bool verify_fi(struct esas2r_adapter *a, 6938c2ecf20Sopenharmony_ci struct esas2r_flash_context *fc) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci struct esas2r_flash_img *fi = fc->fi; 6968c2ecf20Sopenharmony_ci u8 type; 6978c2ecf20Sopenharmony_ci bool imgerr; 6988c2ecf20Sopenharmony_ci u16 i; 6998c2ecf20Sopenharmony_ci u32 len; 7008c2ecf20Sopenharmony_ci struct esas2r_component_header *ch; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* Verify the length - length must even since we do a word checksum */ 7038c2ecf20Sopenharmony_ci len = fi->length; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci if ((len & 1) 7068c2ecf20Sopenharmony_ci || len < fc->fi_hdr_len) { 7078c2ecf20Sopenharmony_ci fi->status = FI_STAT_LENGTH; 7088c2ecf20Sopenharmony_ci return false; 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci /* Get adapter type and verify type in flash image */ 7128c2ecf20Sopenharmony_ci type = get_fi_adap_type(a); 7138c2ecf20Sopenharmony_ci if ((type == FI_AT_UNKNWN) || (fi->adap_typ != type)) { 7148c2ecf20Sopenharmony_ci fi->status = FI_STAT_ADAPTYP; 7158c2ecf20Sopenharmony_ci return false; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci /* 7198c2ecf20Sopenharmony_ci * Loop through each component and verify the img_type and length 7208c2ecf20Sopenharmony_ci * fields. Keep a running count of the sizes sooze we can verify total 7218c2ecf20Sopenharmony_ci * size to additive size. 7228c2ecf20Sopenharmony_ci */ 7238c2ecf20Sopenharmony_ci imgerr = false; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci for (i = 0, len = 0, ch = fi->cmp_hdr; 7268c2ecf20Sopenharmony_ci i < fi->num_comps; 7278c2ecf20Sopenharmony_ci i++, ch++) { 7288c2ecf20Sopenharmony_ci bool cmperr = false; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci /* 7318c2ecf20Sopenharmony_ci * Verify that the component header has the same index as the 7328c2ecf20Sopenharmony_ci * image type. The headers must be ordered correctly 7338c2ecf20Sopenharmony_ci */ 7348c2ecf20Sopenharmony_ci if (i != ch->img_type) { 7358c2ecf20Sopenharmony_ci imgerr = true; 7368c2ecf20Sopenharmony_ci ch->status = CH_STAT_INVALID; 7378c2ecf20Sopenharmony_ci continue; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci switch (ch->img_type) { 7418c2ecf20Sopenharmony_ci case CH_IT_BIOS: 7428c2ecf20Sopenharmony_ci type = CODE_TYPE_PC; 7438c2ecf20Sopenharmony_ci break; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci case CH_IT_MAC: 7468c2ecf20Sopenharmony_ci type = CODE_TYPE_OPEN; 7478c2ecf20Sopenharmony_ci break; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci case CH_IT_EFI: 7508c2ecf20Sopenharmony_ci type = CODE_TYPE_EFI; 7518c2ecf20Sopenharmony_ci break; 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci switch (ch->img_type) { 7558c2ecf20Sopenharmony_ci case CH_IT_FW: 7568c2ecf20Sopenharmony_ci case CH_IT_NVR: 7578c2ecf20Sopenharmony_ci break; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci case CH_IT_BIOS: 7608c2ecf20Sopenharmony_ci case CH_IT_MAC: 7618c2ecf20Sopenharmony_ci case CH_IT_EFI: 7628c2ecf20Sopenharmony_ci if (ch->length & 0x1ff) 7638c2ecf20Sopenharmony_ci cmperr = true; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci /* Test if component image is present */ 7668c2ecf20Sopenharmony_ci if (ch->length == 0) 7678c2ecf20Sopenharmony_ci break; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci /* Image is present - verify the image */ 7708c2ecf20Sopenharmony_ci if (chk_boot((u8 *)fi + ch->image_offset, ch->length) 7718c2ecf20Sopenharmony_ci != type) 7728c2ecf20Sopenharmony_ci cmperr = true; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci break; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci case CH_IT_CFG: 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci /* Test if component image is present */ 7798c2ecf20Sopenharmony_ci if (ch->length == 0) { 7808c2ecf20Sopenharmony_ci cmperr = true; 7818c2ecf20Sopenharmony_ci break; 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci /* Image is present - verify the image */ 7858c2ecf20Sopenharmony_ci if (!chk_cfg((u8 *)fi + ch->image_offset + ch->length, 7868c2ecf20Sopenharmony_ci ch->length, NULL)) 7878c2ecf20Sopenharmony_ci cmperr = true; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci break; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci default: 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci fi->status = FI_STAT_UNKNOWN; 7948c2ecf20Sopenharmony_ci return false; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci if (cmperr) { 7988c2ecf20Sopenharmony_ci imgerr = true; 7998c2ecf20Sopenharmony_ci ch->status = CH_STAT_INVALID; 8008c2ecf20Sopenharmony_ci } else { 8018c2ecf20Sopenharmony_ci ch->status = CH_STAT_PENDING; 8028c2ecf20Sopenharmony_ci len += ch->length; 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci if (imgerr) { 8078c2ecf20Sopenharmony_ci fi->status = FI_STAT_MISSING; 8088c2ecf20Sopenharmony_ci return false; 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci /* Compare fi->length to the sum of ch->length fields */ 8128c2ecf20Sopenharmony_ci if (len != fi->length - fc->fi_hdr_len) { 8138c2ecf20Sopenharmony_ci fi->status = FI_STAT_LENGTH; 8148c2ecf20Sopenharmony_ci return false; 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci /* Compute the checksum - it should come out zero */ 8188c2ecf20Sopenharmony_ci if (fi->checksum != calc_fi_checksum(fc)) { 8198c2ecf20Sopenharmony_ci fi->status = FI_STAT_CHKSUM; 8208c2ecf20Sopenharmony_ci return false; 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci return true; 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci/* Fill in the FS IOCTL response data from a completed request. */ 8278c2ecf20Sopenharmony_cistatic void esas2r_complete_fs_ioctl(struct esas2r_adapter *a, 8288c2ecf20Sopenharmony_ci struct esas2r_request *rq) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci struct esas2r_ioctl_fs *fs = 8318c2ecf20Sopenharmony_ci (struct esas2r_ioctl_fs *)rq->interrupt_cx; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci if (rq->vrq->flash.sub_func == VDA_FLASH_COMMIT) 8348c2ecf20Sopenharmony_ci esas2r_enable_heartbeat(a); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci fs->driver_error = rq->req_stat; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci if (fs->driver_error == RS_SUCCESS) 8398c2ecf20Sopenharmony_ci fs->status = ATTO_STS_SUCCESS; 8408c2ecf20Sopenharmony_ci else 8418c2ecf20Sopenharmony_ci fs->status = ATTO_STS_FAILED; 8428c2ecf20Sopenharmony_ci} 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci/* Prepare an FS IOCTL request to be sent to the firmware. */ 8458c2ecf20Sopenharmony_cibool esas2r_process_fs_ioctl(struct esas2r_adapter *a, 8468c2ecf20Sopenharmony_ci struct esas2r_ioctl_fs *fs, 8478c2ecf20Sopenharmony_ci struct esas2r_request *rq, 8488c2ecf20Sopenharmony_ci struct esas2r_sg_context *sgc) 8498c2ecf20Sopenharmony_ci{ 8508c2ecf20Sopenharmony_ci u8 cmdcnt = (u8)ARRAY_SIZE(cmd_to_fls_func); 8518c2ecf20Sopenharmony_ci struct esas2r_ioctlfs_command *fsc = &fs->command; 8528c2ecf20Sopenharmony_ci u8 func = 0; 8538c2ecf20Sopenharmony_ci u32 datalen; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci fs->status = ATTO_STS_FAILED; 8568c2ecf20Sopenharmony_ci fs->driver_error = RS_PENDING; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci if (fs->version > ESAS2R_FS_VER) { 8598c2ecf20Sopenharmony_ci fs->status = ATTO_STS_INV_VERSION; 8608c2ecf20Sopenharmony_ci return false; 8618c2ecf20Sopenharmony_ci } 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci if (fsc->command >= cmdcnt) { 8648c2ecf20Sopenharmony_ci fs->status = ATTO_STS_INV_FUNC; 8658c2ecf20Sopenharmony_ci return false; 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci func = cmd_to_fls_func[fsc->command]; 8698c2ecf20Sopenharmony_ci if (func == 0xFF) { 8708c2ecf20Sopenharmony_ci fs->status = ATTO_STS_INV_FUNC; 8718c2ecf20Sopenharmony_ci return false; 8728c2ecf20Sopenharmony_ci } 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci if (fsc->command != ESAS2R_FS_CMD_CANCEL) { 8758c2ecf20Sopenharmony_ci if ((a->pcid->device != ATTO_DID_MV_88RC9580 8768c2ecf20Sopenharmony_ci || fs->adap_type != ESAS2R_FS_AT_ESASRAID2) 8778c2ecf20Sopenharmony_ci && (a->pcid->device != ATTO_DID_MV_88RC9580TS 8788c2ecf20Sopenharmony_ci || fs->adap_type != ESAS2R_FS_AT_TSSASRAID2) 8798c2ecf20Sopenharmony_ci && (a->pcid->device != ATTO_DID_MV_88RC9580TSE 8808c2ecf20Sopenharmony_ci || fs->adap_type != ESAS2R_FS_AT_TSSASRAID2E) 8818c2ecf20Sopenharmony_ci && (a->pcid->device != ATTO_DID_MV_88RC9580TL 8828c2ecf20Sopenharmony_ci || fs->adap_type != ESAS2R_FS_AT_TLSASHBA)) { 8838c2ecf20Sopenharmony_ci fs->status = ATTO_STS_INV_ADAPTER; 8848c2ecf20Sopenharmony_ci return false; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci if (fs->driver_ver > ESAS2R_FS_DRVR_VER) { 8888c2ecf20Sopenharmony_ci fs->status = ATTO_STS_INV_DRVR_VER; 8898c2ecf20Sopenharmony_ci return false; 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci if (test_bit(AF_DEGRADED_MODE, &a->flags)) { 8948c2ecf20Sopenharmony_ci fs->status = ATTO_STS_DEGRADED; 8958c2ecf20Sopenharmony_ci return false; 8968c2ecf20Sopenharmony_ci } 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci rq->interrupt_cb = esas2r_complete_fs_ioctl; 8998c2ecf20Sopenharmony_ci rq->interrupt_cx = fs; 9008c2ecf20Sopenharmony_ci datalen = le32_to_cpu(fsc->length); 9018c2ecf20Sopenharmony_ci esas2r_build_flash_req(a, 9028c2ecf20Sopenharmony_ci rq, 9038c2ecf20Sopenharmony_ci func, 9048c2ecf20Sopenharmony_ci fsc->checksum, 9058c2ecf20Sopenharmony_ci le32_to_cpu(fsc->flash_addr), 9068c2ecf20Sopenharmony_ci datalen); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci if (func == VDA_FLASH_WRITE 9098c2ecf20Sopenharmony_ci || func == VDA_FLASH_READ) { 9108c2ecf20Sopenharmony_ci if (datalen == 0) { 9118c2ecf20Sopenharmony_ci fs->status = ATTO_STS_INV_FUNC; 9128c2ecf20Sopenharmony_ci return false; 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci esas2r_sgc_init(sgc, a, rq, rq->vrq->flash.data.sge); 9168c2ecf20Sopenharmony_ci sgc->length = datalen; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci if (!esas2r_build_sg_list(a, rq, sgc)) { 9198c2ecf20Sopenharmony_ci fs->status = ATTO_STS_OUT_OF_RSRC; 9208c2ecf20Sopenharmony_ci return false; 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci if (func == VDA_FLASH_COMMIT) 9258c2ecf20Sopenharmony_ci esas2r_disable_heartbeat(a); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci esas2r_start_request(a, rq); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci return true; 9308c2ecf20Sopenharmony_ci} 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_cistatic bool esas2r_flash_access(struct esas2r_adapter *a, u32 function) 9338c2ecf20Sopenharmony_ci{ 9348c2ecf20Sopenharmony_ci u32 starttime; 9358c2ecf20Sopenharmony_ci u32 timeout; 9368c2ecf20Sopenharmony_ci u32 intstat; 9378c2ecf20Sopenharmony_ci u32 doorbell; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci /* Disable chip interrupts awhile */ 9408c2ecf20Sopenharmony_ci if (function == DRBL_FLASH_REQ) 9418c2ecf20Sopenharmony_ci esas2r_disable_chip_interrupts(a); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci /* Issue the request to the firmware */ 9448c2ecf20Sopenharmony_ci esas2r_write_register_dword(a, MU_DOORBELL_IN, function); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci /* Now wait for the firmware to process it */ 9478c2ecf20Sopenharmony_ci starttime = jiffies_to_msecs(jiffies); 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci if (test_bit(AF_CHPRST_PENDING, &a->flags) || 9508c2ecf20Sopenharmony_ci test_bit(AF_DISC_PENDING, &a->flags)) 9518c2ecf20Sopenharmony_ci timeout = 40000; 9528c2ecf20Sopenharmony_ci else 9538c2ecf20Sopenharmony_ci timeout = 5000; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci while (true) { 9568c2ecf20Sopenharmony_ci intstat = esas2r_read_register_dword(a, MU_INT_STATUS_OUT); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci if (intstat & MU_INTSTAT_DRBL) { 9598c2ecf20Sopenharmony_ci /* Got a doorbell interrupt. Check for the function */ 9608c2ecf20Sopenharmony_ci doorbell = 9618c2ecf20Sopenharmony_ci esas2r_read_register_dword(a, MU_DOORBELL_OUT); 9628c2ecf20Sopenharmony_ci esas2r_write_register_dword(a, MU_DOORBELL_OUT, 9638c2ecf20Sopenharmony_ci doorbell); 9648c2ecf20Sopenharmony_ci if (doorbell & function) 9658c2ecf20Sopenharmony_ci break; 9668c2ecf20Sopenharmony_ci } 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci schedule_timeout_interruptible(msecs_to_jiffies(100)); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci if ((jiffies_to_msecs(jiffies) - starttime) > timeout) { 9718c2ecf20Sopenharmony_ci /* 9728c2ecf20Sopenharmony_ci * Iimeout. If we were requesting flash access, 9738c2ecf20Sopenharmony_ci * indicate we are done so the firmware knows we gave 9748c2ecf20Sopenharmony_ci * up. If this was a REQ, we also need to re-enable 9758c2ecf20Sopenharmony_ci * chip interrupts. 9768c2ecf20Sopenharmony_ci */ 9778c2ecf20Sopenharmony_ci if (function == DRBL_FLASH_REQ) { 9788c2ecf20Sopenharmony_ci esas2r_hdebug("flash access timeout"); 9798c2ecf20Sopenharmony_ci esas2r_write_register_dword(a, MU_DOORBELL_IN, 9808c2ecf20Sopenharmony_ci DRBL_FLASH_DONE); 9818c2ecf20Sopenharmony_ci esas2r_enable_chip_interrupts(a); 9828c2ecf20Sopenharmony_ci } else { 9838c2ecf20Sopenharmony_ci esas2r_hdebug("flash release timeout"); 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci return false; 9878c2ecf20Sopenharmony_ci } 9888c2ecf20Sopenharmony_ci } 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci /* if we're done, re-enable chip interrupts */ 9918c2ecf20Sopenharmony_ci if (function == DRBL_FLASH_DONE) 9928c2ecf20Sopenharmony_ci esas2r_enable_chip_interrupts(a); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci return true; 9958c2ecf20Sopenharmony_ci} 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci#define WINDOW_SIZE ((signed int)MW_DATA_WINDOW_SIZE) 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_cibool esas2r_read_flash_block(struct esas2r_adapter *a, 10008c2ecf20Sopenharmony_ci void *to, 10018c2ecf20Sopenharmony_ci u32 from, 10028c2ecf20Sopenharmony_ci u32 size) 10038c2ecf20Sopenharmony_ci{ 10048c2ecf20Sopenharmony_ci u8 *end = (u8 *)to; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci /* Try to acquire access to the flash */ 10078c2ecf20Sopenharmony_ci if (!esas2r_flash_access(a, DRBL_FLASH_REQ)) 10088c2ecf20Sopenharmony_ci return false; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci while (size) { 10118c2ecf20Sopenharmony_ci u32 len; 10128c2ecf20Sopenharmony_ci u32 offset; 10138c2ecf20Sopenharmony_ci u32 iatvr; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci if (test_bit(AF2_SERIAL_FLASH, &a->flags2)) 10168c2ecf20Sopenharmony_ci iatvr = MW_DATA_ADDR_SER_FLASH + (from & -WINDOW_SIZE); 10178c2ecf20Sopenharmony_ci else 10188c2ecf20Sopenharmony_ci iatvr = MW_DATA_ADDR_PAR_FLASH + (from & -WINDOW_SIZE); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci esas2r_map_data_window(a, iatvr); 10218c2ecf20Sopenharmony_ci offset = from & (WINDOW_SIZE - 1); 10228c2ecf20Sopenharmony_ci len = size; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci if (len > WINDOW_SIZE - offset) 10258c2ecf20Sopenharmony_ci len = WINDOW_SIZE - offset; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci from += len; 10288c2ecf20Sopenharmony_ci size -= len; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci while (len--) { 10318c2ecf20Sopenharmony_ci *end++ = esas2r_read_data_byte(a, offset); 10328c2ecf20Sopenharmony_ci offset++; 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci } 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci /* Release flash access */ 10378c2ecf20Sopenharmony_ci esas2r_flash_access(a, DRBL_FLASH_DONE); 10388c2ecf20Sopenharmony_ci return true; 10398c2ecf20Sopenharmony_ci} 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_cibool esas2r_read_flash_rev(struct esas2r_adapter *a) 10428c2ecf20Sopenharmony_ci{ 10438c2ecf20Sopenharmony_ci u8 bytes[256]; 10448c2ecf20Sopenharmony_ci u16 *pw; 10458c2ecf20Sopenharmony_ci u16 *pwstart; 10468c2ecf20Sopenharmony_ci u16 type; 10478c2ecf20Sopenharmony_ci u16 size; 10488c2ecf20Sopenharmony_ci u32 sz; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci sz = sizeof(bytes); 10518c2ecf20Sopenharmony_ci pw = (u16 *)(bytes + sz); 10528c2ecf20Sopenharmony_ci pwstart = (u16 *)bytes + 2; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci if (!esas2r_read_flash_block(a, bytes, FLS_OFFSET_CPYR - sz, sz)) 10558c2ecf20Sopenharmony_ci goto invalid_rev; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci while (pw >= pwstart) { 10588c2ecf20Sopenharmony_ci pw--; 10598c2ecf20Sopenharmony_ci type = le16_to_cpu(*pw); 10608c2ecf20Sopenharmony_ci pw--; 10618c2ecf20Sopenharmony_ci size = le16_to_cpu(*pw); 10628c2ecf20Sopenharmony_ci pw -= size / 2; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci if (type == FBT_CPYR 10658c2ecf20Sopenharmony_ci || type == FBT_SETUP 10668c2ecf20Sopenharmony_ci || pw < pwstart) 10678c2ecf20Sopenharmony_ci continue; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci if (type == FBT_FLASH_VER) 10708c2ecf20Sopenharmony_ci a->flash_ver = le32_to_cpu(*(u32 *)pw); 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci break; 10738c2ecf20Sopenharmony_ci } 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ciinvalid_rev: 10768c2ecf20Sopenharmony_ci return esas2r_print_flash_rev(a); 10778c2ecf20Sopenharmony_ci} 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_cibool esas2r_print_flash_rev(struct esas2r_adapter *a) 10808c2ecf20Sopenharmony_ci{ 10818c2ecf20Sopenharmony_ci u16 year = LOWORD(a->flash_ver); 10828c2ecf20Sopenharmony_ci u8 day = LOBYTE(HIWORD(a->flash_ver)); 10838c2ecf20Sopenharmony_ci u8 month = HIBYTE(HIWORD(a->flash_ver)); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci if (day == 0 10868c2ecf20Sopenharmony_ci || month == 0 10878c2ecf20Sopenharmony_ci || day > 31 10888c2ecf20Sopenharmony_ci || month > 12 10898c2ecf20Sopenharmony_ci || year < 2006 10908c2ecf20Sopenharmony_ci || year > 9999) { 10918c2ecf20Sopenharmony_ci strcpy(a->flash_rev, "not found"); 10928c2ecf20Sopenharmony_ci a->flash_ver = 0; 10938c2ecf20Sopenharmony_ci return false; 10948c2ecf20Sopenharmony_ci } 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci sprintf(a->flash_rev, "%02d/%02d/%04d", month, day, year); 10978c2ecf20Sopenharmony_ci esas2r_hdebug("flash version: %s", a->flash_rev); 10988c2ecf20Sopenharmony_ci return true; 10998c2ecf20Sopenharmony_ci} 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci/* 11028c2ecf20Sopenharmony_ci * Find the type of boot image type that is currently in the flash. 11038c2ecf20Sopenharmony_ci * The chip only has a 64 KB PCI-e expansion ROM 11048c2ecf20Sopenharmony_ci * size so only one image can be flashed at a time. 11058c2ecf20Sopenharmony_ci */ 11068c2ecf20Sopenharmony_cibool esas2r_read_image_type(struct esas2r_adapter *a) 11078c2ecf20Sopenharmony_ci{ 11088c2ecf20Sopenharmony_ci u8 bytes[256]; 11098c2ecf20Sopenharmony_ci struct esas2r_boot_image *bi; 11108c2ecf20Sopenharmony_ci struct esas2r_boot_header *bh; 11118c2ecf20Sopenharmony_ci u32 sz; 11128c2ecf20Sopenharmony_ci u32 len; 11138c2ecf20Sopenharmony_ci u32 offset; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci /* Start at the base of the boot images and look for a valid image */ 11168c2ecf20Sopenharmony_ci sz = sizeof(bytes); 11178c2ecf20Sopenharmony_ci len = FLS_LENGTH_BOOT; 11188c2ecf20Sopenharmony_ci offset = 0; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci while (true) { 11218c2ecf20Sopenharmony_ci if (!esas2r_read_flash_block(a, bytes, FLS_OFFSET_BOOT + 11228c2ecf20Sopenharmony_ci offset, 11238c2ecf20Sopenharmony_ci sz)) 11248c2ecf20Sopenharmony_ci goto invalid_rev; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci bi = (struct esas2r_boot_image *)bytes; 11278c2ecf20Sopenharmony_ci bh = (struct esas2r_boot_header *)((u8 *)bi + 11288c2ecf20Sopenharmony_ci le16_to_cpu( 11298c2ecf20Sopenharmony_ci bi->header_offset)); 11308c2ecf20Sopenharmony_ci if (bi->signature != cpu_to_le16(0xAA55)) 11318c2ecf20Sopenharmony_ci goto invalid_rev; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci if (bh->code_type == CODE_TYPE_PC) { 11348c2ecf20Sopenharmony_ci strcpy(a->image_type, "BIOS"); 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci return true; 11378c2ecf20Sopenharmony_ci } else if (bh->code_type == CODE_TYPE_EFI) { 11388c2ecf20Sopenharmony_ci struct esas2r_efi_image *ei; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci /* 11418c2ecf20Sopenharmony_ci * So we have an EFI image. There are several types 11428c2ecf20Sopenharmony_ci * so see which architecture we have. 11438c2ecf20Sopenharmony_ci */ 11448c2ecf20Sopenharmony_ci ei = (struct esas2r_efi_image *)bytes; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci switch (le16_to_cpu(ei->machine_type)) { 11478c2ecf20Sopenharmony_ci case EFI_MACHINE_IA32: 11488c2ecf20Sopenharmony_ci strcpy(a->image_type, "EFI 32-bit"); 11498c2ecf20Sopenharmony_ci return true; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci case EFI_MACHINE_IA64: 11528c2ecf20Sopenharmony_ci strcpy(a->image_type, "EFI itanium"); 11538c2ecf20Sopenharmony_ci return true; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci case EFI_MACHINE_X64: 11568c2ecf20Sopenharmony_ci strcpy(a->image_type, "EFI 64-bit"); 11578c2ecf20Sopenharmony_ci return true; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci case EFI_MACHINE_EBC: 11608c2ecf20Sopenharmony_ci strcpy(a->image_type, "EFI EBC"); 11618c2ecf20Sopenharmony_ci return true; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci default: 11648c2ecf20Sopenharmony_ci goto invalid_rev; 11658c2ecf20Sopenharmony_ci } 11668c2ecf20Sopenharmony_ci } else { 11678c2ecf20Sopenharmony_ci u32 thislen; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci /* jump to the next image */ 11708c2ecf20Sopenharmony_ci thislen = (u32)le16_to_cpu(bh->image_length) * 512; 11718c2ecf20Sopenharmony_ci if (thislen == 0 11728c2ecf20Sopenharmony_ci || thislen + offset > len 11738c2ecf20Sopenharmony_ci || bh->indicator == INDICATOR_LAST) 11748c2ecf20Sopenharmony_ci break; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci offset += thislen; 11778c2ecf20Sopenharmony_ci } 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ciinvalid_rev: 11818c2ecf20Sopenharmony_ci strcpy(a->image_type, "no boot images"); 11828c2ecf20Sopenharmony_ci return false; 11838c2ecf20Sopenharmony_ci} 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci/* 11868c2ecf20Sopenharmony_ci * Read and validate current NVRAM parameters by accessing 11878c2ecf20Sopenharmony_ci * physical NVRAM directly. if currently stored parameters are 11888c2ecf20Sopenharmony_ci * invalid, use the defaults. 11898c2ecf20Sopenharmony_ci */ 11908c2ecf20Sopenharmony_cibool esas2r_nvram_read_direct(struct esas2r_adapter *a) 11918c2ecf20Sopenharmony_ci{ 11928c2ecf20Sopenharmony_ci bool result; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci if (down_interruptible(&a->nvram_semaphore)) 11958c2ecf20Sopenharmony_ci return false; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci if (!esas2r_read_flash_block(a, a->nvram, FLS_OFFSET_NVR, 11988c2ecf20Sopenharmony_ci sizeof(struct esas2r_sas_nvram))) { 11998c2ecf20Sopenharmony_ci esas2r_hdebug("NVRAM read failed, using defaults"); 12008c2ecf20Sopenharmony_ci up(&a->nvram_semaphore); 12018c2ecf20Sopenharmony_ci return false; 12028c2ecf20Sopenharmony_ci } 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci result = esas2r_nvram_validate(a); 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci up(&a->nvram_semaphore); 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci return result; 12098c2ecf20Sopenharmony_ci} 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci/* Interrupt callback to process NVRAM completions. */ 12128c2ecf20Sopenharmony_cistatic void esas2r_nvram_callback(struct esas2r_adapter *a, 12138c2ecf20Sopenharmony_ci struct esas2r_request *rq) 12148c2ecf20Sopenharmony_ci{ 12158c2ecf20Sopenharmony_ci struct atto_vda_flash_req *vrq = &rq->vrq->flash; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci if (rq->req_stat == RS_SUCCESS) { 12188c2ecf20Sopenharmony_ci /* last request was successful. see what to do now. */ 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci switch (vrq->sub_func) { 12218c2ecf20Sopenharmony_ci case VDA_FLASH_BEGINW: 12228c2ecf20Sopenharmony_ci vrq->sub_func = VDA_FLASH_WRITE; 12238c2ecf20Sopenharmony_ci rq->req_stat = RS_PENDING; 12248c2ecf20Sopenharmony_ci break; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci case VDA_FLASH_WRITE: 12278c2ecf20Sopenharmony_ci vrq->sub_func = VDA_FLASH_COMMIT; 12288c2ecf20Sopenharmony_ci rq->req_stat = RS_PENDING; 12298c2ecf20Sopenharmony_ci break; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci case VDA_FLASH_READ: 12328c2ecf20Sopenharmony_ci esas2r_nvram_validate(a); 12338c2ecf20Sopenharmony_ci break; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci case VDA_FLASH_COMMIT: 12368c2ecf20Sopenharmony_ci default: 12378c2ecf20Sopenharmony_ci break; 12388c2ecf20Sopenharmony_ci } 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci if (rq->req_stat != RS_PENDING) { 12428c2ecf20Sopenharmony_ci /* update the NVRAM state */ 12438c2ecf20Sopenharmony_ci if (rq->req_stat == RS_SUCCESS) 12448c2ecf20Sopenharmony_ci set_bit(AF_NVR_VALID, &a->flags); 12458c2ecf20Sopenharmony_ci else 12468c2ecf20Sopenharmony_ci clear_bit(AF_NVR_VALID, &a->flags); 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci esas2r_enable_heartbeat(a); 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci up(&a->nvram_semaphore); 12518c2ecf20Sopenharmony_ci } 12528c2ecf20Sopenharmony_ci} 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci/* 12558c2ecf20Sopenharmony_ci * Write the contents of nvram to the adapter's physical NVRAM. 12568c2ecf20Sopenharmony_ci * The cached copy of the NVRAM is also updated. 12578c2ecf20Sopenharmony_ci */ 12588c2ecf20Sopenharmony_cibool esas2r_nvram_write(struct esas2r_adapter *a, struct esas2r_request *rq, 12598c2ecf20Sopenharmony_ci struct esas2r_sas_nvram *nvram) 12608c2ecf20Sopenharmony_ci{ 12618c2ecf20Sopenharmony_ci struct esas2r_sas_nvram *n = nvram; 12628c2ecf20Sopenharmony_ci u8 sas_address_bytes[8]; 12638c2ecf20Sopenharmony_ci u32 *sas_address_dwords = (u32 *)&sas_address_bytes[0]; 12648c2ecf20Sopenharmony_ci struct atto_vda_flash_req *vrq = &rq->vrq->flash; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci if (test_bit(AF_DEGRADED_MODE, &a->flags)) 12678c2ecf20Sopenharmony_ci return false; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci if (down_interruptible(&a->nvram_semaphore)) 12708c2ecf20Sopenharmony_ci return false; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci if (n == NULL) 12738c2ecf20Sopenharmony_ci n = a->nvram; 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci /* check the validity of the settings */ 12768c2ecf20Sopenharmony_ci if (n->version > SASNVR_VERSION) { 12778c2ecf20Sopenharmony_ci up(&a->nvram_semaphore); 12788c2ecf20Sopenharmony_ci return false; 12798c2ecf20Sopenharmony_ci } 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci memcpy(&sas_address_bytes[0], n->sas_addr, 8); 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci if (sas_address_bytes[0] != 0x50 12848c2ecf20Sopenharmony_ci || sas_address_bytes[1] != 0x01 12858c2ecf20Sopenharmony_ci || sas_address_bytes[2] != 0x08 12868c2ecf20Sopenharmony_ci || (sas_address_bytes[3] & 0xF0) != 0x60 12878c2ecf20Sopenharmony_ci || ((sas_address_bytes[3] & 0x0F) | sas_address_dwords[1]) == 0) { 12888c2ecf20Sopenharmony_ci up(&a->nvram_semaphore); 12898c2ecf20Sopenharmony_ci return false; 12908c2ecf20Sopenharmony_ci } 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci if (n->spin_up_delay > SASNVR_SPINUP_MAX) 12938c2ecf20Sopenharmony_ci n->spin_up_delay = SASNVR_SPINUP_MAX; 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci n->version = SASNVR_VERSION; 12968c2ecf20Sopenharmony_ci n->checksum = n->checksum - esas2r_nvramcalc_cksum(n); 12978c2ecf20Sopenharmony_ci memcpy(a->nvram, n, sizeof(struct esas2r_sas_nvram)); 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci /* write the NVRAM */ 13008c2ecf20Sopenharmony_ci n = a->nvram; 13018c2ecf20Sopenharmony_ci esas2r_disable_heartbeat(a); 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci esas2r_build_flash_req(a, 13048c2ecf20Sopenharmony_ci rq, 13058c2ecf20Sopenharmony_ci VDA_FLASH_BEGINW, 13068c2ecf20Sopenharmony_ci esas2r_nvramcalc_xor_cksum(n), 13078c2ecf20Sopenharmony_ci FLS_OFFSET_NVR, 13088c2ecf20Sopenharmony_ci sizeof(struct esas2r_sas_nvram)); 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) { 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci vrq->data.sge[0].length = 13138c2ecf20Sopenharmony_ci cpu_to_le32(SGE_LAST | 13148c2ecf20Sopenharmony_ci sizeof(struct esas2r_sas_nvram)); 13158c2ecf20Sopenharmony_ci vrq->data.sge[0].address = cpu_to_le64( 13168c2ecf20Sopenharmony_ci a->uncached_phys + (u64)((u8 *)n - a->uncached)); 13178c2ecf20Sopenharmony_ci } else { 13188c2ecf20Sopenharmony_ci vrq->data.prde[0].ctl_len = 13198c2ecf20Sopenharmony_ci cpu_to_le32(sizeof(struct esas2r_sas_nvram)); 13208c2ecf20Sopenharmony_ci vrq->data.prde[0].address = cpu_to_le64( 13218c2ecf20Sopenharmony_ci a->uncached_phys 13228c2ecf20Sopenharmony_ci + (u64)((u8 *)n - a->uncached)); 13238c2ecf20Sopenharmony_ci } 13248c2ecf20Sopenharmony_ci rq->interrupt_cb = esas2r_nvram_callback; 13258c2ecf20Sopenharmony_ci esas2r_start_request(a, rq); 13268c2ecf20Sopenharmony_ci return true; 13278c2ecf20Sopenharmony_ci} 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci/* Validate the cached NVRAM. if the NVRAM is invalid, load the defaults. */ 13308c2ecf20Sopenharmony_cibool esas2r_nvram_validate(struct esas2r_adapter *a) 13318c2ecf20Sopenharmony_ci{ 13328c2ecf20Sopenharmony_ci struct esas2r_sas_nvram *n = a->nvram; 13338c2ecf20Sopenharmony_ci bool rslt = false; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci if (n->signature[0] != 'E' 13368c2ecf20Sopenharmony_ci || n->signature[1] != 'S' 13378c2ecf20Sopenharmony_ci || n->signature[2] != 'A' 13388c2ecf20Sopenharmony_ci || n->signature[3] != 'S') { 13398c2ecf20Sopenharmony_ci esas2r_hdebug("invalid NVRAM signature"); 13408c2ecf20Sopenharmony_ci } else if (esas2r_nvramcalc_cksum(n)) { 13418c2ecf20Sopenharmony_ci esas2r_hdebug("invalid NVRAM checksum"); 13428c2ecf20Sopenharmony_ci } else if (n->version > SASNVR_VERSION) { 13438c2ecf20Sopenharmony_ci esas2r_hdebug("invalid NVRAM version"); 13448c2ecf20Sopenharmony_ci } else { 13458c2ecf20Sopenharmony_ci set_bit(AF_NVR_VALID, &a->flags); 13468c2ecf20Sopenharmony_ci rslt = true; 13478c2ecf20Sopenharmony_ci } 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci if (rslt == false) { 13508c2ecf20Sopenharmony_ci esas2r_hdebug("using defaults"); 13518c2ecf20Sopenharmony_ci esas2r_nvram_set_defaults(a); 13528c2ecf20Sopenharmony_ci } 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci return rslt; 13558c2ecf20Sopenharmony_ci} 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci/* 13588c2ecf20Sopenharmony_ci * Set the cached NVRAM to defaults. note that this function sets the default 13598c2ecf20Sopenharmony_ci * NVRAM when it has been determined that the physical NVRAM is invalid. 13608c2ecf20Sopenharmony_ci * In this case, the SAS address is fabricated. 13618c2ecf20Sopenharmony_ci */ 13628c2ecf20Sopenharmony_civoid esas2r_nvram_set_defaults(struct esas2r_adapter *a) 13638c2ecf20Sopenharmony_ci{ 13648c2ecf20Sopenharmony_ci struct esas2r_sas_nvram *n = a->nvram; 13658c2ecf20Sopenharmony_ci u32 time = jiffies_to_msecs(jiffies); 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci clear_bit(AF_NVR_VALID, &a->flags); 13688c2ecf20Sopenharmony_ci *n = default_sas_nvram; 13698c2ecf20Sopenharmony_ci n->sas_addr[3] |= 0x0F; 13708c2ecf20Sopenharmony_ci n->sas_addr[4] = HIBYTE(LOWORD(time)); 13718c2ecf20Sopenharmony_ci n->sas_addr[5] = LOBYTE(LOWORD(time)); 13728c2ecf20Sopenharmony_ci n->sas_addr[6] = a->pcid->bus->number; 13738c2ecf20Sopenharmony_ci n->sas_addr[7] = a->pcid->devfn; 13748c2ecf20Sopenharmony_ci} 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_civoid esas2r_nvram_get_defaults(struct esas2r_adapter *a, 13778c2ecf20Sopenharmony_ci struct esas2r_sas_nvram *nvram) 13788c2ecf20Sopenharmony_ci{ 13798c2ecf20Sopenharmony_ci u8 sas_addr[8]; 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci /* 13828c2ecf20Sopenharmony_ci * in case we are copying the defaults into the adapter, copy the SAS 13838c2ecf20Sopenharmony_ci * address out first. 13848c2ecf20Sopenharmony_ci */ 13858c2ecf20Sopenharmony_ci memcpy(&sas_addr[0], a->nvram->sas_addr, 8); 13868c2ecf20Sopenharmony_ci *nvram = default_sas_nvram; 13878c2ecf20Sopenharmony_ci memcpy(&nvram->sas_addr[0], &sas_addr[0], 8); 13888c2ecf20Sopenharmony_ci} 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_cibool esas2r_fm_api(struct esas2r_adapter *a, struct esas2r_flash_img *fi, 13918c2ecf20Sopenharmony_ci struct esas2r_request *rq, struct esas2r_sg_context *sgc) 13928c2ecf20Sopenharmony_ci{ 13938c2ecf20Sopenharmony_ci struct esas2r_flash_context *fc = &a->flash_context; 13948c2ecf20Sopenharmony_ci u8 j; 13958c2ecf20Sopenharmony_ci struct esas2r_component_header *ch; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci if (test_and_set_bit(AF_FLASH_LOCK, &a->flags)) { 13988c2ecf20Sopenharmony_ci /* flag was already set */ 13998c2ecf20Sopenharmony_ci fi->status = FI_STAT_BUSY; 14008c2ecf20Sopenharmony_ci return false; 14018c2ecf20Sopenharmony_ci } 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci memcpy(&fc->sgc, sgc, sizeof(struct esas2r_sg_context)); 14048c2ecf20Sopenharmony_ci sgc = &fc->sgc; 14058c2ecf20Sopenharmony_ci fc->fi = fi; 14068c2ecf20Sopenharmony_ci fc->sgc_offset = sgc->cur_offset; 14078c2ecf20Sopenharmony_ci rq->req_stat = RS_SUCCESS; 14088c2ecf20Sopenharmony_ci rq->interrupt_cx = fc; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci switch (fi->fi_version) { 14118c2ecf20Sopenharmony_ci case FI_VERSION_1: 14128c2ecf20Sopenharmony_ci fc->scratch = ((struct esas2r_flash_img *)fi)->scratch_buf; 14138c2ecf20Sopenharmony_ci fc->num_comps = FI_NUM_COMPS_V1; 14148c2ecf20Sopenharmony_ci fc->fi_hdr_len = sizeof(struct esas2r_flash_img); 14158c2ecf20Sopenharmony_ci break; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci default: 14188c2ecf20Sopenharmony_ci return complete_fmapi_req(a, rq, FI_STAT_IMG_VER); 14198c2ecf20Sopenharmony_ci } 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci if (test_bit(AF_DEGRADED_MODE, &a->flags)) 14228c2ecf20Sopenharmony_ci return complete_fmapi_req(a, rq, FI_STAT_DEGRADED); 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci switch (fi->action) { 14258c2ecf20Sopenharmony_ci case FI_ACT_DOWN: /* Download the components */ 14268c2ecf20Sopenharmony_ci /* Verify the format of the flash image */ 14278c2ecf20Sopenharmony_ci if (!verify_fi(a, fc)) 14288c2ecf20Sopenharmony_ci return complete_fmapi_req(a, rq, fi->status); 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci /* Adjust the BIOS fields that are dependent on the HBA */ 14318c2ecf20Sopenharmony_ci ch = &fi->cmp_hdr[CH_IT_BIOS]; 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci if (ch->length) 14348c2ecf20Sopenharmony_ci fix_bios(a, fi); 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci /* Adjust the EFI fields that are dependent on the HBA */ 14378c2ecf20Sopenharmony_ci ch = &fi->cmp_hdr[CH_IT_EFI]; 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci if (ch->length) 14408c2ecf20Sopenharmony_ci fix_efi(a, fi); 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci /* 14438c2ecf20Sopenharmony_ci * Since the image was just modified, compute the checksum on 14448c2ecf20Sopenharmony_ci * the modified image. First update the CRC for the composite 14458c2ecf20Sopenharmony_ci * expansion ROM image. 14468c2ecf20Sopenharmony_ci */ 14478c2ecf20Sopenharmony_ci fi->checksum = calc_fi_checksum(fc); 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci /* Disable the heartbeat */ 14508c2ecf20Sopenharmony_ci esas2r_disable_heartbeat(a); 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci /* Now start up the download sequence */ 14538c2ecf20Sopenharmony_ci fc->task = FMTSK_ERASE_BOOT; 14548c2ecf20Sopenharmony_ci fc->func = VDA_FLASH_BEGINW; 14558c2ecf20Sopenharmony_ci fc->comp_typ = CH_IT_CFG; 14568c2ecf20Sopenharmony_ci fc->flsh_addr = FLS_OFFSET_BOOT; 14578c2ecf20Sopenharmony_ci fc->sgc.length = FLS_LENGTH_BOOT; 14588c2ecf20Sopenharmony_ci fc->sgc.cur_offset = NULL; 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci /* Setup the callback address */ 14618c2ecf20Sopenharmony_ci fc->interrupt_cb = fw_download_proc; 14628c2ecf20Sopenharmony_ci break; 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci case FI_ACT_UPSZ: /* Get upload sizes */ 14658c2ecf20Sopenharmony_ci fi->adap_typ = get_fi_adap_type(a); 14668c2ecf20Sopenharmony_ci fi->flags = 0; 14678c2ecf20Sopenharmony_ci fi->num_comps = fc->num_comps; 14688c2ecf20Sopenharmony_ci fi->length = fc->fi_hdr_len; 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci /* Report the type of boot image in the rel_version string */ 14718c2ecf20Sopenharmony_ci memcpy(fi->rel_version, a->image_type, 14728c2ecf20Sopenharmony_ci sizeof(fi->rel_version)); 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci /* Build the component headers */ 14758c2ecf20Sopenharmony_ci for (j = 0, ch = fi->cmp_hdr; 14768c2ecf20Sopenharmony_ci j < fi->num_comps; 14778c2ecf20Sopenharmony_ci j++, ch++) { 14788c2ecf20Sopenharmony_ci ch->img_type = j; 14798c2ecf20Sopenharmony_ci ch->status = CH_STAT_PENDING; 14808c2ecf20Sopenharmony_ci ch->length = 0; 14818c2ecf20Sopenharmony_ci ch->version = 0xffffffff; 14828c2ecf20Sopenharmony_ci ch->image_offset = 0; 14838c2ecf20Sopenharmony_ci ch->pad[0] = 0; 14848c2ecf20Sopenharmony_ci ch->pad[1] = 0; 14858c2ecf20Sopenharmony_ci } 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci if (a->flash_ver != 0) { 14888c2ecf20Sopenharmony_ci fi->cmp_hdr[CH_IT_BIOS].version = 14898c2ecf20Sopenharmony_ci fi->cmp_hdr[CH_IT_MAC].version = 14908c2ecf20Sopenharmony_ci fi->cmp_hdr[CH_IT_EFI].version = 14918c2ecf20Sopenharmony_ci fi->cmp_hdr[CH_IT_CFG].version 14928c2ecf20Sopenharmony_ci = a->flash_ver; 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci fi->cmp_hdr[CH_IT_BIOS].status = 14958c2ecf20Sopenharmony_ci fi->cmp_hdr[CH_IT_MAC].status = 14968c2ecf20Sopenharmony_ci fi->cmp_hdr[CH_IT_EFI].status = 14978c2ecf20Sopenharmony_ci fi->cmp_hdr[CH_IT_CFG].status = 14988c2ecf20Sopenharmony_ci CH_STAT_SUCCESS; 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci return complete_fmapi_req(a, rq, FI_STAT_SUCCESS); 15018c2ecf20Sopenharmony_ci } 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci fallthrough; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci case FI_ACT_UP: /* Upload the components */ 15068c2ecf20Sopenharmony_ci default: 15078c2ecf20Sopenharmony_ci return complete_fmapi_req(a, rq, FI_STAT_INVALID); 15088c2ecf20Sopenharmony_ci } 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci /* 15118c2ecf20Sopenharmony_ci * If we make it here, fc has been setup to do the first task. Call 15128c2ecf20Sopenharmony_ci * load_image to format the request, start it, and get out. The 15138c2ecf20Sopenharmony_ci * interrupt code will call the callback when the first message is 15148c2ecf20Sopenharmony_ci * complete. 15158c2ecf20Sopenharmony_ci */ 15168c2ecf20Sopenharmony_ci if (!load_image(a, rq)) 15178c2ecf20Sopenharmony_ci return complete_fmapi_req(a, rq, FI_STAT_FAILED); 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci esas2r_start_request(a, rq); 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci return true; 15228c2ecf20Sopenharmony_ci} 1523