162306a36Sopenharmony_ci 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/drivers/scsi/esas2r/esas2r_flash.c 462306a36Sopenharmony_ci * For use with ATTO ExpressSAS R6xx SAS/SATA RAID controllers 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (c) 2001-2013 ATTO Technology, Inc. 762306a36Sopenharmony_ci * (mailto:linuxdrivers@attotech.com) 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or 1062306a36Sopenharmony_ci * modify it under the terms of the GNU General Public License 1162306a36Sopenharmony_ci * as published by the Free Software Foundation; either version 2 1262306a36Sopenharmony_ci * of the License, or (at your option) any later version. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful, 1562306a36Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 1662306a36Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1762306a36Sopenharmony_ci * GNU General Public License for more details. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * NO WARRANTY 2062306a36Sopenharmony_ci * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR 2162306a36Sopenharmony_ci * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT 2262306a36Sopenharmony_ci * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, 2362306a36Sopenharmony_ci * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is 2462306a36Sopenharmony_ci * solely responsible for determining the appropriateness of using and 2562306a36Sopenharmony_ci * distributing the Program and assumes all risks associated with its 2662306a36Sopenharmony_ci * exercise of rights under this Agreement, including but not limited to 2762306a36Sopenharmony_ci * the risks and costs of program errors, damage to or loss of data, 2862306a36Sopenharmony_ci * programs or equipment, and unavailability or interruption of operations. 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * DISCLAIMER OF LIABILITY 3162306a36Sopenharmony_ci * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY 3262306a36Sopenharmony_ci * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3362306a36Sopenharmony_ci * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND 3462306a36Sopenharmony_ci * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 3562306a36Sopenharmony_ci * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 3662306a36Sopenharmony_ci * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED 3762306a36Sopenharmony_ci * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License 4062306a36Sopenharmony_ci * along with this program; if not, write to the Free Software 4162306a36Sopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 4262306a36Sopenharmony_ci * USA. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#include "esas2r.h" 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* local macro defs */ 4862306a36Sopenharmony_ci#define esas2r_nvramcalc_cksum(n) \ 4962306a36Sopenharmony_ci (esas2r_calc_byte_cksum((u8 *)(n), sizeof(struct esas2r_sas_nvram), \ 5062306a36Sopenharmony_ci SASNVR_CKSUM_SEED)) 5162306a36Sopenharmony_ci#define esas2r_nvramcalc_xor_cksum(n) \ 5262306a36Sopenharmony_ci (esas2r_calc_byte_xor_cksum((u8 *)(n), \ 5362306a36Sopenharmony_ci sizeof(struct esas2r_sas_nvram), 0)) 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define ESAS2R_FS_DRVR_VER 2 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic struct esas2r_sas_nvram default_sas_nvram = { 5862306a36Sopenharmony_ci { 'E', 'S', 'A', 'S' }, /* signature */ 5962306a36Sopenharmony_ci SASNVR_VERSION, /* version */ 6062306a36Sopenharmony_ci 0, /* checksum */ 6162306a36Sopenharmony_ci 31, /* max_lun_for_target */ 6262306a36Sopenharmony_ci SASNVR_PCILAT_MAX, /* pci_latency */ 6362306a36Sopenharmony_ci SASNVR1_BOOT_DRVR, /* options1 */ 6462306a36Sopenharmony_ci SASNVR2_HEARTBEAT | SASNVR2_SINGLE_BUS /* options2 */ 6562306a36Sopenharmony_ci | SASNVR2_SW_MUX_CTRL, 6662306a36Sopenharmony_ci SASNVR_COAL_DIS, /* int_coalescing */ 6762306a36Sopenharmony_ci SASNVR_CMDTHR_NONE, /* cmd_throttle */ 6862306a36Sopenharmony_ci 3, /* dev_wait_time */ 6962306a36Sopenharmony_ci 1, /* dev_wait_count */ 7062306a36Sopenharmony_ci 0, /* spin_up_delay */ 7162306a36Sopenharmony_ci 0, /* ssp_align_rate */ 7262306a36Sopenharmony_ci { 0x50, 0x01, 0x08, 0x60, /* sas_addr */ 7362306a36Sopenharmony_ci 0x00, 0x00, 0x00, 0x00 }, 7462306a36Sopenharmony_ci { SASNVR_SPEED_AUTO }, /* phy_speed */ 7562306a36Sopenharmony_ci { SASNVR_MUX_DISABLED }, /* SAS multiplexing */ 7662306a36Sopenharmony_ci { 0 }, /* phy_flags */ 7762306a36Sopenharmony_ci SASNVR_SORT_SAS_ADDR, /* sort_type */ 7862306a36Sopenharmony_ci 3, /* dpm_reqcmd_lmt */ 7962306a36Sopenharmony_ci 3, /* dpm_stndby_time */ 8062306a36Sopenharmony_ci 0, /* dpm_active_time */ 8162306a36Sopenharmony_ci { 0 }, /* phy_target_id */ 8262306a36Sopenharmony_ci SASNVR_VSMH_DISABLED, /* virt_ses_mode */ 8362306a36Sopenharmony_ci SASNVR_RWM_DEFAULT, /* read_write_mode */ 8462306a36Sopenharmony_ci 0, /* link down timeout */ 8562306a36Sopenharmony_ci { 0 } /* reserved */ 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic u8 cmd_to_fls_func[] = { 8962306a36Sopenharmony_ci 0xFF, 9062306a36Sopenharmony_ci VDA_FLASH_READ, 9162306a36Sopenharmony_ci VDA_FLASH_BEGINW, 9262306a36Sopenharmony_ci VDA_FLASH_WRITE, 9362306a36Sopenharmony_ci VDA_FLASH_COMMIT, 9462306a36Sopenharmony_ci VDA_FLASH_CANCEL 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic u8 esas2r_calc_byte_xor_cksum(u8 *addr, u32 len, u8 seed) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci u32 cksum = seed; 10062306a36Sopenharmony_ci u8 *p = (u8 *)&cksum; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci while (len) { 10362306a36Sopenharmony_ci if (((uintptr_t)addr & 3) == 0) 10462306a36Sopenharmony_ci break; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci cksum = cksum ^ *addr; 10762306a36Sopenharmony_ci addr++; 10862306a36Sopenharmony_ci len--; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci while (len >= sizeof(u32)) { 11162306a36Sopenharmony_ci cksum = cksum ^ *(u32 *)addr; 11262306a36Sopenharmony_ci addr += 4; 11362306a36Sopenharmony_ci len -= 4; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci while (len--) { 11662306a36Sopenharmony_ci cksum = cksum ^ *addr; 11762306a36Sopenharmony_ci addr++; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci return p[0] ^ p[1] ^ p[2] ^ p[3]; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic u8 esas2r_calc_byte_cksum(void *addr, u32 len, u8 seed) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci u8 *p = (u8 *)addr; 12562306a36Sopenharmony_ci u8 cksum = seed; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci while (len--) 12862306a36Sopenharmony_ci cksum = cksum + p[len]; 12962306a36Sopenharmony_ci return cksum; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* Interrupt callback to process FM API write requests. */ 13362306a36Sopenharmony_cistatic void esas2r_fmapi_callback(struct esas2r_adapter *a, 13462306a36Sopenharmony_ci struct esas2r_request *rq) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci struct atto_vda_flash_req *vrq = &rq->vrq->flash; 13762306a36Sopenharmony_ci struct esas2r_flash_context *fc = 13862306a36Sopenharmony_ci (struct esas2r_flash_context *)rq->interrupt_cx; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (rq->req_stat == RS_SUCCESS) { 14162306a36Sopenharmony_ci /* Last request was successful. See what to do now. */ 14262306a36Sopenharmony_ci switch (vrq->sub_func) { 14362306a36Sopenharmony_ci case VDA_FLASH_BEGINW: 14462306a36Sopenharmony_ci if (fc->sgc.cur_offset == NULL) 14562306a36Sopenharmony_ci goto commit; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci vrq->sub_func = VDA_FLASH_WRITE; 14862306a36Sopenharmony_ci rq->req_stat = RS_PENDING; 14962306a36Sopenharmony_ci break; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci case VDA_FLASH_WRITE: 15262306a36Sopenharmony_cicommit: 15362306a36Sopenharmony_ci vrq->sub_func = VDA_FLASH_COMMIT; 15462306a36Sopenharmony_ci rq->req_stat = RS_PENDING; 15562306a36Sopenharmony_ci rq->interrupt_cb = fc->interrupt_cb; 15662306a36Sopenharmony_ci break; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci default: 15962306a36Sopenharmony_ci break; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (rq->req_stat != RS_PENDING) 16462306a36Sopenharmony_ci /* 16562306a36Sopenharmony_ci * All done. call the real callback to complete the FM API 16662306a36Sopenharmony_ci * request. We should only get here if a BEGINW or WRITE 16762306a36Sopenharmony_ci * operation failed. 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ci (*fc->interrupt_cb)(a, rq); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci/* 17362306a36Sopenharmony_ci * Build a flash request based on the flash context. The request status 17462306a36Sopenharmony_ci * is filled in on an error. 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_cistatic void build_flash_msg(struct esas2r_adapter *a, 17762306a36Sopenharmony_ci struct esas2r_request *rq) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct esas2r_flash_context *fc = 18062306a36Sopenharmony_ci (struct esas2r_flash_context *)rq->interrupt_cx; 18162306a36Sopenharmony_ci struct esas2r_sg_context *sgc = &fc->sgc; 18262306a36Sopenharmony_ci u8 cksum = 0; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* calculate the checksum */ 18562306a36Sopenharmony_ci if (fc->func == VDA_FLASH_BEGINW) { 18662306a36Sopenharmony_ci if (sgc->cur_offset) 18762306a36Sopenharmony_ci cksum = esas2r_calc_byte_xor_cksum(sgc->cur_offset, 18862306a36Sopenharmony_ci sgc->length, 18962306a36Sopenharmony_ci 0); 19062306a36Sopenharmony_ci rq->interrupt_cb = esas2r_fmapi_callback; 19162306a36Sopenharmony_ci } else { 19262306a36Sopenharmony_ci rq->interrupt_cb = fc->interrupt_cb; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci esas2r_build_flash_req(a, 19562306a36Sopenharmony_ci rq, 19662306a36Sopenharmony_ci fc->func, 19762306a36Sopenharmony_ci cksum, 19862306a36Sopenharmony_ci fc->flsh_addr, 19962306a36Sopenharmony_ci sgc->length); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci esas2r_rq_free_sg_lists(rq, a); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* 20462306a36Sopenharmony_ci * remember the length we asked for. we have to keep track of 20562306a36Sopenharmony_ci * the current amount done so we know how much to compare when 20662306a36Sopenharmony_ci * doing the verification phase. 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_ci fc->curr_len = fc->sgc.length; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (sgc->cur_offset) { 21162306a36Sopenharmony_ci /* setup the S/G context to build the S/G table */ 21262306a36Sopenharmony_ci esas2r_sgc_init(sgc, a, rq, &rq->vrq->flash.data.sge[0]); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (!esas2r_build_sg_list(a, rq, sgc)) { 21562306a36Sopenharmony_ci rq->req_stat = RS_BUSY; 21662306a36Sopenharmony_ci return; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci } else { 21962306a36Sopenharmony_ci fc->sgc.length = 0; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* update the flsh_addr to the next one to write to */ 22362306a36Sopenharmony_ci fc->flsh_addr += fc->curr_len; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/* determine the method to process the flash request */ 22762306a36Sopenharmony_cistatic bool load_image(struct esas2r_adapter *a, struct esas2r_request *rq) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci /* 23062306a36Sopenharmony_ci * assume we have more to do. if we return with the status set to 23162306a36Sopenharmony_ci * RS_PENDING, FM API tasks will continue. 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_ci rq->req_stat = RS_PENDING; 23462306a36Sopenharmony_ci if (test_bit(AF_DEGRADED_MODE, &a->flags)) 23562306a36Sopenharmony_ci /* not supported for now */; 23662306a36Sopenharmony_ci else 23762306a36Sopenharmony_ci build_flash_msg(a, rq); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return rq->req_stat == RS_PENDING; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci/* boot image fixer uppers called before downloading the image. */ 24362306a36Sopenharmony_cistatic void fix_bios(struct esas2r_adapter *a, struct esas2r_flash_img *fi) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct esas2r_component_header *ch = &fi->cmp_hdr[CH_IT_BIOS]; 24662306a36Sopenharmony_ci struct esas2r_pc_image *pi; 24762306a36Sopenharmony_ci struct esas2r_boot_header *bh; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci pi = (struct esas2r_pc_image *)((u8 *)fi + ch->image_offset); 25062306a36Sopenharmony_ci bh = 25162306a36Sopenharmony_ci (struct esas2r_boot_header *)((u8 *)pi + 25262306a36Sopenharmony_ci le16_to_cpu(pi->header_offset)); 25362306a36Sopenharmony_ci bh->device_id = cpu_to_le16(a->pcid->device); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* Recalculate the checksum in the PNP header if there */ 25662306a36Sopenharmony_ci if (pi->pnp_offset) { 25762306a36Sopenharmony_ci u8 *pnp_header_bytes = 25862306a36Sopenharmony_ci ((u8 *)pi + le16_to_cpu(pi->pnp_offset)); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* Identifier - dword that starts at byte 10 */ 26162306a36Sopenharmony_ci *((u32 *)&pnp_header_bytes[10]) = 26262306a36Sopenharmony_ci cpu_to_le32(MAKEDWORD(a->pcid->subsystem_vendor, 26362306a36Sopenharmony_ci a->pcid->subsystem_device)); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* Checksum - byte 9 */ 26662306a36Sopenharmony_ci pnp_header_bytes[9] -= esas2r_calc_byte_cksum(pnp_header_bytes, 26762306a36Sopenharmony_ci 32, 0); 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* Recalculate the checksum needed by the PC */ 27162306a36Sopenharmony_ci pi->checksum = pi->checksum - 27262306a36Sopenharmony_ci esas2r_calc_byte_cksum((u8 *)pi, ch->length, 0); 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic void fix_efi(struct esas2r_adapter *a, struct esas2r_flash_img *fi) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct esas2r_component_header *ch = &fi->cmp_hdr[CH_IT_EFI]; 27862306a36Sopenharmony_ci u32 len = ch->length; 27962306a36Sopenharmony_ci u32 offset = ch->image_offset; 28062306a36Sopenharmony_ci struct esas2r_efi_image *ei; 28162306a36Sopenharmony_ci struct esas2r_boot_header *bh; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci while (len) { 28462306a36Sopenharmony_ci u32 thislen; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci ei = (struct esas2r_efi_image *)((u8 *)fi + offset); 28762306a36Sopenharmony_ci bh = (struct esas2r_boot_header *)((u8 *)ei + 28862306a36Sopenharmony_ci le16_to_cpu( 28962306a36Sopenharmony_ci ei->header_offset)); 29062306a36Sopenharmony_ci bh->device_id = cpu_to_le16(a->pcid->device); 29162306a36Sopenharmony_ci thislen = (u32)le16_to_cpu(bh->image_length) * 512; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (thislen > len) 29462306a36Sopenharmony_ci break; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci len -= thislen; 29762306a36Sopenharmony_ci offset += thislen; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/* Complete a FM API request with the specified status. */ 30262306a36Sopenharmony_cistatic bool complete_fmapi_req(struct esas2r_adapter *a, 30362306a36Sopenharmony_ci struct esas2r_request *rq, u8 fi_stat) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci struct esas2r_flash_context *fc = 30662306a36Sopenharmony_ci (struct esas2r_flash_context *)rq->interrupt_cx; 30762306a36Sopenharmony_ci struct esas2r_flash_img *fi = fc->fi; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci fi->status = fi_stat; 31062306a36Sopenharmony_ci fi->driver_error = rq->req_stat; 31162306a36Sopenharmony_ci rq->interrupt_cb = NULL; 31262306a36Sopenharmony_ci rq->req_stat = RS_SUCCESS; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (fi_stat != FI_STAT_IMG_VER) 31562306a36Sopenharmony_ci memset(fc->scratch, 0, FM_BUF_SZ); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci esas2r_enable_heartbeat(a); 31862306a36Sopenharmony_ci clear_bit(AF_FLASH_LOCK, &a->flags); 31962306a36Sopenharmony_ci return false; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci/* Process each phase of the flash download process. */ 32362306a36Sopenharmony_cistatic void fw_download_proc(struct esas2r_adapter *a, 32462306a36Sopenharmony_ci struct esas2r_request *rq) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct esas2r_flash_context *fc = 32762306a36Sopenharmony_ci (struct esas2r_flash_context *)rq->interrupt_cx; 32862306a36Sopenharmony_ci struct esas2r_flash_img *fi = fc->fi; 32962306a36Sopenharmony_ci struct esas2r_component_header *ch; 33062306a36Sopenharmony_ci u32 len; 33162306a36Sopenharmony_ci u8 *p, *q; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* If the previous operation failed, just return. */ 33462306a36Sopenharmony_ci if (rq->req_stat != RS_SUCCESS) 33562306a36Sopenharmony_ci goto error; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* 33862306a36Sopenharmony_ci * If an upload just completed and the compare length is non-zero, 33962306a36Sopenharmony_ci * then we just read back part of the image we just wrote. verify the 34062306a36Sopenharmony_ci * section and continue reading until the entire image is verified. 34162306a36Sopenharmony_ci */ 34262306a36Sopenharmony_ci if (fc->func == VDA_FLASH_READ 34362306a36Sopenharmony_ci && fc->cmp_len) { 34462306a36Sopenharmony_ci ch = &fi->cmp_hdr[fc->comp_typ]; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci p = fc->scratch; 34762306a36Sopenharmony_ci q = (u8 *)fi /* start of the whole gob */ 34862306a36Sopenharmony_ci + ch->image_offset /* start of the current image */ 34962306a36Sopenharmony_ci + ch->length /* end of the current image */ 35062306a36Sopenharmony_ci - fc->cmp_len; /* where we are now */ 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci /* 35362306a36Sopenharmony_ci * NOTE - curr_len is the exact count of bytes for the read 35462306a36Sopenharmony_ci * even when the end is read and its not a full buffer 35562306a36Sopenharmony_ci */ 35662306a36Sopenharmony_ci for (len = fc->curr_len; len; len--) 35762306a36Sopenharmony_ci if (*p++ != *q++) 35862306a36Sopenharmony_ci goto error; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci fc->cmp_len -= fc->curr_len; /* # left to compare */ 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* Update fc and determine the length for the next upload */ 36362306a36Sopenharmony_ci if (fc->cmp_len > FM_BUF_SZ) 36462306a36Sopenharmony_ci fc->sgc.length = FM_BUF_SZ; 36562306a36Sopenharmony_ci else 36662306a36Sopenharmony_ci fc->sgc.length = fc->cmp_len; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci fc->sgc.cur_offset = fc->sgc_offset + 36962306a36Sopenharmony_ci ((u8 *)fc->scratch - (u8 *)fi); 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* 37362306a36Sopenharmony_ci * This code uses a 'while' statement since the next component may 37462306a36Sopenharmony_ci * have a length = zero. This can happen since some components are 37562306a36Sopenharmony_ci * not required. At the end of this 'while' we set up the length 37662306a36Sopenharmony_ci * for the next request and therefore sgc.length can be = 0. 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_ci while (fc->sgc.length == 0) { 37962306a36Sopenharmony_ci ch = &fi->cmp_hdr[fc->comp_typ]; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci switch (fc->task) { 38262306a36Sopenharmony_ci case FMTSK_ERASE_BOOT: 38362306a36Sopenharmony_ci /* the BIOS image is written next */ 38462306a36Sopenharmony_ci ch = &fi->cmp_hdr[CH_IT_BIOS]; 38562306a36Sopenharmony_ci if (ch->length == 0) 38662306a36Sopenharmony_ci goto no_bios; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci fc->task = FMTSK_WRTBIOS; 38962306a36Sopenharmony_ci fc->func = VDA_FLASH_BEGINW; 39062306a36Sopenharmony_ci fc->comp_typ = CH_IT_BIOS; 39162306a36Sopenharmony_ci fc->flsh_addr = FLS_OFFSET_BOOT; 39262306a36Sopenharmony_ci fc->sgc.length = ch->length; 39362306a36Sopenharmony_ci fc->sgc.cur_offset = fc->sgc_offset + 39462306a36Sopenharmony_ci ch->image_offset; 39562306a36Sopenharmony_ci break; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci case FMTSK_WRTBIOS: 39862306a36Sopenharmony_ci /* 39962306a36Sopenharmony_ci * The BIOS image has been written - read it and 40062306a36Sopenharmony_ci * verify it 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_ci fc->task = FMTSK_READBIOS; 40362306a36Sopenharmony_ci fc->func = VDA_FLASH_READ; 40462306a36Sopenharmony_ci fc->flsh_addr = FLS_OFFSET_BOOT; 40562306a36Sopenharmony_ci fc->cmp_len = ch->length; 40662306a36Sopenharmony_ci fc->sgc.length = FM_BUF_SZ; 40762306a36Sopenharmony_ci fc->sgc.cur_offset = fc->sgc_offset 40862306a36Sopenharmony_ci + ((u8 *)fc->scratch - 40962306a36Sopenharmony_ci (u8 *)fi); 41062306a36Sopenharmony_ci break; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci case FMTSK_READBIOS: 41362306a36Sopenharmony_cino_bios: 41462306a36Sopenharmony_ci /* 41562306a36Sopenharmony_ci * Mark the component header status for the image 41662306a36Sopenharmony_ci * completed 41762306a36Sopenharmony_ci */ 41862306a36Sopenharmony_ci ch->status = CH_STAT_SUCCESS; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* The MAC image is written next */ 42162306a36Sopenharmony_ci ch = &fi->cmp_hdr[CH_IT_MAC]; 42262306a36Sopenharmony_ci if (ch->length == 0) 42362306a36Sopenharmony_ci goto no_mac; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci fc->task = FMTSK_WRTMAC; 42662306a36Sopenharmony_ci fc->func = VDA_FLASH_BEGINW; 42762306a36Sopenharmony_ci fc->comp_typ = CH_IT_MAC; 42862306a36Sopenharmony_ci fc->flsh_addr = FLS_OFFSET_BOOT 42962306a36Sopenharmony_ci + fi->cmp_hdr[CH_IT_BIOS].length; 43062306a36Sopenharmony_ci fc->sgc.length = ch->length; 43162306a36Sopenharmony_ci fc->sgc.cur_offset = fc->sgc_offset + 43262306a36Sopenharmony_ci ch->image_offset; 43362306a36Sopenharmony_ci break; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci case FMTSK_WRTMAC: 43662306a36Sopenharmony_ci /* The MAC image has been written - read and verify */ 43762306a36Sopenharmony_ci fc->task = FMTSK_READMAC; 43862306a36Sopenharmony_ci fc->func = VDA_FLASH_READ; 43962306a36Sopenharmony_ci fc->flsh_addr -= ch->length; 44062306a36Sopenharmony_ci fc->cmp_len = ch->length; 44162306a36Sopenharmony_ci fc->sgc.length = FM_BUF_SZ; 44262306a36Sopenharmony_ci fc->sgc.cur_offset = fc->sgc_offset 44362306a36Sopenharmony_ci + ((u8 *)fc->scratch - 44462306a36Sopenharmony_ci (u8 *)fi); 44562306a36Sopenharmony_ci break; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci case FMTSK_READMAC: 44862306a36Sopenharmony_cino_mac: 44962306a36Sopenharmony_ci /* 45062306a36Sopenharmony_ci * Mark the component header status for the image 45162306a36Sopenharmony_ci * completed 45262306a36Sopenharmony_ci */ 45362306a36Sopenharmony_ci ch->status = CH_STAT_SUCCESS; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* The EFI image is written next */ 45662306a36Sopenharmony_ci ch = &fi->cmp_hdr[CH_IT_EFI]; 45762306a36Sopenharmony_ci if (ch->length == 0) 45862306a36Sopenharmony_ci goto no_efi; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci fc->task = FMTSK_WRTEFI; 46162306a36Sopenharmony_ci fc->func = VDA_FLASH_BEGINW; 46262306a36Sopenharmony_ci fc->comp_typ = CH_IT_EFI; 46362306a36Sopenharmony_ci fc->flsh_addr = FLS_OFFSET_BOOT 46462306a36Sopenharmony_ci + fi->cmp_hdr[CH_IT_BIOS].length 46562306a36Sopenharmony_ci + fi->cmp_hdr[CH_IT_MAC].length; 46662306a36Sopenharmony_ci fc->sgc.length = ch->length; 46762306a36Sopenharmony_ci fc->sgc.cur_offset = fc->sgc_offset + 46862306a36Sopenharmony_ci ch->image_offset; 46962306a36Sopenharmony_ci break; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci case FMTSK_WRTEFI: 47262306a36Sopenharmony_ci /* The EFI image has been written - read and verify */ 47362306a36Sopenharmony_ci fc->task = FMTSK_READEFI; 47462306a36Sopenharmony_ci fc->func = VDA_FLASH_READ; 47562306a36Sopenharmony_ci fc->flsh_addr -= ch->length; 47662306a36Sopenharmony_ci fc->cmp_len = ch->length; 47762306a36Sopenharmony_ci fc->sgc.length = FM_BUF_SZ; 47862306a36Sopenharmony_ci fc->sgc.cur_offset = fc->sgc_offset 47962306a36Sopenharmony_ci + ((u8 *)fc->scratch - 48062306a36Sopenharmony_ci (u8 *)fi); 48162306a36Sopenharmony_ci break; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci case FMTSK_READEFI: 48462306a36Sopenharmony_cino_efi: 48562306a36Sopenharmony_ci /* 48662306a36Sopenharmony_ci * Mark the component header status for the image 48762306a36Sopenharmony_ci * completed 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_ci ch->status = CH_STAT_SUCCESS; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci /* The CFG image is written next */ 49262306a36Sopenharmony_ci ch = &fi->cmp_hdr[CH_IT_CFG]; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (ch->length == 0) 49562306a36Sopenharmony_ci goto no_cfg; 49662306a36Sopenharmony_ci fc->task = FMTSK_WRTCFG; 49762306a36Sopenharmony_ci fc->func = VDA_FLASH_BEGINW; 49862306a36Sopenharmony_ci fc->comp_typ = CH_IT_CFG; 49962306a36Sopenharmony_ci fc->flsh_addr = FLS_OFFSET_CPYR - ch->length; 50062306a36Sopenharmony_ci fc->sgc.length = ch->length; 50162306a36Sopenharmony_ci fc->sgc.cur_offset = fc->sgc_offset + 50262306a36Sopenharmony_ci ch->image_offset; 50362306a36Sopenharmony_ci break; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci case FMTSK_WRTCFG: 50662306a36Sopenharmony_ci /* The CFG image has been written - read and verify */ 50762306a36Sopenharmony_ci fc->task = FMTSK_READCFG; 50862306a36Sopenharmony_ci fc->func = VDA_FLASH_READ; 50962306a36Sopenharmony_ci fc->flsh_addr = FLS_OFFSET_CPYR - ch->length; 51062306a36Sopenharmony_ci fc->cmp_len = ch->length; 51162306a36Sopenharmony_ci fc->sgc.length = FM_BUF_SZ; 51262306a36Sopenharmony_ci fc->sgc.cur_offset = fc->sgc_offset 51362306a36Sopenharmony_ci + ((u8 *)fc->scratch - 51462306a36Sopenharmony_ci (u8 *)fi); 51562306a36Sopenharmony_ci break; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci case FMTSK_READCFG: 51862306a36Sopenharmony_cino_cfg: 51962306a36Sopenharmony_ci /* 52062306a36Sopenharmony_ci * Mark the component header status for the image 52162306a36Sopenharmony_ci * completed 52262306a36Sopenharmony_ci */ 52362306a36Sopenharmony_ci ch->status = CH_STAT_SUCCESS; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* 52662306a36Sopenharmony_ci * The download is complete. If in degraded mode, 52762306a36Sopenharmony_ci * attempt a chip reset. 52862306a36Sopenharmony_ci */ 52962306a36Sopenharmony_ci if (test_bit(AF_DEGRADED_MODE, &a->flags)) 53062306a36Sopenharmony_ci esas2r_local_reset_adapter(a); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci a->flash_ver = fi->cmp_hdr[CH_IT_BIOS].version; 53362306a36Sopenharmony_ci esas2r_print_flash_rev(a); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* Update the type of boot image on the card */ 53662306a36Sopenharmony_ci memcpy(a->image_type, fi->rel_version, 53762306a36Sopenharmony_ci sizeof(fi->rel_version)); 53862306a36Sopenharmony_ci complete_fmapi_req(a, rq, FI_STAT_SUCCESS); 53962306a36Sopenharmony_ci return; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* If verifying, don't try reading more than what's there */ 54362306a36Sopenharmony_ci if (fc->func == VDA_FLASH_READ 54462306a36Sopenharmony_ci && fc->sgc.length > fc->cmp_len) 54562306a36Sopenharmony_ci fc->sgc.length = fc->cmp_len; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* Build the request to perform the next action */ 54962306a36Sopenharmony_ci if (!load_image(a, rq)) { 55062306a36Sopenharmony_cierror: 55162306a36Sopenharmony_ci if (fc->comp_typ < fi->num_comps) { 55262306a36Sopenharmony_ci ch = &fi->cmp_hdr[fc->comp_typ]; 55362306a36Sopenharmony_ci ch->status = CH_STAT_FAILED; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci complete_fmapi_req(a, rq, FI_STAT_FAILED); 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci/* Determine the flash image adaptyp for this adapter */ 56162306a36Sopenharmony_cistatic u8 get_fi_adap_type(struct esas2r_adapter *a) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci u8 type; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci /* use the device ID to get the correct adap_typ for this HBA */ 56662306a36Sopenharmony_ci switch (a->pcid->device) { 56762306a36Sopenharmony_ci case ATTO_DID_INTEL_IOP348: 56862306a36Sopenharmony_ci type = FI_AT_SUN_LAKE; 56962306a36Sopenharmony_ci break; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci case ATTO_DID_MV_88RC9580: 57262306a36Sopenharmony_ci case ATTO_DID_MV_88RC9580TS: 57362306a36Sopenharmony_ci case ATTO_DID_MV_88RC9580TSE: 57462306a36Sopenharmony_ci case ATTO_DID_MV_88RC9580TL: 57562306a36Sopenharmony_ci type = FI_AT_MV_9580; 57662306a36Sopenharmony_ci break; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci default: 57962306a36Sopenharmony_ci type = FI_AT_UNKNWN; 58062306a36Sopenharmony_ci break; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci return type; 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci/* Size of config + copyright + flash_ver images, 0 for failure. */ 58762306a36Sopenharmony_cistatic u32 chk_cfg(u8 *cfg, u32 length, u32 *flash_ver) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci u16 *pw = (u16 *)cfg - 1; 59062306a36Sopenharmony_ci u32 sz = 0; 59162306a36Sopenharmony_ci u32 len = length; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (len == 0) 59462306a36Sopenharmony_ci len = FM_BUF_SZ; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (flash_ver) 59762306a36Sopenharmony_ci *flash_ver = 0; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci while (true) { 60062306a36Sopenharmony_ci u16 type; 60162306a36Sopenharmony_ci u16 size; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci type = le16_to_cpu(*pw--); 60462306a36Sopenharmony_ci size = le16_to_cpu(*pw--); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci if (type != FBT_CPYR 60762306a36Sopenharmony_ci && type != FBT_SETUP 60862306a36Sopenharmony_ci && type != FBT_FLASH_VER) 60962306a36Sopenharmony_ci break; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (type == FBT_FLASH_VER 61262306a36Sopenharmony_ci && flash_ver) 61362306a36Sopenharmony_ci *flash_ver = le32_to_cpu(*(u32 *)(pw - 1)); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci sz += size + (2 * sizeof(u16)); 61662306a36Sopenharmony_ci pw -= size / sizeof(u16); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (sz > len - (2 * sizeof(u16))) 61962306a36Sopenharmony_ci break; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci /* See if we are comparing the size to the specified length */ 62362306a36Sopenharmony_ci if (length && sz != length) 62462306a36Sopenharmony_ci return 0; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci return sz; 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci/* Verify that the boot image is valid */ 63062306a36Sopenharmony_cistatic u8 chk_boot(u8 *boot_img, u32 length) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci struct esas2r_boot_image *bi = (struct esas2r_boot_image *)boot_img; 63362306a36Sopenharmony_ci u16 hdroffset = le16_to_cpu(bi->header_offset); 63462306a36Sopenharmony_ci struct esas2r_boot_header *bh; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci if (bi->signature != le16_to_cpu(0xaa55) 63762306a36Sopenharmony_ci || (long)hdroffset > 63862306a36Sopenharmony_ci (long)(65536L - sizeof(struct esas2r_boot_header)) 63962306a36Sopenharmony_ci || (hdroffset & 3) 64062306a36Sopenharmony_ci || (hdroffset < sizeof(struct esas2r_boot_image)) 64162306a36Sopenharmony_ci || ((u32)hdroffset + sizeof(struct esas2r_boot_header) > length)) 64262306a36Sopenharmony_ci return 0xff; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci bh = (struct esas2r_boot_header *)((char *)bi + hdroffset); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if (bh->signature[0] != 'P' 64762306a36Sopenharmony_ci || bh->signature[1] != 'C' 64862306a36Sopenharmony_ci || bh->signature[2] != 'I' 64962306a36Sopenharmony_ci || bh->signature[3] != 'R' 65062306a36Sopenharmony_ci || le16_to_cpu(bh->struct_length) < 65162306a36Sopenharmony_ci (u16)sizeof(struct esas2r_boot_header) 65262306a36Sopenharmony_ci || bh->class_code[2] != 0x01 65362306a36Sopenharmony_ci || bh->class_code[1] != 0x04 65462306a36Sopenharmony_ci || bh->class_code[0] != 0x00 65562306a36Sopenharmony_ci || (bh->code_type != CODE_TYPE_PC 65662306a36Sopenharmony_ci && bh->code_type != CODE_TYPE_OPEN 65762306a36Sopenharmony_ci && bh->code_type != CODE_TYPE_EFI)) 65862306a36Sopenharmony_ci return 0xff; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci return bh->code_type; 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci/* The sum of all the WORDS of the image */ 66462306a36Sopenharmony_cistatic u16 calc_fi_checksum(struct esas2r_flash_context *fc) 66562306a36Sopenharmony_ci{ 66662306a36Sopenharmony_ci struct esas2r_flash_img *fi = fc->fi; 66762306a36Sopenharmony_ci u16 cksum; 66862306a36Sopenharmony_ci u32 len; 66962306a36Sopenharmony_ci u16 *pw; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci for (len = (fi->length - fc->fi_hdr_len) / 2, 67262306a36Sopenharmony_ci pw = (u16 *)((u8 *)fi + fc->fi_hdr_len), 67362306a36Sopenharmony_ci cksum = 0; 67462306a36Sopenharmony_ci len; 67562306a36Sopenharmony_ci len--, pw++) 67662306a36Sopenharmony_ci cksum = cksum + le16_to_cpu(*pw); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci return cksum; 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci/* 68262306a36Sopenharmony_ci * Verify the flash image structure. The following verifications will 68362306a36Sopenharmony_ci * be performed: 68462306a36Sopenharmony_ci * 1) verify the fi_version is correct 68562306a36Sopenharmony_ci * 2) verify the checksum of the entire image. 68662306a36Sopenharmony_ci * 3) validate the adap_typ, action and length fields. 68762306a36Sopenharmony_ci * 4) validate each component header. check the img_type and 68862306a36Sopenharmony_ci * length fields 68962306a36Sopenharmony_ci * 5) validate each component image. validate signatures and 69062306a36Sopenharmony_ci * local checksums 69162306a36Sopenharmony_ci */ 69262306a36Sopenharmony_cistatic bool verify_fi(struct esas2r_adapter *a, 69362306a36Sopenharmony_ci struct esas2r_flash_context *fc) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci struct esas2r_flash_img *fi = fc->fi; 69662306a36Sopenharmony_ci u8 type; 69762306a36Sopenharmony_ci bool imgerr; 69862306a36Sopenharmony_ci u16 i; 69962306a36Sopenharmony_ci u32 len; 70062306a36Sopenharmony_ci struct esas2r_component_header *ch; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* Verify the length - length must even since we do a word checksum */ 70362306a36Sopenharmony_ci len = fi->length; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if ((len & 1) 70662306a36Sopenharmony_ci || len < fc->fi_hdr_len) { 70762306a36Sopenharmony_ci fi->status = FI_STAT_LENGTH; 70862306a36Sopenharmony_ci return false; 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* Get adapter type and verify type in flash image */ 71262306a36Sopenharmony_ci type = get_fi_adap_type(a); 71362306a36Sopenharmony_ci if ((type == FI_AT_UNKNWN) || (fi->adap_typ != type)) { 71462306a36Sopenharmony_ci fi->status = FI_STAT_ADAPTYP; 71562306a36Sopenharmony_ci return false; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci /* 71962306a36Sopenharmony_ci * Loop through each component and verify the img_type and length 72062306a36Sopenharmony_ci * fields. Keep a running count of the sizes sooze we can verify total 72162306a36Sopenharmony_ci * size to additive size. 72262306a36Sopenharmony_ci */ 72362306a36Sopenharmony_ci imgerr = false; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci for (i = 0, len = 0, ch = fi->cmp_hdr; 72662306a36Sopenharmony_ci i < fi->num_comps; 72762306a36Sopenharmony_ci i++, ch++) { 72862306a36Sopenharmony_ci bool cmperr = false; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci /* 73162306a36Sopenharmony_ci * Verify that the component header has the same index as the 73262306a36Sopenharmony_ci * image type. The headers must be ordered correctly 73362306a36Sopenharmony_ci */ 73462306a36Sopenharmony_ci if (i != ch->img_type) { 73562306a36Sopenharmony_ci imgerr = true; 73662306a36Sopenharmony_ci ch->status = CH_STAT_INVALID; 73762306a36Sopenharmony_ci continue; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci switch (ch->img_type) { 74162306a36Sopenharmony_ci case CH_IT_BIOS: 74262306a36Sopenharmony_ci type = CODE_TYPE_PC; 74362306a36Sopenharmony_ci break; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci case CH_IT_MAC: 74662306a36Sopenharmony_ci type = CODE_TYPE_OPEN; 74762306a36Sopenharmony_ci break; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci case CH_IT_EFI: 75062306a36Sopenharmony_ci type = CODE_TYPE_EFI; 75162306a36Sopenharmony_ci break; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci switch (ch->img_type) { 75562306a36Sopenharmony_ci case CH_IT_FW: 75662306a36Sopenharmony_ci case CH_IT_NVR: 75762306a36Sopenharmony_ci break; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci case CH_IT_BIOS: 76062306a36Sopenharmony_ci case CH_IT_MAC: 76162306a36Sopenharmony_ci case CH_IT_EFI: 76262306a36Sopenharmony_ci if (ch->length & 0x1ff) 76362306a36Sopenharmony_ci cmperr = true; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci /* Test if component image is present */ 76662306a36Sopenharmony_ci if (ch->length == 0) 76762306a36Sopenharmony_ci break; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* Image is present - verify the image */ 77062306a36Sopenharmony_ci if (chk_boot((u8 *)fi + ch->image_offset, ch->length) 77162306a36Sopenharmony_ci != type) 77262306a36Sopenharmony_ci cmperr = true; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci break; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci case CH_IT_CFG: 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci /* Test if component image is present */ 77962306a36Sopenharmony_ci if (ch->length == 0) { 78062306a36Sopenharmony_ci cmperr = true; 78162306a36Sopenharmony_ci break; 78262306a36Sopenharmony_ci } 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci /* Image is present - verify the image */ 78562306a36Sopenharmony_ci if (!chk_cfg((u8 *)fi + ch->image_offset + ch->length, 78662306a36Sopenharmony_ci ch->length, NULL)) 78762306a36Sopenharmony_ci cmperr = true; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci break; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci default: 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci fi->status = FI_STAT_UNKNOWN; 79462306a36Sopenharmony_ci return false; 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci if (cmperr) { 79862306a36Sopenharmony_ci imgerr = true; 79962306a36Sopenharmony_ci ch->status = CH_STAT_INVALID; 80062306a36Sopenharmony_ci } else { 80162306a36Sopenharmony_ci ch->status = CH_STAT_PENDING; 80262306a36Sopenharmony_ci len += ch->length; 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (imgerr) { 80762306a36Sopenharmony_ci fi->status = FI_STAT_MISSING; 80862306a36Sopenharmony_ci return false; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci /* Compare fi->length to the sum of ch->length fields */ 81262306a36Sopenharmony_ci if (len != fi->length - fc->fi_hdr_len) { 81362306a36Sopenharmony_ci fi->status = FI_STAT_LENGTH; 81462306a36Sopenharmony_ci return false; 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci /* Compute the checksum - it should come out zero */ 81862306a36Sopenharmony_ci if (fi->checksum != calc_fi_checksum(fc)) { 81962306a36Sopenharmony_ci fi->status = FI_STAT_CHKSUM; 82062306a36Sopenharmony_ci return false; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci return true; 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci/* Fill in the FS IOCTL response data from a completed request. */ 82762306a36Sopenharmony_cistatic void esas2r_complete_fs_ioctl(struct esas2r_adapter *a, 82862306a36Sopenharmony_ci struct esas2r_request *rq) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci struct esas2r_ioctl_fs *fs = 83162306a36Sopenharmony_ci (struct esas2r_ioctl_fs *)rq->interrupt_cx; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if (rq->vrq->flash.sub_func == VDA_FLASH_COMMIT) 83462306a36Sopenharmony_ci esas2r_enable_heartbeat(a); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci fs->driver_error = rq->req_stat; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci if (fs->driver_error == RS_SUCCESS) 83962306a36Sopenharmony_ci fs->status = ATTO_STS_SUCCESS; 84062306a36Sopenharmony_ci else 84162306a36Sopenharmony_ci fs->status = ATTO_STS_FAILED; 84262306a36Sopenharmony_ci} 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci/* Prepare an FS IOCTL request to be sent to the firmware. */ 84562306a36Sopenharmony_cibool esas2r_process_fs_ioctl(struct esas2r_adapter *a, 84662306a36Sopenharmony_ci struct esas2r_ioctl_fs *fs, 84762306a36Sopenharmony_ci struct esas2r_request *rq, 84862306a36Sopenharmony_ci struct esas2r_sg_context *sgc) 84962306a36Sopenharmony_ci{ 85062306a36Sopenharmony_ci u8 cmdcnt = (u8)ARRAY_SIZE(cmd_to_fls_func); 85162306a36Sopenharmony_ci struct esas2r_ioctlfs_command *fsc = &fs->command; 85262306a36Sopenharmony_ci u8 func = 0; 85362306a36Sopenharmony_ci u32 datalen; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci fs->status = ATTO_STS_FAILED; 85662306a36Sopenharmony_ci fs->driver_error = RS_PENDING; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci if (fs->version > ESAS2R_FS_VER) { 85962306a36Sopenharmony_ci fs->status = ATTO_STS_INV_VERSION; 86062306a36Sopenharmony_ci return false; 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci if (fsc->command >= cmdcnt) { 86462306a36Sopenharmony_ci fs->status = ATTO_STS_INV_FUNC; 86562306a36Sopenharmony_ci return false; 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci func = cmd_to_fls_func[fsc->command]; 86962306a36Sopenharmony_ci if (func == 0xFF) { 87062306a36Sopenharmony_ci fs->status = ATTO_STS_INV_FUNC; 87162306a36Sopenharmony_ci return false; 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci if (fsc->command != ESAS2R_FS_CMD_CANCEL) { 87562306a36Sopenharmony_ci if ((a->pcid->device != ATTO_DID_MV_88RC9580 87662306a36Sopenharmony_ci || fs->adap_type != ESAS2R_FS_AT_ESASRAID2) 87762306a36Sopenharmony_ci && (a->pcid->device != ATTO_DID_MV_88RC9580TS 87862306a36Sopenharmony_ci || fs->adap_type != ESAS2R_FS_AT_TSSASRAID2) 87962306a36Sopenharmony_ci && (a->pcid->device != ATTO_DID_MV_88RC9580TSE 88062306a36Sopenharmony_ci || fs->adap_type != ESAS2R_FS_AT_TSSASRAID2E) 88162306a36Sopenharmony_ci && (a->pcid->device != ATTO_DID_MV_88RC9580TL 88262306a36Sopenharmony_ci || fs->adap_type != ESAS2R_FS_AT_TLSASHBA)) { 88362306a36Sopenharmony_ci fs->status = ATTO_STS_INV_ADAPTER; 88462306a36Sopenharmony_ci return false; 88562306a36Sopenharmony_ci } 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci if (fs->driver_ver > ESAS2R_FS_DRVR_VER) { 88862306a36Sopenharmony_ci fs->status = ATTO_STS_INV_DRVR_VER; 88962306a36Sopenharmony_ci return false; 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci if (test_bit(AF_DEGRADED_MODE, &a->flags)) { 89462306a36Sopenharmony_ci fs->status = ATTO_STS_DEGRADED; 89562306a36Sopenharmony_ci return false; 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci rq->interrupt_cb = esas2r_complete_fs_ioctl; 89962306a36Sopenharmony_ci rq->interrupt_cx = fs; 90062306a36Sopenharmony_ci datalen = le32_to_cpu(fsc->length); 90162306a36Sopenharmony_ci esas2r_build_flash_req(a, 90262306a36Sopenharmony_ci rq, 90362306a36Sopenharmony_ci func, 90462306a36Sopenharmony_ci fsc->checksum, 90562306a36Sopenharmony_ci le32_to_cpu(fsc->flash_addr), 90662306a36Sopenharmony_ci datalen); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci if (func == VDA_FLASH_WRITE 90962306a36Sopenharmony_ci || func == VDA_FLASH_READ) { 91062306a36Sopenharmony_ci if (datalen == 0) { 91162306a36Sopenharmony_ci fs->status = ATTO_STS_INV_FUNC; 91262306a36Sopenharmony_ci return false; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci esas2r_sgc_init(sgc, a, rq, rq->vrq->flash.data.sge); 91662306a36Sopenharmony_ci sgc->length = datalen; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (!esas2r_build_sg_list(a, rq, sgc)) { 91962306a36Sopenharmony_ci fs->status = ATTO_STS_OUT_OF_RSRC; 92062306a36Sopenharmony_ci return false; 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci } 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci if (func == VDA_FLASH_COMMIT) 92562306a36Sopenharmony_ci esas2r_disable_heartbeat(a); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci esas2r_start_request(a, rq); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci return true; 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_cistatic bool esas2r_flash_access(struct esas2r_adapter *a, u32 function) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci u32 starttime; 93562306a36Sopenharmony_ci u32 timeout; 93662306a36Sopenharmony_ci u32 intstat; 93762306a36Sopenharmony_ci u32 doorbell; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci /* Disable chip interrupts awhile */ 94062306a36Sopenharmony_ci if (function == DRBL_FLASH_REQ) 94162306a36Sopenharmony_ci esas2r_disable_chip_interrupts(a); 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci /* Issue the request to the firmware */ 94462306a36Sopenharmony_ci esas2r_write_register_dword(a, MU_DOORBELL_IN, function); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci /* Now wait for the firmware to process it */ 94762306a36Sopenharmony_ci starttime = jiffies_to_msecs(jiffies); 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci if (test_bit(AF_CHPRST_PENDING, &a->flags) || 95062306a36Sopenharmony_ci test_bit(AF_DISC_PENDING, &a->flags)) 95162306a36Sopenharmony_ci timeout = 40000; 95262306a36Sopenharmony_ci else 95362306a36Sopenharmony_ci timeout = 5000; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci while (true) { 95662306a36Sopenharmony_ci intstat = esas2r_read_register_dword(a, MU_INT_STATUS_OUT); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci if (intstat & MU_INTSTAT_DRBL) { 95962306a36Sopenharmony_ci /* Got a doorbell interrupt. Check for the function */ 96062306a36Sopenharmony_ci doorbell = 96162306a36Sopenharmony_ci esas2r_read_register_dword(a, MU_DOORBELL_OUT); 96262306a36Sopenharmony_ci esas2r_write_register_dword(a, MU_DOORBELL_OUT, 96362306a36Sopenharmony_ci doorbell); 96462306a36Sopenharmony_ci if (doorbell & function) 96562306a36Sopenharmony_ci break; 96662306a36Sopenharmony_ci } 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci schedule_timeout_interruptible(msecs_to_jiffies(100)); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci if ((jiffies_to_msecs(jiffies) - starttime) > timeout) { 97162306a36Sopenharmony_ci /* 97262306a36Sopenharmony_ci * Iimeout. If we were requesting flash access, 97362306a36Sopenharmony_ci * indicate we are done so the firmware knows we gave 97462306a36Sopenharmony_ci * up. If this was a REQ, we also need to re-enable 97562306a36Sopenharmony_ci * chip interrupts. 97662306a36Sopenharmony_ci */ 97762306a36Sopenharmony_ci if (function == DRBL_FLASH_REQ) { 97862306a36Sopenharmony_ci esas2r_hdebug("flash access timeout"); 97962306a36Sopenharmony_ci esas2r_write_register_dword(a, MU_DOORBELL_IN, 98062306a36Sopenharmony_ci DRBL_FLASH_DONE); 98162306a36Sopenharmony_ci esas2r_enable_chip_interrupts(a); 98262306a36Sopenharmony_ci } else { 98362306a36Sopenharmony_ci esas2r_hdebug("flash release timeout"); 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci return false; 98762306a36Sopenharmony_ci } 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci /* if we're done, re-enable chip interrupts */ 99162306a36Sopenharmony_ci if (function == DRBL_FLASH_DONE) 99262306a36Sopenharmony_ci esas2r_enable_chip_interrupts(a); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci return true; 99562306a36Sopenharmony_ci} 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci#define WINDOW_SIZE ((signed int)MW_DATA_WINDOW_SIZE) 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_cibool esas2r_read_flash_block(struct esas2r_adapter *a, 100062306a36Sopenharmony_ci void *to, 100162306a36Sopenharmony_ci u32 from, 100262306a36Sopenharmony_ci u32 size) 100362306a36Sopenharmony_ci{ 100462306a36Sopenharmony_ci u8 *end = (u8 *)to; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci /* Try to acquire access to the flash */ 100762306a36Sopenharmony_ci if (!esas2r_flash_access(a, DRBL_FLASH_REQ)) 100862306a36Sopenharmony_ci return false; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci while (size) { 101162306a36Sopenharmony_ci u32 len; 101262306a36Sopenharmony_ci u32 offset; 101362306a36Sopenharmony_ci u32 iatvr; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci if (test_bit(AF2_SERIAL_FLASH, &a->flags2)) 101662306a36Sopenharmony_ci iatvr = MW_DATA_ADDR_SER_FLASH + (from & -WINDOW_SIZE); 101762306a36Sopenharmony_ci else 101862306a36Sopenharmony_ci iatvr = MW_DATA_ADDR_PAR_FLASH + (from & -WINDOW_SIZE); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci esas2r_map_data_window(a, iatvr); 102162306a36Sopenharmony_ci offset = from & (WINDOW_SIZE - 1); 102262306a36Sopenharmony_ci len = size; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci if (len > WINDOW_SIZE - offset) 102562306a36Sopenharmony_ci len = WINDOW_SIZE - offset; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci from += len; 102862306a36Sopenharmony_ci size -= len; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci while (len--) { 103162306a36Sopenharmony_ci *end++ = esas2r_read_data_byte(a, offset); 103262306a36Sopenharmony_ci offset++; 103362306a36Sopenharmony_ci } 103462306a36Sopenharmony_ci } 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci /* Release flash access */ 103762306a36Sopenharmony_ci esas2r_flash_access(a, DRBL_FLASH_DONE); 103862306a36Sopenharmony_ci return true; 103962306a36Sopenharmony_ci} 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_cibool esas2r_read_flash_rev(struct esas2r_adapter *a) 104262306a36Sopenharmony_ci{ 104362306a36Sopenharmony_ci u8 bytes[256]; 104462306a36Sopenharmony_ci u16 *pw; 104562306a36Sopenharmony_ci u16 *pwstart; 104662306a36Sopenharmony_ci u16 type; 104762306a36Sopenharmony_ci u16 size; 104862306a36Sopenharmony_ci u32 sz; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci sz = sizeof(bytes); 105162306a36Sopenharmony_ci pw = (u16 *)(bytes + sz); 105262306a36Sopenharmony_ci pwstart = (u16 *)bytes + 2; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci if (!esas2r_read_flash_block(a, bytes, FLS_OFFSET_CPYR - sz, sz)) 105562306a36Sopenharmony_ci goto invalid_rev; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci while (pw >= pwstart) { 105862306a36Sopenharmony_ci pw--; 105962306a36Sopenharmony_ci type = le16_to_cpu(*pw); 106062306a36Sopenharmony_ci pw--; 106162306a36Sopenharmony_ci size = le16_to_cpu(*pw); 106262306a36Sopenharmony_ci pw -= size / 2; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci if (type == FBT_CPYR 106562306a36Sopenharmony_ci || type == FBT_SETUP 106662306a36Sopenharmony_ci || pw < pwstart) 106762306a36Sopenharmony_ci continue; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci if (type == FBT_FLASH_VER) 107062306a36Sopenharmony_ci a->flash_ver = le32_to_cpu(*(u32 *)pw); 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci break; 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ciinvalid_rev: 107662306a36Sopenharmony_ci return esas2r_print_flash_rev(a); 107762306a36Sopenharmony_ci} 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_cibool esas2r_print_flash_rev(struct esas2r_adapter *a) 108062306a36Sopenharmony_ci{ 108162306a36Sopenharmony_ci u16 year = LOWORD(a->flash_ver); 108262306a36Sopenharmony_ci u8 day = LOBYTE(HIWORD(a->flash_ver)); 108362306a36Sopenharmony_ci u8 month = HIBYTE(HIWORD(a->flash_ver)); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci if (day == 0 108662306a36Sopenharmony_ci || month == 0 108762306a36Sopenharmony_ci || day > 31 108862306a36Sopenharmony_ci || month > 12 108962306a36Sopenharmony_ci || year < 2006 109062306a36Sopenharmony_ci || year > 9999) { 109162306a36Sopenharmony_ci strcpy(a->flash_rev, "not found"); 109262306a36Sopenharmony_ci a->flash_ver = 0; 109362306a36Sopenharmony_ci return false; 109462306a36Sopenharmony_ci } 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci sprintf(a->flash_rev, "%02d/%02d/%04d", month, day, year); 109762306a36Sopenharmony_ci esas2r_hdebug("flash version: %s", a->flash_rev); 109862306a36Sopenharmony_ci return true; 109962306a36Sopenharmony_ci} 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci/* 110262306a36Sopenharmony_ci * Find the type of boot image type that is currently in the flash. 110362306a36Sopenharmony_ci * The chip only has a 64 KB PCI-e expansion ROM 110462306a36Sopenharmony_ci * size so only one image can be flashed at a time. 110562306a36Sopenharmony_ci */ 110662306a36Sopenharmony_cibool esas2r_read_image_type(struct esas2r_adapter *a) 110762306a36Sopenharmony_ci{ 110862306a36Sopenharmony_ci u8 bytes[256]; 110962306a36Sopenharmony_ci struct esas2r_boot_image *bi; 111062306a36Sopenharmony_ci struct esas2r_boot_header *bh; 111162306a36Sopenharmony_ci u32 sz; 111262306a36Sopenharmony_ci u32 len; 111362306a36Sopenharmony_ci u32 offset; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci /* Start at the base of the boot images and look for a valid image */ 111662306a36Sopenharmony_ci sz = sizeof(bytes); 111762306a36Sopenharmony_ci len = FLS_LENGTH_BOOT; 111862306a36Sopenharmony_ci offset = 0; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci while (true) { 112162306a36Sopenharmony_ci if (!esas2r_read_flash_block(a, bytes, FLS_OFFSET_BOOT + 112262306a36Sopenharmony_ci offset, 112362306a36Sopenharmony_ci sz)) 112462306a36Sopenharmony_ci goto invalid_rev; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci bi = (struct esas2r_boot_image *)bytes; 112762306a36Sopenharmony_ci bh = (struct esas2r_boot_header *)((u8 *)bi + 112862306a36Sopenharmony_ci le16_to_cpu( 112962306a36Sopenharmony_ci bi->header_offset)); 113062306a36Sopenharmony_ci if (bi->signature != cpu_to_le16(0xAA55)) 113162306a36Sopenharmony_ci goto invalid_rev; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci if (bh->code_type == CODE_TYPE_PC) { 113462306a36Sopenharmony_ci strcpy(a->image_type, "BIOS"); 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci return true; 113762306a36Sopenharmony_ci } else if (bh->code_type == CODE_TYPE_EFI) { 113862306a36Sopenharmony_ci struct esas2r_efi_image *ei; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci /* 114162306a36Sopenharmony_ci * So we have an EFI image. There are several types 114262306a36Sopenharmony_ci * so see which architecture we have. 114362306a36Sopenharmony_ci */ 114462306a36Sopenharmony_ci ei = (struct esas2r_efi_image *)bytes; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci switch (le16_to_cpu(ei->machine_type)) { 114762306a36Sopenharmony_ci case EFI_MACHINE_IA32: 114862306a36Sopenharmony_ci strcpy(a->image_type, "EFI 32-bit"); 114962306a36Sopenharmony_ci return true; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci case EFI_MACHINE_IA64: 115262306a36Sopenharmony_ci strcpy(a->image_type, "EFI itanium"); 115362306a36Sopenharmony_ci return true; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci case EFI_MACHINE_X64: 115662306a36Sopenharmony_ci strcpy(a->image_type, "EFI 64-bit"); 115762306a36Sopenharmony_ci return true; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci case EFI_MACHINE_EBC: 116062306a36Sopenharmony_ci strcpy(a->image_type, "EFI EBC"); 116162306a36Sopenharmony_ci return true; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci default: 116462306a36Sopenharmony_ci goto invalid_rev; 116562306a36Sopenharmony_ci } 116662306a36Sopenharmony_ci } else { 116762306a36Sopenharmony_ci u32 thislen; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci /* jump to the next image */ 117062306a36Sopenharmony_ci thislen = (u32)le16_to_cpu(bh->image_length) * 512; 117162306a36Sopenharmony_ci if (thislen == 0 117262306a36Sopenharmony_ci || thislen + offset > len 117362306a36Sopenharmony_ci || bh->indicator == INDICATOR_LAST) 117462306a36Sopenharmony_ci break; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci offset += thislen; 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ciinvalid_rev: 118162306a36Sopenharmony_ci strcpy(a->image_type, "no boot images"); 118262306a36Sopenharmony_ci return false; 118362306a36Sopenharmony_ci} 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci/* 118662306a36Sopenharmony_ci * Read and validate current NVRAM parameters by accessing 118762306a36Sopenharmony_ci * physical NVRAM directly. if currently stored parameters are 118862306a36Sopenharmony_ci * invalid, use the defaults. 118962306a36Sopenharmony_ci */ 119062306a36Sopenharmony_cibool esas2r_nvram_read_direct(struct esas2r_adapter *a) 119162306a36Sopenharmony_ci{ 119262306a36Sopenharmony_ci bool result; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci if (down_interruptible(&a->nvram_semaphore)) 119562306a36Sopenharmony_ci return false; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci if (!esas2r_read_flash_block(a, a->nvram, FLS_OFFSET_NVR, 119862306a36Sopenharmony_ci sizeof(struct esas2r_sas_nvram))) { 119962306a36Sopenharmony_ci esas2r_hdebug("NVRAM read failed, using defaults"); 120062306a36Sopenharmony_ci up(&a->nvram_semaphore); 120162306a36Sopenharmony_ci return false; 120262306a36Sopenharmony_ci } 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci result = esas2r_nvram_validate(a); 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci up(&a->nvram_semaphore); 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci return result; 120962306a36Sopenharmony_ci} 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci/* Interrupt callback to process NVRAM completions. */ 121262306a36Sopenharmony_cistatic void esas2r_nvram_callback(struct esas2r_adapter *a, 121362306a36Sopenharmony_ci struct esas2r_request *rq) 121462306a36Sopenharmony_ci{ 121562306a36Sopenharmony_ci struct atto_vda_flash_req *vrq = &rq->vrq->flash; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci if (rq->req_stat == RS_SUCCESS) { 121862306a36Sopenharmony_ci /* last request was successful. see what to do now. */ 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci switch (vrq->sub_func) { 122162306a36Sopenharmony_ci case VDA_FLASH_BEGINW: 122262306a36Sopenharmony_ci vrq->sub_func = VDA_FLASH_WRITE; 122362306a36Sopenharmony_ci rq->req_stat = RS_PENDING; 122462306a36Sopenharmony_ci break; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci case VDA_FLASH_WRITE: 122762306a36Sopenharmony_ci vrq->sub_func = VDA_FLASH_COMMIT; 122862306a36Sopenharmony_ci rq->req_stat = RS_PENDING; 122962306a36Sopenharmony_ci break; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci case VDA_FLASH_READ: 123262306a36Sopenharmony_ci esas2r_nvram_validate(a); 123362306a36Sopenharmony_ci break; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci case VDA_FLASH_COMMIT: 123662306a36Sopenharmony_ci default: 123762306a36Sopenharmony_ci break; 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci } 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci if (rq->req_stat != RS_PENDING) { 124262306a36Sopenharmony_ci /* update the NVRAM state */ 124362306a36Sopenharmony_ci if (rq->req_stat == RS_SUCCESS) 124462306a36Sopenharmony_ci set_bit(AF_NVR_VALID, &a->flags); 124562306a36Sopenharmony_ci else 124662306a36Sopenharmony_ci clear_bit(AF_NVR_VALID, &a->flags); 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci esas2r_enable_heartbeat(a); 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci up(&a->nvram_semaphore); 125162306a36Sopenharmony_ci } 125262306a36Sopenharmony_ci} 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci/* 125562306a36Sopenharmony_ci * Write the contents of nvram to the adapter's physical NVRAM. 125662306a36Sopenharmony_ci * The cached copy of the NVRAM is also updated. 125762306a36Sopenharmony_ci */ 125862306a36Sopenharmony_cibool esas2r_nvram_write(struct esas2r_adapter *a, struct esas2r_request *rq, 125962306a36Sopenharmony_ci struct esas2r_sas_nvram *nvram) 126062306a36Sopenharmony_ci{ 126162306a36Sopenharmony_ci struct esas2r_sas_nvram *n = nvram; 126262306a36Sopenharmony_ci u8 sas_address_bytes[8]; 126362306a36Sopenharmony_ci u32 *sas_address_dwords = (u32 *)&sas_address_bytes[0]; 126462306a36Sopenharmony_ci struct atto_vda_flash_req *vrq = &rq->vrq->flash; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci if (test_bit(AF_DEGRADED_MODE, &a->flags)) 126762306a36Sopenharmony_ci return false; 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci if (down_interruptible(&a->nvram_semaphore)) 127062306a36Sopenharmony_ci return false; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci if (n == NULL) 127362306a36Sopenharmony_ci n = a->nvram; 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci /* check the validity of the settings */ 127662306a36Sopenharmony_ci if (n->version > SASNVR_VERSION) { 127762306a36Sopenharmony_ci up(&a->nvram_semaphore); 127862306a36Sopenharmony_ci return false; 127962306a36Sopenharmony_ci } 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci memcpy(&sas_address_bytes[0], n->sas_addr, 8); 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci if (sas_address_bytes[0] != 0x50 128462306a36Sopenharmony_ci || sas_address_bytes[1] != 0x01 128562306a36Sopenharmony_ci || sas_address_bytes[2] != 0x08 128662306a36Sopenharmony_ci || (sas_address_bytes[3] & 0xF0) != 0x60 128762306a36Sopenharmony_ci || ((sas_address_bytes[3] & 0x0F) | sas_address_dwords[1]) == 0) { 128862306a36Sopenharmony_ci up(&a->nvram_semaphore); 128962306a36Sopenharmony_ci return false; 129062306a36Sopenharmony_ci } 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci if (n->spin_up_delay > SASNVR_SPINUP_MAX) 129362306a36Sopenharmony_ci n->spin_up_delay = SASNVR_SPINUP_MAX; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci n->version = SASNVR_VERSION; 129662306a36Sopenharmony_ci n->checksum = n->checksum - esas2r_nvramcalc_cksum(n); 129762306a36Sopenharmony_ci memcpy(a->nvram, n, sizeof(struct esas2r_sas_nvram)); 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci /* write the NVRAM */ 130062306a36Sopenharmony_ci n = a->nvram; 130162306a36Sopenharmony_ci esas2r_disable_heartbeat(a); 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci esas2r_build_flash_req(a, 130462306a36Sopenharmony_ci rq, 130562306a36Sopenharmony_ci VDA_FLASH_BEGINW, 130662306a36Sopenharmony_ci esas2r_nvramcalc_xor_cksum(n), 130762306a36Sopenharmony_ci FLS_OFFSET_NVR, 130862306a36Sopenharmony_ci sizeof(struct esas2r_sas_nvram)); 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) { 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci vrq->data.sge[0].length = 131362306a36Sopenharmony_ci cpu_to_le32(SGE_LAST | 131462306a36Sopenharmony_ci sizeof(struct esas2r_sas_nvram)); 131562306a36Sopenharmony_ci vrq->data.sge[0].address = cpu_to_le64( 131662306a36Sopenharmony_ci a->uncached_phys + (u64)((u8 *)n - a->uncached)); 131762306a36Sopenharmony_ci } else { 131862306a36Sopenharmony_ci vrq->data.prde[0].ctl_len = 131962306a36Sopenharmony_ci cpu_to_le32(sizeof(struct esas2r_sas_nvram)); 132062306a36Sopenharmony_ci vrq->data.prde[0].address = cpu_to_le64( 132162306a36Sopenharmony_ci a->uncached_phys 132262306a36Sopenharmony_ci + (u64)((u8 *)n - a->uncached)); 132362306a36Sopenharmony_ci } 132462306a36Sopenharmony_ci rq->interrupt_cb = esas2r_nvram_callback; 132562306a36Sopenharmony_ci esas2r_start_request(a, rq); 132662306a36Sopenharmony_ci return true; 132762306a36Sopenharmony_ci} 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci/* Validate the cached NVRAM. if the NVRAM is invalid, load the defaults. */ 133062306a36Sopenharmony_cibool esas2r_nvram_validate(struct esas2r_adapter *a) 133162306a36Sopenharmony_ci{ 133262306a36Sopenharmony_ci struct esas2r_sas_nvram *n = a->nvram; 133362306a36Sopenharmony_ci bool rslt = false; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci if (n->signature[0] != 'E' 133662306a36Sopenharmony_ci || n->signature[1] != 'S' 133762306a36Sopenharmony_ci || n->signature[2] != 'A' 133862306a36Sopenharmony_ci || n->signature[3] != 'S') { 133962306a36Sopenharmony_ci esas2r_hdebug("invalid NVRAM signature"); 134062306a36Sopenharmony_ci } else if (esas2r_nvramcalc_cksum(n)) { 134162306a36Sopenharmony_ci esas2r_hdebug("invalid NVRAM checksum"); 134262306a36Sopenharmony_ci } else if (n->version > SASNVR_VERSION) { 134362306a36Sopenharmony_ci esas2r_hdebug("invalid NVRAM version"); 134462306a36Sopenharmony_ci } else { 134562306a36Sopenharmony_ci set_bit(AF_NVR_VALID, &a->flags); 134662306a36Sopenharmony_ci rslt = true; 134762306a36Sopenharmony_ci } 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci if (rslt == false) { 135062306a36Sopenharmony_ci esas2r_hdebug("using defaults"); 135162306a36Sopenharmony_ci esas2r_nvram_set_defaults(a); 135262306a36Sopenharmony_ci } 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci return rslt; 135562306a36Sopenharmony_ci} 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci/* 135862306a36Sopenharmony_ci * Set the cached NVRAM to defaults. note that this function sets the default 135962306a36Sopenharmony_ci * NVRAM when it has been determined that the physical NVRAM is invalid. 136062306a36Sopenharmony_ci * In this case, the SAS address is fabricated. 136162306a36Sopenharmony_ci */ 136262306a36Sopenharmony_civoid esas2r_nvram_set_defaults(struct esas2r_adapter *a) 136362306a36Sopenharmony_ci{ 136462306a36Sopenharmony_ci struct esas2r_sas_nvram *n = a->nvram; 136562306a36Sopenharmony_ci u32 time = jiffies_to_msecs(jiffies); 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci clear_bit(AF_NVR_VALID, &a->flags); 136862306a36Sopenharmony_ci *n = default_sas_nvram; 136962306a36Sopenharmony_ci n->sas_addr[3] |= 0x0F; 137062306a36Sopenharmony_ci n->sas_addr[4] = HIBYTE(LOWORD(time)); 137162306a36Sopenharmony_ci n->sas_addr[5] = LOBYTE(LOWORD(time)); 137262306a36Sopenharmony_ci n->sas_addr[6] = a->pcid->bus->number; 137362306a36Sopenharmony_ci n->sas_addr[7] = a->pcid->devfn; 137462306a36Sopenharmony_ci} 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_civoid esas2r_nvram_get_defaults(struct esas2r_adapter *a, 137762306a36Sopenharmony_ci struct esas2r_sas_nvram *nvram) 137862306a36Sopenharmony_ci{ 137962306a36Sopenharmony_ci u8 sas_addr[8]; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci /* 138262306a36Sopenharmony_ci * in case we are copying the defaults into the adapter, copy the SAS 138362306a36Sopenharmony_ci * address out first. 138462306a36Sopenharmony_ci */ 138562306a36Sopenharmony_ci memcpy(&sas_addr[0], a->nvram->sas_addr, 8); 138662306a36Sopenharmony_ci *nvram = default_sas_nvram; 138762306a36Sopenharmony_ci memcpy(&nvram->sas_addr[0], &sas_addr[0], 8); 138862306a36Sopenharmony_ci} 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_cibool esas2r_fm_api(struct esas2r_adapter *a, struct esas2r_flash_img *fi, 139162306a36Sopenharmony_ci struct esas2r_request *rq, struct esas2r_sg_context *sgc) 139262306a36Sopenharmony_ci{ 139362306a36Sopenharmony_ci struct esas2r_flash_context *fc = &a->flash_context; 139462306a36Sopenharmony_ci u8 j; 139562306a36Sopenharmony_ci struct esas2r_component_header *ch; 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci if (test_and_set_bit(AF_FLASH_LOCK, &a->flags)) { 139862306a36Sopenharmony_ci /* flag was already set */ 139962306a36Sopenharmony_ci fi->status = FI_STAT_BUSY; 140062306a36Sopenharmony_ci return false; 140162306a36Sopenharmony_ci } 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci memcpy(&fc->sgc, sgc, sizeof(struct esas2r_sg_context)); 140462306a36Sopenharmony_ci sgc = &fc->sgc; 140562306a36Sopenharmony_ci fc->fi = fi; 140662306a36Sopenharmony_ci fc->sgc_offset = sgc->cur_offset; 140762306a36Sopenharmony_ci rq->req_stat = RS_SUCCESS; 140862306a36Sopenharmony_ci rq->interrupt_cx = fc; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci switch (fi->fi_version) { 141162306a36Sopenharmony_ci case FI_VERSION_1: 141262306a36Sopenharmony_ci fc->scratch = ((struct esas2r_flash_img *)fi)->scratch_buf; 141362306a36Sopenharmony_ci fc->num_comps = FI_NUM_COMPS_V1; 141462306a36Sopenharmony_ci fc->fi_hdr_len = sizeof(struct esas2r_flash_img); 141562306a36Sopenharmony_ci break; 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci default: 141862306a36Sopenharmony_ci return complete_fmapi_req(a, rq, FI_STAT_IMG_VER); 141962306a36Sopenharmony_ci } 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci if (test_bit(AF_DEGRADED_MODE, &a->flags)) 142262306a36Sopenharmony_ci return complete_fmapi_req(a, rq, FI_STAT_DEGRADED); 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci switch (fi->action) { 142562306a36Sopenharmony_ci case FI_ACT_DOWN: /* Download the components */ 142662306a36Sopenharmony_ci /* Verify the format of the flash image */ 142762306a36Sopenharmony_ci if (!verify_fi(a, fc)) 142862306a36Sopenharmony_ci return complete_fmapi_req(a, rq, fi->status); 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci /* Adjust the BIOS fields that are dependent on the HBA */ 143162306a36Sopenharmony_ci ch = &fi->cmp_hdr[CH_IT_BIOS]; 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci if (ch->length) 143462306a36Sopenharmony_ci fix_bios(a, fi); 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci /* Adjust the EFI fields that are dependent on the HBA */ 143762306a36Sopenharmony_ci ch = &fi->cmp_hdr[CH_IT_EFI]; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci if (ch->length) 144062306a36Sopenharmony_ci fix_efi(a, fi); 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci /* 144362306a36Sopenharmony_ci * Since the image was just modified, compute the checksum on 144462306a36Sopenharmony_ci * the modified image. First update the CRC for the composite 144562306a36Sopenharmony_ci * expansion ROM image. 144662306a36Sopenharmony_ci */ 144762306a36Sopenharmony_ci fi->checksum = calc_fi_checksum(fc); 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci /* Disable the heartbeat */ 145062306a36Sopenharmony_ci esas2r_disable_heartbeat(a); 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci /* Now start up the download sequence */ 145362306a36Sopenharmony_ci fc->task = FMTSK_ERASE_BOOT; 145462306a36Sopenharmony_ci fc->func = VDA_FLASH_BEGINW; 145562306a36Sopenharmony_ci fc->comp_typ = CH_IT_CFG; 145662306a36Sopenharmony_ci fc->flsh_addr = FLS_OFFSET_BOOT; 145762306a36Sopenharmony_ci fc->sgc.length = FLS_LENGTH_BOOT; 145862306a36Sopenharmony_ci fc->sgc.cur_offset = NULL; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci /* Setup the callback address */ 146162306a36Sopenharmony_ci fc->interrupt_cb = fw_download_proc; 146262306a36Sopenharmony_ci break; 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci case FI_ACT_UPSZ: /* Get upload sizes */ 146562306a36Sopenharmony_ci fi->adap_typ = get_fi_adap_type(a); 146662306a36Sopenharmony_ci fi->flags = 0; 146762306a36Sopenharmony_ci fi->num_comps = fc->num_comps; 146862306a36Sopenharmony_ci fi->length = fc->fi_hdr_len; 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci /* Report the type of boot image in the rel_version string */ 147162306a36Sopenharmony_ci memcpy(fi->rel_version, a->image_type, 147262306a36Sopenharmony_ci sizeof(fi->rel_version)); 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci /* Build the component headers */ 147562306a36Sopenharmony_ci for (j = 0, ch = fi->cmp_hdr; 147662306a36Sopenharmony_ci j < fi->num_comps; 147762306a36Sopenharmony_ci j++, ch++) { 147862306a36Sopenharmony_ci ch->img_type = j; 147962306a36Sopenharmony_ci ch->status = CH_STAT_PENDING; 148062306a36Sopenharmony_ci ch->length = 0; 148162306a36Sopenharmony_ci ch->version = 0xffffffff; 148262306a36Sopenharmony_ci ch->image_offset = 0; 148362306a36Sopenharmony_ci ch->pad[0] = 0; 148462306a36Sopenharmony_ci ch->pad[1] = 0; 148562306a36Sopenharmony_ci } 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci if (a->flash_ver != 0) { 148862306a36Sopenharmony_ci fi->cmp_hdr[CH_IT_BIOS].version = 148962306a36Sopenharmony_ci fi->cmp_hdr[CH_IT_MAC].version = 149062306a36Sopenharmony_ci fi->cmp_hdr[CH_IT_EFI].version = 149162306a36Sopenharmony_ci fi->cmp_hdr[CH_IT_CFG].version 149262306a36Sopenharmony_ci = a->flash_ver; 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci fi->cmp_hdr[CH_IT_BIOS].status = 149562306a36Sopenharmony_ci fi->cmp_hdr[CH_IT_MAC].status = 149662306a36Sopenharmony_ci fi->cmp_hdr[CH_IT_EFI].status = 149762306a36Sopenharmony_ci fi->cmp_hdr[CH_IT_CFG].status = 149862306a36Sopenharmony_ci CH_STAT_SUCCESS; 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci return complete_fmapi_req(a, rq, FI_STAT_SUCCESS); 150162306a36Sopenharmony_ci } 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci fallthrough; 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci case FI_ACT_UP: /* Upload the components */ 150662306a36Sopenharmony_ci default: 150762306a36Sopenharmony_ci return complete_fmapi_req(a, rq, FI_STAT_INVALID); 150862306a36Sopenharmony_ci } 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci /* 151162306a36Sopenharmony_ci * If we make it here, fc has been setup to do the first task. Call 151262306a36Sopenharmony_ci * load_image to format the request, start it, and get out. The 151362306a36Sopenharmony_ci * interrupt code will call the callback when the first message is 151462306a36Sopenharmony_ci * complete. 151562306a36Sopenharmony_ci */ 151662306a36Sopenharmony_ci if (!load_image(a, rq)) 151762306a36Sopenharmony_ci return complete_fmapi_req(a, rq, FI_STAT_FAILED); 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci esas2r_start_request(a, rq); 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci return true; 152262306a36Sopenharmony_ci} 1523