text: 
  /*
   *  linux/drivers/scsi/esas2r/esas2r_flash.c
   *      For use with ATTO ExpressSAS R6xx SAS/SATA RAID controllers
   *
   *  Copyright (c) 2001-2013 ATTO Technology, Inc.
   *  (mailto:linuxdrivers@attotech.com)
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; either version 2
   * of the License, or (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * NO WARRANTY
   * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
   * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
   * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
   * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
   * solely responsible for determining the appropriateness of using and
   * distributing the Program and assumes all risks associated with its
   * exercise of rights under this Agreement, including but not limited to
   * the risks and costs of program errors, damage to or loss of data,
   * programs or equipment, and unavailability or interruption of operations.
   *
   * DISCLAIMER OF LIABILITY
   * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
   * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
   * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
   * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
   * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
   * USA.
   */
  
  #include "esas2r.h"
  
  /* local macro defs */
  #define esas2r_nvramcalc_cksum(n)     \
  	(esas2r_calc_byte_cksum((u8 *)(n), sizeof(struct esas2r_sas_nvram), \
  				SASNVR_CKSUM_SEED))
  #define esas2r_nvramcalc_xor_cksum(n)  \
  	(esas2r_calc_byte_xor_cksum((u8 *)(n), \
  				    sizeof(struct esas2r_sas_nvram), 0))
  
  #define ESAS2R_FS_DRVR_VER 2
  
  static struct esas2r_sas_nvram default_sas_nvram = {
  	{ 'E',	'S',  'A',  'S'			     }, /* signature          */
  	SASNVR_VERSION,                                 /* version            */
  	0,                                              /* checksum           */
  	31,                                             /* max_lun_for_target */
  	SASNVR_PCILAT_MAX,                              /* pci_latency        */
  	SASNVR1_BOOT_DRVR,                              /* options1           */
  	SASNVR2_HEARTBEAT   | SASNVR2_SINGLE_BUS        /* options2           */
  	| SASNVR2_SW_MUX_CTRL,
  	SASNVR_COAL_DIS,                                /* int_coalescing     */
  	SASNVR_CMDTHR_NONE,                             /* cmd_throttle       */
  	3,                                              /* dev_wait_time      */
  	1,                                              /* dev_wait_count     */
  	0,                                              /* spin_up_delay      */
  	0,                                              /* ssp_align_rate     */
  	{ 0x50, 0x01, 0x08, 0x60,                       /* sas_addr           */
  	  0x00, 0x00, 0x00, 0x00 },
  	{ SASNVR_SPEED_AUTO },                          /* phy_speed          */
  	{ SASNVR_MUX_DISABLED },                        /* SAS multiplexing   */
  	{ 0 },                                          /* phy_flags          */
  	SASNVR_SORT_SAS_ADDR,                           /* sort_type          */
  	3,                                              /* dpm_reqcmd_lmt     */
  	3,                                              /* dpm_stndby_time    */
  	0,                                              /* dpm_active_time    */
  	{ 0 },                                          /* phy_target_id      */
  	SASNVR_VSMH_DISABLED,                           /* virt_ses_mode      */
  	SASNVR_RWM_DEFAULT,                             /* read_write_mode    */
  	0,                                              /* link down timeout  */
  	{ 0 }                                           /* reserved           */
  };
  
  static u8 cmd_to_fls_func[] = {
  	0xFF,
  	VDA_FLASH_READ,
  	VDA_FLASH_BEGINW,
  	VDA_FLASH_WRITE,
  	VDA_FLASH_COMMIT,
  	VDA_FLASH_CANCEL
  };
  
  static u8 esas2r_calc_byte_xor_cksum(u8 *addr, u32 len, u8 seed)
  {
  	u32 cksum = seed;
  	u8 *p = (u8 *)&cksum;
  
  	while (len) {
  		if (((uintptr_t)addr & 3) == 0)
  			break;
  
  		cksum = cksum ^ *addr;
  		addr++;
  		len--;
  	}
  	while (len >= sizeof(u32)) {
  		cksum = cksum ^ *(u32 *)addr;
  		addr += 4;
  		len -= 4;
  	}
  	while (len--) {
  		cksum = cksum ^ *addr;
  		addr++;
  	}
  	return p[0] ^ p[1] ^ p[2] ^ p[3];
  }
  
  static u8 esas2r_calc_byte_cksum(void *addr, u32 len, u8 seed)
  {
  	u8 *p = (u8 *)addr;
  	u8 cksum = seed;
  
  	while (len--)
  		cksum = cksum + p[len];
  	return cksum;
  }
  
  /* Interrupt callback to process FM API write requests. */
  static void esas2r_fmapi_callback(struct esas2r_adapter *a,
  				  struct esas2r_request *rq)
  {
  	struct atto_vda_flash_req *vrq = &rq->vrq->flash;
  	struct esas2r_flash_context *fc =
  		(struct esas2r_flash_context *)rq->interrupt_cx;
  
  	if (rq->req_stat == RS_SUCCESS) {
  		/* Last request was successful.  See what to do now. */
  		switch (vrq->sub_func) {
  		case VDA_FLASH_BEGINW:
  			if (fc->sgc.cur_offset == NULL)
  				goto commit;
  
  			vrq->sub_func = VDA_FLASH_WRITE;
  			rq->req_stat = RS_PENDING;
  			break;
  
  		case VDA_FLASH_WRITE:
  commit:
  			vrq->sub_func = VDA_FLASH_COMMIT;
  			rq->req_stat = RS_PENDING;
  			rq->interrupt_cb = fc->interrupt_cb;
  			break;
  
  		default:
  			break;
  		}
  	}
  
  	if (rq->req_stat != RS_PENDING)
  		/*
  		 * All done. call the real callback to complete the FM API
  		 * request.  We should only get here if a BEGINW or WRITE
  		 * operation failed.
  		 */
  		(*fc->interrupt_cb)(a, rq);
  }
  
  /*
   * Build a flash request based on the flash context.  The request status
   * is filled in on an error.
   */
  static void build_flash_msg(struct esas2r_adapter *a,
  			    struct esas2r_request *rq)
  {
  	struct esas2r_flash_context *fc =
  		(struct esas2r_flash_context *)rq->interrupt_cx;
  	struct esas2r_sg_context *sgc = &fc->sgc;
  	u8 cksum = 0;
  
  	/* calculate the checksum */
  	if (fc->func == VDA_FLASH_BEGINW) {
  		if (sgc->cur_offset)
  			cksum = esas2r_calc_byte_xor_cksum(sgc->cur_offset,
  							   sgc->length,
  							   0);
  		rq->interrupt_cb = esas2r_fmapi_callback;
  	} else {
  		rq->interrupt_cb = fc->interrupt_cb;
  	}
  	esas2r_build_flash_req(a,
  			       rq,
  			       fc->func,
  			       cksum,
  			       fc->flsh_addr,
  			       sgc->length);
  
  	esas2r_rq_free_sg_lists(rq, a);
  
  	/*
  	 * remember the length we asked for.  we have to keep track of
  	 * the current amount done so we know how much to compare when
  	 * doing the verification phase.
  	 */
  	fc->curr_len = fc->sgc.length;
  
  	if (sgc->cur_offset) {
  		/* setup the S/G context to build the S/G table  */
  		esas2r_sgc_init(sgc, a, rq, &rq->vrq->flash.data.sge[0]);
  
  		if (!esas2r_build_sg_list(a, rq, sgc)) {
  			rq->req_stat = RS_BUSY;
  			return;
  		}
  	} else {
  		fc->sgc.length = 0;
  	}
  
  	/* update the flsh_addr to the next one to write to  */
  	fc->flsh_addr += fc->curr_len;
  }
  
  /* determine the method to process the flash request */
  static bool load_image(struct esas2r_adapter *a, struct esas2r_request *rq)
  {
  	/*
  	 * assume we have more to do.  if we return with the status set to
  	 * RS_PENDING, FM API tasks will continue.
  	 */
  	rq->req_stat = RS_PENDING;
  	if (test_bit(AF_DEGRADED_MODE, &a->flags))
  		/* not suppported for now */;
  	else
  		build_flash_msg(a, rq);
  
  	return rq->req_stat == RS_PENDING;
  }
  
  /*  boot image fixer uppers called before downloading the image. */
  static void fix_bios(struct esas2r_adapter *a, struct esas2r_flash_img *fi)
  {
  	struct esas2r_component_header *ch = &fi->cmp_hdr[CH_IT_BIOS];
  	struct esas2r_pc_image *pi;
  	struct esas2r_boot_header *bh;
  
  	pi = (struct esas2r_pc_image *)((u8 *)fi + ch->image_offset);
  	bh =
  		(struct esas2r_boot_header *)((u8 *)pi +
  					      le16_to_cpu(pi->header_offset));
  	bh->device_id = cpu_to_le16(a->pcid->device);
  
  	/* Recalculate the checksum in the PNP header if there  */
  	if (pi->pnp_offset) {
  		u8 *pnp_header_bytes =
  			((u8 *)pi + le16_to_cpu(pi->pnp_offset));
  
  		/* Identifier - dword that starts at byte 10 */
  		*((u32 *)&pnp_header_bytes[10]) =
  			cpu_to_le32(MAKEDWORD(a->pcid->subsystem_vendor,
  					      a->pcid->subsystem_device));
  
  		/* Checksum - byte 9 */
  		pnp_header_bytes[9] -= esas2r_calc_byte_cksum(pnp_header_bytes,
  							      32, 0);
  	}
  
  	/* Recalculate the checksum needed by the PC */
  	pi->checksum = pi->checksum -
  		       esas2r_calc_byte_cksum((u8 *)pi, ch->length, 0);
  }
  
  static void fix_efi(struct esas2r_adapter *a, struct esas2r_flash_img *fi)
  {
  	struct esas2r_component_header *ch = &fi->cmp_hdr[CH_IT_EFI];
  	u32 len = ch->length;
  	u32 offset = ch->image_offset;
  	struct esas2r_efi_image *ei;
  	struct esas2r_boot_header *bh;
  
  	while (len) {
  		u32 thislen;
  
  		ei = (struct esas2r_efi_image *)((u8 *)fi + offset);
  		bh = (struct esas2r_boot_header *)((u8 *)ei +
  						   le16_to_cpu(
  							   ei->header_offset));
  		bh->device_id = cpu_to_le16(a->pcid->device);
  		thislen = (u32)le16_to_cpu(bh->image_length) * 512;
  
  		if (thislen > len)
  			break;
  
  		len -= thislen;
  		offset += thislen;
  	}
  }
  
  /* Complete a FM API request with the specified status. */
  static bool complete_fmapi_req(struct esas2r_adapter *a,
  			       struct esas2r_request *rq, u8 fi_stat)
  {
  	struct esas2r_flash_context *fc =
  		(struct esas2r_flash_context *)rq->interrupt_cx;
  	struct esas2r_flash_img *fi = fc->fi;
  
  	fi->status = fi_stat;
  	fi->driver_error = rq->req_stat;
  	rq->interrupt_cb = NULL;
  	rq->req_stat = RS_SUCCESS;
  
  	if (fi_stat != FI_STAT_IMG_VER)
  		memset(fc->scratch, 0, FM_BUF_SZ);
  
  	esas2r_enable_heartbeat(a);
  	clear_bit(AF_FLASH_LOCK, &a->flags);
  	return false;
  }
  
  /* Process each phase of the flash download process. */
  static void fw_download_proc(struct esas2r_adapter *a,
  			     struct esas2r_request *rq)
  {
  	struct esas2r_flash_context *fc =
  		(struct esas2r_flash_context *)rq->interrupt_cx;
  	struct esas2r_flash_img *fi = fc->fi;
  	struct esas2r_component_header *ch;
  	u32 len;
  	u8 *p, *q;
  
  	/* If the previous operation failed, just return. */
  	if (rq->req_stat != RS_SUCCESS)
  		goto error;
  
  	/*
  	 * If an upload just completed and the compare length is non-zero,
  	 * then we just read back part of the image we just wrote.  verify the
  	 * section and continue reading until the entire image is verified.
  	 */
  	if (fc->func == VDA_FLASH_READ
  	    && fc->cmp_len) {
  		ch = &fi->cmp_hdr[fc->comp_typ];
  
  		p = fc->scratch;
  		q = (u8 *)fi                    /* start of the whole gob     */
  		    + ch->image_offset          /* start of the current image */
  		    + ch->length                /* end of the current image   */
  		    - fc->cmp_len;              /* where we are now           */
  
  		/*
  		 * NOTE - curr_len is the exact count of bytes for the read
  		 *        even when the end is read and its not a full buffer
  		 */
  		for (len = fc->curr_len; len; len--)
  			if (*p++ != *q++)
  				goto error;
  
  		fc->cmp_len -= fc->curr_len; /* # left to compare    */
  
  		/* Update fc and determine the length for the next upload */
  		if (fc->cmp_len > FM_BUF_SZ)
  			fc->sgc.length = FM_BUF_SZ;
  		else
  			fc->sgc.length = fc->cmp_len;
  
  		fc->sgc.cur_offset = fc->sgc_offset +
  				     ((u8 *)fc->scratch - (u8 *)fi);
  	}
  
  	/*
  	 * This code uses a 'while' statement since the next component may
  	 * have a length = zero.  This can happen since some components are
  	 * not required.  At the end of this 'while' we set up the length
  	 * for the next request and therefore sgc.length can be = 0.
  	 */
  	while (fc->sgc.length == 0) {
  		ch = &fi->cmp_hdr[fc->comp_typ];
  
  		switch (fc->task) {
  		case FMTSK_ERASE_BOOT:
  			/* the BIOS image is written next */
  			ch = &fi->cmp_hdr[CH_IT_BIOS];
  			if (ch->length == 0)
  				goto no_bios;
  
  			fc->task = FMTSK_WRTBIOS;
  			fc->func = VDA_FLASH_BEGINW;
  			fc->comp_typ = CH_IT_BIOS;
  			fc->flsh_addr = FLS_OFFSET_BOOT;
  			fc->sgc.length = ch->length;
  			fc->sgc.cur_offset = fc->sgc_offset +
  					     ch->image_offset;
  			break;
  
  		case FMTSK_WRTBIOS:
  			/*
  			 * The BIOS image has been written - read it and
  			 * verify it
  			 */
  			fc->task = FMTSK_READBIOS;
  			fc->func = VDA_FLASH_READ;
  			fc->flsh_addr = FLS_OFFSET_BOOT;
  			fc->cmp_len = ch->length;
  			fc->sgc.length = FM_BUF_SZ;
  			fc->sgc.cur_offset = fc->sgc_offset
  					     + ((u8 *)fc->scratch -
  						(u8 *)fi);
  			break;
  
  		case FMTSK_READBIOS:
  no_bios:
  			/*
  			 * Mark the component header status for the image
  			 * completed
  			 */
  			ch->status = CH_STAT_SUCCESS;
  
  			/* The MAC image is written next */
  			ch = &fi->cmp_hdr[CH_IT_MAC];
  			if (ch->length == 0)
  				goto no_mac;
  
  			fc->task = FMTSK_WRTMAC;
  			fc->func = VDA_FLASH_BEGINW;
  			fc->comp_typ = CH_IT_MAC;
  			fc->flsh_addr = FLS_OFFSET_BOOT
  					+ fi->cmp_hdr[CH_IT_BIOS].length;
  			fc->sgc.length = ch->length;
  			fc->sgc.cur_offset = fc->sgc_offset +
  					     ch->image_offset;
  			break;
  
  		case FMTSK_WRTMAC:
  			/* The MAC image has been written - read and verify */
  			fc->task = FMTSK_READMAC;
  			fc->func = VDA_FLASH_READ;
  			fc->flsh_addr -= ch->length;
  			fc->cmp_len = ch->length;
  			fc->sgc.length = FM_BUF_SZ;
  			fc->sgc.cur_offset = fc->sgc_offset
  					     + ((u8 *)fc->scratch -
  						(u8 *)fi);
  			break;
  
  		case FMTSK_READMAC:
  no_mac:
  			/*
  			 * Mark the component header status for the image
  			 * completed
  			 */
  			ch->status = CH_STAT_SUCCESS;
  
  			/* The EFI image is written next */
  			ch = &fi->cmp_hdr[CH_IT_EFI];
  			if (ch->length == 0)
  				goto no_efi;
  
  			fc->task = FMTSK_WRTEFI;
  			fc->func = VDA_FLASH_BEGINW;
  			fc->comp_typ = CH_IT_EFI;
  			fc->flsh_addr = FLS_OFFSET_BOOT
  					+ fi->cmp_hdr[CH_IT_BIOS].length
  					+ fi->cmp_hdr[CH_IT_MAC].length;
  			fc->sgc.length = ch->length;
  			fc->sgc.cur_offset = fc->sgc_offset +
  					     ch->image_offset;
  			break;
  
  		case FMTSK_WRTEFI:
  			/* The EFI image has been written - read and verify */
  			fc->task = FMTSK_READEFI;
  			fc->func = VDA_FLASH_READ;
  			fc->flsh_addr -= ch->length;
  			fc->cmp_len = ch->length;
  			fc->sgc.length = FM_BUF_SZ;
  			fc->sgc.cur_offset = fc->sgc_offset
  					     + ((u8 *)fc->scratch -
  						(u8 *)fi);
  			break;
  
  		case FMTSK_READEFI:
  no_efi:
  			/*
  			 * Mark the component header status for the image
  			 * completed
  			 */
  			ch->status = CH_STAT_SUCCESS;
  
  			/* The CFG image is written next */
  			ch = &fi->cmp_hdr[CH_IT_CFG];
  
  			if (ch->length == 0)
  				goto no_cfg;
  			fc->task = FMTSK_WRTCFG;
  			fc->func = VDA_FLASH_BEGINW;
  			fc->comp_typ = CH_IT_CFG;
  			fc->flsh_addr = FLS_OFFSET_CPYR - ch->length;
  			fc->sgc.length = ch->length;
  			fc->sgc.cur_offset = fc->sgc_offset +
  					     ch->image_offset;
  			break;
  
  		case FMTSK_WRTCFG:
  			/* The CFG image has been written - read and verify */
  			fc->task = FMTSK_READCFG;
  			fc->func = VDA_FLASH_READ;
  			fc->flsh_addr = FLS_OFFSET_CPYR - ch->length;
  			fc->cmp_len = ch->length;
  			fc->sgc.length = FM_BUF_SZ;
  			fc->sgc.cur_offset = fc->sgc_offset
  					     + ((u8 *)fc->scratch -
  						(u8 *)fi);
  			break;
  
  		case FMTSK_READCFG:
  no_cfg:
  			/*
  			 * Mark the component header status for the image
  			 * completed
  			 */
  			ch->status = CH_STAT_SUCCESS;
  
  			/*
  			 * The download is complete.  If in degraded mode,
  			 * attempt a chip reset.
  			 */
  			if (test_bit(AF_DEGRADED_MODE, &a->flags))
  				esas2r_local_reset_adapter(a);
  
  			a->flash_ver = fi->cmp_hdr[CH_IT_BIOS].version;
  			esas2r_print_flash_rev(a);
  
  			/* Update the type of boot image on the card */
  			memcpy(a->image_type, fi->rel_version,
  			       sizeof(fi->rel_version));
  			complete_fmapi_req(a, rq, FI_STAT_SUCCESS);
  			return;
  		}
  
  		/* If verifying, don't try reading more than what's there */
  		if (fc->func == VDA_FLASH_READ
  		    && fc->sgc.length > fc->cmp_len)
  			fc->sgc.length = fc->cmp_len;
  	}
  
  	/* Build the request to perform the next action */
  	if (!load_image(a, rq)) {
  error:
  		if (fc->comp_typ < fi->num_comps) {
  			ch = &fi->cmp_hdr[fc->comp_typ];
  			ch->status = CH_STAT_FAILED;
  		}
  
  		complete_fmapi_req(a, rq, FI_STAT_FAILED);
  	}
  }
  
  /* Determine the flash image adaptyp for this adapter */
  static u8 get_fi_adap_type(struct esas2r_adapter *a)
  {
  	u8 type;
  
  	/* use the device ID to get the correct adap_typ for this HBA */
  	switch (a->pcid->device) {
  	case ATTO_DID_INTEL_IOP348:
  		type = FI_AT_SUN_LAKE;
  		break;
  
  	case ATTO_DID_MV_88RC9580:
  	case ATTO_DID_MV_88RC9580TS:
  	case ATTO_DID_MV_88RC9580TSE:
  	case ATTO_DID_MV_88RC9580TL:
  		type = FI_AT_MV_9580;
  		break;
  
  	default:
  		type = FI_AT_UNKNWN;
  		break;
  	}
  
  	return type;
  }
  
  /* Size of config + copyright + flash_ver images, 0 for failure. */
  static u32 chk_cfg(u8 *cfg, u32 length, u32 *flash_ver)
  {
  	u16 *pw = (u16 *)cfg - 1;
  	u32 sz = 0;
  	u32 len = length;
  
  	if (len == 0)
  		len = FM_BUF_SZ;
  
  	if (flash_ver)
  		*flash_ver = 0;
  
  	while (true) {
  		u16 type;
  		u16 size;
  
  		type = le16_to_cpu(*pw--);
  		size = le16_to_cpu(*pw--);
  
  		if (type != FBT_CPYR
  		    && type != FBT_SETUP
  		    && type != FBT_FLASH_VER)
  			break;
  
  		if (type == FBT_FLASH_VER
  		    && flash_ver)
  			*flash_ver = le32_to_cpu(*(u32 *)(pw - 1));
  
  		sz += size + (2 * sizeof(u16));
  		pw -= size / sizeof(u16);
  
  		if (sz > len - (2 * sizeof(u16)))
  			break;
  	}
  
  	/* See if we are comparing the size to the specified length */
  	if (length && sz != length)
  		return 0;
  
  	return sz;
  }
  
  /* Verify that the boot image is valid */
  static u8 chk_boot(u8 *boot_img, u32 length)
  {
  	struct esas2r_boot_image *bi = (struct esas2r_boot_image *)boot_img;
  	u16 hdroffset = le16_to_cpu(bi->header_offset);
  	struct esas2r_boot_header *bh;
  
  	if (bi->signature != le16_to_cpu(0xaa55)
  	    || (long)hdroffset >
  	    (long)(65536L - sizeof(struct esas2r_boot_header))
  	    || (hdroffset & 3)
  	    || (hdroffset < sizeof(struct esas2r_boot_image))
  	    || ((u32)hdroffset + sizeof(struct esas2r_boot_header) > length))
  		return 0xff;
  
  	bh = (struct esas2r_boot_header *)((char *)bi + hdroffset);
  
  	if (bh->signature[0] != 'P'
  	    || bh->signature[1] != 'C'
  	    || bh->signature[2] != 'I'
  	    || bh->signature[3] != 'R'
  	    || le16_to_cpu(bh->struct_length) <
  	    (u16)sizeof(struct esas2r_boot_header)
  	    || bh->class_code[2] != 0x01
  	    || bh->class_code[1] != 0x04
  	    || bh->class_code[0] != 0x00
  	    || (bh->code_type != CODE_TYPE_PC
  		&& bh->code_type != CODE_TYPE_OPEN
  		&& bh->code_type != CODE_TYPE_EFI))
  		return 0xff;
  
  	return bh->code_type;
  }
  
  /* The sum of all the WORDS of the image */
  static u16 calc_fi_checksum(struct esas2r_flash_context *fc)
  {
  	struct esas2r_flash_img *fi = fc->fi;
  	u16 cksum;
  	u32 len;
  	u16 *pw;
  
  	for (len = (fi->length - fc->fi_hdr_len) / 2,
  	     pw = (u16 *)((u8 *)fi + fc->fi_hdr_len),
  	     cksum = 0;
  	     len;
  	     len--, pw++)
  		cksum = cksum + le16_to_cpu(*pw);
  
  	return cksum;
  }
  
  /*
   * Verify the flash image structure.  The following verifications will
   * be performed:
   *              1)  verify the fi_version is correct
   *              2)  verify the checksum of the entire image.
   *              3)  validate the adap_typ, action and length fields.
   *              4)  valdiate each component header. check the img_type and
   *                  length fields
   *              5)  valdiate each component image.  validate signatures and
   *                  local checksums
   */
  static bool verify_fi(struct esas2r_adapter *a,
  		      struct esas2r_flash_context *fc)
  {
  	struct esas2r_flash_img *fi = fc->fi;
  	u8 type;
  	bool imgerr;
  	u16 i;
  	u32 len;
  	struct esas2r_component_header *ch;
  
  	/* Verify the length - length must even since we do a word checksum */
  	len = fi->length;
  
  	if ((len & 1)
  	    || len < fc->fi_hdr_len) {
  		fi->status = FI_STAT_LENGTH;
  		return false;
  	}
  
  	/* Get adapter type and verify type in flash image */
  	type = get_fi_adap_type(a);
  	if ((type == FI_AT_UNKNWN) || (fi->adap_typ != type)) {
  		fi->status = FI_STAT_ADAPTYP;
  		return false;
  	}
  
  	/*
  	 * Loop through each component and verify the img_type and length
  	 * fields.  Keep a running count of the sizes sooze we can verify total
  	 * size to additive size.
  	 */
  	imgerr = false;
  
  	for (i = 0, len = 0, ch = fi->cmp_hdr;
  	     i < fi->num_comps;
  	     i++, ch++) {
  		bool cmperr = false;
  
  		/*
  		 * Verify that the component header has the same index as the
  		 * image type.  The headers must be ordered correctly
  		 */
  		if (i != ch->img_type) {
  			imgerr = true;
  			ch->status = CH_STAT_INVALID;
  			continue;
  		}
  
  		switch (ch->img_type) {
  		case CH_IT_BIOS:
  			type = CODE_TYPE_PC;
  			break;
  
  		case CH_IT_MAC:
  			type = CODE_TYPE_OPEN;
  			break;
  
  		case CH_IT_EFI:
  			type = CODE_TYPE_EFI;
  			break;
  		}
  
  		switch (ch->img_type) {
  		case CH_IT_FW:
  		case CH_IT_NVR:
  			break;
  
  		case CH_IT_BIOS:
  		case CH_IT_MAC:
  		case CH_IT_EFI:
  			if (ch->length & 0x1ff)
  				cmperr = true;
  
  			/* Test if component image is present  */
  			if (ch->length == 0)
  				break;
  
  			/* Image is present - verify the image */
  			if (chk_boot((u8 *)fi + ch->image_offset, ch->length)
  			    != type)
  				cmperr = true;
  
  			break;
  
  		case CH_IT_CFG:
  
  			/* Test if component image is present */
  			if (ch->length == 0) {
  				cmperr = true;
  				break;
  			}
  
  			/* Image is present - verify the image */
  			if (!chk_cfg((u8 *)fi + ch->image_offset + ch->length,
  				     ch->length, NULL))
  				cmperr = true;
  
  			break;
  
  		default:
  
  			fi->status = FI_STAT_UNKNOWN;
  			return false;
  		}
  
  		if (cmperr) {
  			imgerr = true;
  			ch->status = CH_STAT_INVALID;
  		} else {
  			ch->status = CH_STAT_PENDING;
  			len += ch->length;
  		}
  	}
  
  	if (imgerr) {
  		fi->status = FI_STAT_MISSING;
  		return false;
  	}
  
  	/* Compare fi->length to the sum of ch->length fields */
  	if (len != fi->length - fc->fi_hdr_len) {
  		fi->status = FI_STAT_LENGTH;
  		return false;
  	}
  
  	/* Compute the checksum - it should come out zero */
  	if (fi->checksum != calc_fi_checksum(fc)) {
  		fi->status = FI_STAT_CHKSUM;
  		return false;
  	}
  
  	return true;
  }
  
  /* Fill in the FS IOCTL response data from a completed request. */
  static void esas2r_complete_fs_ioctl(struct esas2r_adapter *a,
  				     struct esas2r_request *rq)
  {
  	struct esas2r_ioctl_fs *fs =
  		(struct esas2r_ioctl_fs *)rq->interrupt_cx;
  
  	if (rq->vrq->flash.sub_func == VDA_FLASH_COMMIT)
  		esas2r_enable_heartbeat(a);
  
  	fs->driver_error = rq->req_stat;
  
  	if (fs->driver_error == RS_SUCCESS)
  		fs->status = ATTO_STS_SUCCESS;
  	else
  		fs->status = ATTO_STS_FAILED;
  }
  
  /* Prepare an FS IOCTL request to be sent to the firmware. */
  bool esas2r_process_fs_ioctl(struct esas2r_adapter *a,
  			     struct esas2r_ioctl_fs *fs,
  			     struct esas2r_request *rq,
  			     struct esas2r_sg_context *sgc)
  {
  	u8 cmdcnt = (u8)ARRAY_SIZE(cmd_to_fls_func);
  	struct esas2r_ioctlfs_command *fsc = &fs->command;
  	u8 func = 0;
  	u32 datalen;
  
  	fs->status = ATTO_STS_FAILED;
  	fs->driver_error = RS_PENDING;
  
  	if (fs->version > ESAS2R_FS_VER) {
  		fs->status = ATTO_STS_INV_VERSION;
  		return false;
  	}
  
  	if (fsc->command >= cmdcnt) {
  		fs->status = ATTO_STS_INV_FUNC;
  		return false;
  	}
  
  	func = cmd_to_fls_func[fsc->command];
  	if (func == 0xFF) {
  		fs->status = ATTO_STS_INV_FUNC;
  		return false;
  	}
  
  	if (fsc->command != ESAS2R_FS_CMD_CANCEL) {
  		if ((a->pcid->device != ATTO_DID_MV_88RC9580
  		     || fs->adap_type != ESAS2R_FS_AT_ESASRAID2)
  		    && (a->pcid->device != ATTO_DID_MV_88RC9580TS
  			|| fs->adap_type != ESAS2R_FS_AT_TSSASRAID2)
  		    && (a->pcid->device != ATTO_DID_MV_88RC9580TSE
  			|| fs->adap_type != ESAS2R_FS_AT_TSSASRAID2E)
  		    && (a->pcid->device != ATTO_DID_MV_88RC9580TL
  			|| fs->adap_type != ESAS2R_FS_AT_TLSASHBA)) {
  			fs->status = ATTO_STS_INV_ADAPTER;
  			return false;
  		}
  
  		if (fs->driver_ver > ESAS2R_FS_DRVR_VER) {
  			fs->status = ATTO_STS_INV_DRVR_VER;
  			return false;
  		}
  	}
  
  	if (test_bit(AF_DEGRADED_MODE, &a->flags)) {
  		fs->status = ATTO_STS_DEGRADED;
  		return false;
  	}
  
  	rq->interrupt_cb = esas2r_complete_fs_ioctl;
  	rq->interrupt_cx = fs;
  	datalen = le32_to_cpu(fsc->length);
  	esas2r_build_flash_req(a,
  			       rq,
  			       func,
  			       fsc->checksum,
  			       le32_to_cpu(fsc->flash_addr),
  			       datalen);
  
  	if (func == VDA_FLASH_WRITE
  	    || func == VDA_FLASH_READ) {
  		if (datalen == 0) {
  			fs->status = ATTO_STS_INV_FUNC;
  			return false;
  		}
  
  		esas2r_sgc_init(sgc, a, rq, rq->vrq->flash.data.sge);
  		sgc->length = datalen;
  
  		if (!esas2r_build_sg_list(a, rq, sgc)) {
  			fs->status = ATTO_STS_OUT_OF_RSRC;
  			return false;
  		}
  	}
  
  	if (func == VDA_FLASH_COMMIT)
  		esas2r_disable_heartbeat(a);
  
  	esas2r_start_request(a, rq);
  
  	return true;
  }
  
  static bool esas2r_flash_access(struct esas2r_adapter *a, u32 function)
  {
  	u32 starttime;
  	u32 timeout;
  	u32 intstat;
  	u32 doorbell;
  
  	/* Disable chip interrupts awhile */
  	if (function == DRBL_FLASH_REQ)
  		esas2r_disable_chip_interrupts(a);
  
  	/* Issue the request to the firmware */
  	esas2r_write_register_dword(a, MU_DOORBELL_IN, function);
  
  	/* Now wait for the firmware to process it */
  	starttime = jiffies_to_msecs(jiffies);
  
  	if (test_bit(AF_CHPRST_PENDING, &a->flags) ||
  	    test_bit(AF_DISC_PENDING, &a->flags))
  		timeout = 40000;
  	else
  		timeout = 5000;
  
  	while (true) {
  		intstat = esas2r_read_register_dword(a, MU_INT_STATUS_OUT);
  
  		if (intstat & MU_INTSTAT_DRBL) {
  			/* Got a doorbell interrupt.  Check for the function */
  			doorbell =
  				esas2r_read_register_dword(a, MU_DOORBELL_OUT);
  			esas2r_write_register_dword(a, MU_DOORBELL_OUT,
  						    doorbell);
  			if (doorbell & function)
  				break;
  		}
  
  		schedule_timeout_interruptible(msecs_to_jiffies(100));
  
  		if ((jiffies_to_msecs(jiffies) - starttime) > timeout) {
  			/*
  			 * Iimeout.  If we were requesting flash access,
  			 * indicate we are done so the firmware knows we gave
  			 * up.  If this was a REQ, we also need to re-enable
  			 * chip interrupts.
  			 */
  			if (function == DRBL_FLASH_REQ) {
  				esas2r_hdebug("flash access timeout");
  				esas2r_write_register_dword(a, MU_DOORBELL_IN,
  							    DRBL_FLASH_DONE);
  				esas2r_enable_chip_interrupts(a);
  			} else {
  				esas2r_hdebug("flash release timeout");
  			}
  
  			return false;
  		}
  	}
  
  	/* if we're done, re-enable chip interrupts */
  	if (function == DRBL_FLASH_DONE)
  		esas2r_enable_chip_interrupts(a);
  
  	return true;
  }
  
  #define WINDOW_SIZE ((signed int)MW_DATA_WINDOW_SIZE)
  
  bool esas2r_read_flash_block(struct esas2r_adapter *a,
  			     void *to,
  			     u32 from,
  			     u32 size)
  {
  	u8 *end = (u8 *)to;
  
  	/* Try to acquire access to the flash */
  	if (!esas2r_flash_access(a, DRBL_FLASH_REQ))
  		return false;
  
  	while (size) {
  		u32 len;
  		u32 offset;
  		u32 iatvr;
  
  		if (test_bit(AF2_SERIAL_FLASH, &a->flags2))
  			iatvr = MW_DATA_ADDR_SER_FLASH + (from & -WINDOW_SIZE);
  		else
  			iatvr = MW_DATA_ADDR_PAR_FLASH + (from & -WINDOW_SIZE);
  
  		esas2r_map_data_window(a, iatvr);
  		offset = from & (WINDOW_SIZE - 1);
  		len = size;
  
  		if (len > WINDOW_SIZE - offset)
  			len = WINDOW_SIZE - offset;
  
  		from += len;
  		size -= len;
  
  		while (len--) {
  			*end++ = esas2r_read_data_byte(a, offset);
  			offset++;
  		}
  	}
  
  	/* Release flash access */
  	esas2r_flash_access(a, DRBL_FLASH_DONE);
  	return true;
  }
  
  bool esas2r_read_flash_rev(struct esas2r_adapter *a)
  {
  	u8 bytes[256];
  	u16 *pw;
  	u16 *pwstart;
  	u16 type;
  	u16 size;
  	u32 sz;
  
  	sz = sizeof(bytes);
  	pw = (u16 *)(bytes + sz);
  	pwstart = (u16 *)bytes + 2;
  
  	if (!esas2r_read_flash_block(a, bytes, FLS_OFFSET_CPYR - sz, sz))
  		goto invalid_rev;
  
  	while (pw >= pwstart) {
  		pw--;
  		type = le16_to_cpu(*pw);
  		pw--;
  		size = le16_to_cpu(*pw);
  		pw -= size / 2;
  
  		if (type == FBT_CPYR
  		    || type == FBT_SETUP
  		    || pw < pwstart)
  			continue;
  
  		if (type == FBT_FLASH_VER)
  			a->flash_ver = le32_to_cpu(*(u32 *)pw);
  
  		break;
  	}
  
  invalid_rev:
  	return esas2r_print_flash_rev(a);
  }
  
  bool esas2r_print_flash_rev(struct esas2r_adapter *a)
  {
  	u16 year = LOWORD(a->flash_ver);
  	u8 day = LOBYTE(HIWORD(a->flash_ver));
  	u8 month = HIBYTE(HIWORD(a->flash_ver));
  
  	if (day == 0
  	    || month == 0
  	    || day > 31
  	    || month > 12
  	    || year < 2006
  	    || year > 9999) {
  		strcpy(a->flash_rev, "not found");
  		a->flash_ver = 0;
  		return false;
  	}
  
  	sprintf(a->flash_rev, "%02d/%02d/%04d", month, day, year);
  	esas2r_hdebug("flash version: %s", a->flash_rev);
  	return true;
  }
  
  /*
   * Find the type of boot image type that is currently in the flash.
   * The chip only has a 64 KB PCI-e expansion ROM
   * size so only one image can be flashed at a time.
   */
  bool esas2r_read_image_type(struct esas2r_adapter *a)
  {
  	u8 bytes[256];
  	struct esas2r_boot_image *bi;
  	struct esas2r_boot_header *bh;
  	u32 sz;
  	u32 len;
  	u32 offset;
  
  	/* Start at the base of the boot images and look for a valid image */
  	sz = sizeof(bytes);
  	len = FLS_LENGTH_BOOT;
  	offset = 0;
  
  	while (true) {
  		if (!esas2r_read_flash_block(a, bytes, FLS_OFFSET_BOOT +
  					     offset,
  					     sz))
  			goto invalid_rev;
  
  		bi = (struct esas2r_boot_image *)bytes;
  		bh = (struct esas2r_boot_header *)((u8 *)bi +
  						   le16_to_cpu(
  							   bi->header_offset));
  		if (bi->signature != cpu_to_le16(0xAA55))
  			goto invalid_rev;
  
  		if (bh->code_type == CODE_TYPE_PC) {
  			strcpy(a->image_type, "BIOS");
  
  			return true;
  		} else if (bh->code_type == CODE_TYPE_EFI) {
  			struct esas2r_efi_image *ei;
  
  			/*
  			 * So we have an EFI image.  There are several types
  			 * so see which architecture we have.
  			 */
  			ei = (struct esas2r_efi_image *)bytes;
  
  			switch (le16_to_cpu(ei->machine_type)) {
  			case EFI_MACHINE_IA32:
  				strcpy(a->image_type, "EFI 32-bit");
  				return true;
  
  			case EFI_MACHINE_IA64:
  				strcpy(a->image_type, "EFI itanium");
  				return true;
  
  			case EFI_MACHINE_X64:
  				strcpy(a->image_type, "EFI 64-bit");
  				return true;
  
  			case EFI_MACHINE_EBC:
  				strcpy(a->image_type, "EFI EBC");
  				return true;
  
  			default:
  				goto invalid_rev;
  			}
  		} else {
  			u32 thislen;
  
  			/* jump to the next image */
  			thislen = (u32)le16_to_cpu(bh->image_length) * 512;
  			if (thislen == 0
  			    || thislen + offset > len
  			    || bh->indicator == INDICATOR_LAST)
  				break;
  
  			offset += thislen;
  		}
  	}
  
  invalid_rev:
  	strcpy(a->image_type, "no boot images");
  	return false;
  }
  
  /*
   *  Read and validate current NVRAM parameters by accessing
   *  physical NVRAM directly.  if currently stored parameters are
   *  invalid, use the defaults.
   */
  bool esas2r_nvram_read_direct(struct esas2r_adapter *a)
  {
  	bool result;
  
  	if (down_interruptible(&a->nvram_semaphore))
  		return false;
  
  	if (!esas2r_read_flash_block(a, a->nvram, FLS_OFFSET_NVR,
  				     sizeof(struct esas2r_sas_nvram))) {
  		esas2r_hdebug("NVRAM read failed, using defaults");
  		return false;
  	}
  
  	result = esas2r_nvram_validate(a);
  
  	up(&a->nvram_semaphore);
  
  	return result;
  }
  
  /* Interrupt callback to process NVRAM completions. */
  static void esas2r_nvram_callback(struct esas2r_adapter *a,
  				  struct esas2r_request *rq)
  {
  	struct atto_vda_flash_req *vrq = &rq->vrq->flash;
  
  	if (rq->req_stat == RS_SUCCESS) {
  		/* last request was successful.  see what to do now. */
  
  		switch (vrq->sub_func) {
  		case VDA_FLASH_BEGINW:
  			vrq->sub_func = VDA_FLASH_WRITE;
  			rq->req_stat = RS_PENDING;
  			break;
  
  		case VDA_FLASH_WRITE:
  			vrq->sub_func = VDA_FLASH_COMMIT;
  			rq->req_stat = RS_PENDING;
  			break;
  
  		case VDA_FLASH_READ:
  			esas2r_nvram_validate(a);
  			break;
  
  		case VDA_FLASH_COMMIT:
  		default:
  			break;
  		}
  	}
  
  	if (rq->req_stat != RS_PENDING) {
  		/* update the NVRAM state */
  		if (rq->req_stat == RS_SUCCESS)
  			set_bit(AF_NVR_VALID, &a->flags);
  		else
  			clear_bit(AF_NVR_VALID, &a->flags);
  
  		esas2r_enable_heartbeat(a);
  
  		up(&a->nvram_semaphore);
  	}
  }
  
  /*
   * Write the contents of nvram to the adapter's physical NVRAM.
   * The cached copy of the NVRAM is also updated.
   */
  bool esas2r_nvram_write(struct esas2r_adapter *a, struct esas2r_request *rq,
  			struct esas2r_sas_nvram *nvram)
  {
  	struct esas2r_sas_nvram *n = nvram;
  	u8 sas_address_bytes[8];
  	u32 *sas_address_dwords = (u32 *)&sas_address_bytes[0];
  	struct atto_vda_flash_req *vrq = &rq->vrq->flash;
  
  	if (test_bit(AF_DEGRADED_MODE, &a->flags))
  		return false;
  
  	if (down_interruptible(&a->nvram_semaphore))
  		return false;
  
  	if (n == NULL)
  		n = a->nvram;
  
  	/* check the validity of the settings */
  	if (n->version > SASNVR_VERSION) {
  		up(&a->nvram_semaphore);
  		return false;
  	}
  
  	memcpy(&sas_address_bytes[0], n->sas_addr, 8);
  
  	if (sas_address_bytes[0] != 0x50
  	    || sas_address_bytes[1] != 0x01
  	    || sas_address_bytes[2] != 0x08
  	    || (sas_address_bytes[3] & 0xF0) != 0x60
  	    || ((sas_address_bytes[3] & 0x0F) | sas_address_dwords[1]) == 0) {
  		up(&a->nvram_semaphore);
  		return false;
  	}
  
  	if (n->spin_up_delay > SASNVR_SPINUP_MAX)
  		n->spin_up_delay = SASNVR_SPINUP_MAX;
  
  	n->version = SASNVR_VERSION;
  	n->checksum = n->checksum - esas2r_nvramcalc_cksum(n);
  	memcpy(a->nvram, n, sizeof(struct esas2r_sas_nvram));
  
  	/* write the NVRAM */
  	n = a->nvram;
  	esas2r_disable_heartbeat(a);
  
  	esas2r_build_flash_req(a,
  			       rq,
  			       VDA_FLASH_BEGINW,
  			       esas2r_nvramcalc_xor_cksum(n),
  			       FLS_OFFSET_NVR,
  			       sizeof(struct esas2r_sas_nvram));
  
  	if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) {
  
  		vrq->data.sge[0].length =
  			cpu_to_le32(SGE_LAST |
  				    sizeof(struct esas2r_sas_nvram));
  		vrq->data.sge[0].address = cpu_to_le64(
  			a->uncached_phys + (u64)((u8 *)n - a->uncached));
  	} else {
  		vrq->data.prde[0].ctl_len =
  			cpu_to_le32(sizeof(struct esas2r_sas_nvram));
  		vrq->data.prde[0].address = cpu_to_le64(
  			a->uncached_phys
  			+ (u64)((u8 *)n - a->uncached));
  	}
  	rq->interrupt_cb = esas2r_nvram_callback;
  	esas2r_start_request(a, rq);
  	return true;
  }
  
  /* Validate the cached NVRAM.  if the NVRAM is invalid, load the defaults. */
  bool esas2r_nvram_validate(struct esas2r_adapter *a)
  {
  	struct esas2r_sas_nvram *n = a->nvram;
  	bool rslt = false;
  
  	if (n->signature[0] != 'E'
  	    || n->signature[1] != 'S'
  	    || n->signature[2] != 'A'
  	    || n->signature[3] != 'S') {
  		esas2r_hdebug("invalid NVRAM signature");
  	} else if (esas2r_nvramcalc_cksum(n)) {
  		esas2r_hdebug("invalid NVRAM checksum");
  	} else if (n->version > SASNVR_VERSION) {
  		esas2r_hdebug("invalid NVRAM version");
  	} else {
  		set_bit(AF_NVR_VALID, &a->flags);
  		rslt = true;
  	}
  
  	if (rslt == false) {
  		esas2r_hdebug("using defaults");
  		esas2r_nvram_set_defaults(a);
  	}
  
  	return rslt;
  }
  
  /*
   * Set the cached NVRAM to defaults.  note that this function sets the default
   * NVRAM when it has been determined that the physical NVRAM is invalid.
   * In this case, the SAS address is fabricated.
   */
  void esas2r_nvram_set_defaults(struct esas2r_adapter *a)
  {
  	struct esas2r_sas_nvram *n = a->nvram;
  	u32 time = jiffies_to_msecs(jiffies);
  
  	clear_bit(AF_NVR_VALID, &a->flags);
  	*n = default_sas_nvram;
  	n->sas_addr[3] |= 0x0F;
  	n->sas_addr[4] = HIBYTE(LOWORD(time));
  	n->sas_addr[5] = LOBYTE(LOWORD(time));
  	n->sas_addr[6] = a->pcid->bus->number;
  	n->sas_addr[7] = a->pcid->devfn;
  }
  
  void esas2r_nvram_get_defaults(struct esas2r_adapter *a,
  			       struct esas2r_sas_nvram *nvram)
  {
  	u8 sas_addr[8];
  
  	/*
  	 * in case we are copying the defaults into the adapter, copy the SAS
  	 * address out first.
  	 */
  	memcpy(&sas_addr[0], a->nvram->sas_addr, 8);
  	*nvram = default_sas_nvram;
  	memcpy(&nvram->sas_addr[0], &sas_addr[0], 8);
  }
  
  bool esas2r_fm_api(struct esas2r_adapter *a, struct esas2r_flash_img *fi,
  		   struct esas2r_request *rq, struct esas2r_sg_context *sgc)
  {
  	struct esas2r_flash_context *fc = &a->flash_context;
  	u8 j;
  	struct esas2r_component_header *ch;
  
  	if (test_and_set_bit(AF_FLASH_LOCK, &a->flags)) {
  		/* flag was already set */
  		fi->status = FI_STAT_BUSY;
  		return false;
  	}
  
  	memcpy(&fc->sgc, sgc, sizeof(struct esas2r_sg_context));
  	sgc = &fc->sgc;
  	fc->fi = fi;
  	fc->sgc_offset = sgc->cur_offset;
  	rq->req_stat = RS_SUCCESS;
  	rq->interrupt_cx = fc;
  
  	switch (fi->fi_version) {
  	case FI_VERSION_1:
  		fc->scratch = ((struct esas2r_flash_img *)fi)->scratch_buf;
  		fc->num_comps = FI_NUM_COMPS_V1;
  		fc->fi_hdr_len = sizeof(struct esas2r_flash_img);
  		break;
  
  	default:
  		return complete_fmapi_req(a, rq, FI_STAT_IMG_VER);
  	}
  
  	if (test_bit(AF_DEGRADED_MODE, &a->flags))
  		return complete_fmapi_req(a, rq, FI_STAT_DEGRADED);
  
  	switch (fi->action) {
  	case FI_ACT_DOWN: /* Download the components */
  		/* Verify the format of the flash image */
  		if (!verify_fi(a, fc))
  			return complete_fmapi_req(a, rq, fi->status);
  
  		/* Adjust the BIOS fields that are dependent on the HBA */
  		ch = &fi->cmp_hdr[CH_IT_BIOS];
  
  		if (ch->length)
  			fix_bios(a, fi);
  
  		/* Adjust the EFI fields that are dependent on the HBA */
  		ch = &fi->cmp_hdr[CH_IT_EFI];
  
  		if (ch->length)
  			fix_efi(a, fi);
  
  		/*
  		 * Since the image was just modified, compute the checksum on
  		 * the modified image.  First update the CRC for the composite
  		 * expansion ROM image.
  		 */
  		fi->checksum = calc_fi_checksum(fc);
  
  		/* Disable the heartbeat */
  		esas2r_disable_heartbeat(a);
  
  		/* Now start up the download sequence */
  		fc->task = FMTSK_ERASE_BOOT;
  		fc->func = VDA_FLASH_BEGINW;
  		fc->comp_typ = CH_IT_CFG;
  		fc->flsh_addr = FLS_OFFSET_BOOT;
  		fc->sgc.length = FLS_LENGTH_BOOT;
  		fc->sgc.cur_offset = NULL;
  
  		/* Setup the callback address */
  		fc->interrupt_cb = fw_download_proc;
  		break;
  
  	case FI_ACT_UPSZ: /* Get upload sizes */
  		fi->adap_typ = get_fi_adap_type(a);
  		fi->flags = 0;
  		fi->num_comps = fc->num_comps;
  		fi->length = fc->fi_hdr_len;
  
  		/* Report the type of boot image in the rel_version string */
  		memcpy(fi->rel_version, a->image_type,
  		       sizeof(fi->rel_version));
  
  		/* Build the component headers */
  		for (j = 0, ch = fi->cmp_hdr;
  		     j < fi->num_comps;
  		     j++, ch++) {
  			ch->img_type = j;
  			ch->status = CH_STAT_PENDING;
  			ch->length = 0;
  			ch->version = 0xffffffff;
  			ch->image_offset = 0;
  			ch->pad[0] = 0;
  			ch->pad[1] = 0;
  		}
  
  		if (a->flash_ver != 0) {
  			fi->cmp_hdr[CH_IT_BIOS].version =
  				fi->cmp_hdr[CH_IT_MAC].version =
  					fi->cmp_hdr[CH_IT_EFI].version =
  						fi->cmp_hdr[CH_IT_CFG].version
  							= a->flash_ver;
  
  			fi->cmp_hdr[CH_IT_BIOS].status =
  				fi->cmp_hdr[CH_IT_MAC].status =
  					fi->cmp_hdr[CH_IT_EFI].status =
  						fi->cmp_hdr[CH_IT_CFG].status =
  							CH_STAT_SUCCESS;
  
  			return complete_fmapi_req(a, rq, FI_STAT_SUCCESS);
  		}
  
  	/* fall through */
  
  	case FI_ACT_UP: /* Upload the components */
  	default:
  		return complete_fmapi_req(a, rq, FI_STAT_INVALID);
  	}
  
  	/*
  	 * If we make it here, fc has been setup to do the first task.  Call
  	 * load_image to format the request, start it, and get out.  The
  	 * interrupt code will call the callback when the first message is
  	 * complete.
  	 */
  	if (!load_image(a, rq))
  		return complete_fmapi_req(a, rq, FI_STAT_FAILED);
  
  	esas2r_start_request(a, rq);
  
  	return true;
  }
  