xref: /linux/drivers/scsi/esas2r/esas2r_flash.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
126780d9eSBradley Grove 
226780d9eSBradley Grove /*
326780d9eSBradley Grove  *  linux/drivers/scsi/esas2r/esas2r_flash.c
426780d9eSBradley Grove  *      For use with ATTO ExpressSAS R6xx SAS/SATA RAID controllers
526780d9eSBradley Grove  *
626780d9eSBradley Grove  *  Copyright (c) 2001-2013 ATTO Technology, Inc.
726780d9eSBradley Grove  *  (mailto:linuxdrivers@attotech.com)
826780d9eSBradley Grove  *
926780d9eSBradley Grove  * This program is free software; you can redistribute it and/or
1026780d9eSBradley Grove  * modify it under the terms of the GNU General Public License
1126780d9eSBradley Grove  * as published by the Free Software Foundation; either version 2
1226780d9eSBradley Grove  * of the License, or (at your option) any later version.
1326780d9eSBradley Grove  *
1426780d9eSBradley Grove  * This program is distributed in the hope that it will be useful,
1526780d9eSBradley Grove  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1626780d9eSBradley Grove  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1726780d9eSBradley Grove  * GNU General Public License for more details.
1826780d9eSBradley Grove  *
1926780d9eSBradley Grove  * NO WARRANTY
2026780d9eSBradley Grove  * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
2126780d9eSBradley Grove  * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
2226780d9eSBradley Grove  * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
2326780d9eSBradley Grove  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
2426780d9eSBradley Grove  * solely responsible for determining the appropriateness of using and
2526780d9eSBradley Grove  * distributing the Program and assumes all risks associated with its
2626780d9eSBradley Grove  * exercise of rights under this Agreement, including but not limited to
2726780d9eSBradley Grove  * the risks and costs of program errors, damage to or loss of data,
2826780d9eSBradley Grove  * programs or equipment, and unavailability or interruption of operations.
2926780d9eSBradley Grove  *
3026780d9eSBradley Grove  * DISCLAIMER OF LIABILITY
3126780d9eSBradley Grove  * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
3226780d9eSBradley Grove  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3326780d9eSBradley Grove  * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
3426780d9eSBradley Grove  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
3526780d9eSBradley Grove  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
3626780d9eSBradley Grove  * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
3726780d9eSBradley Grove  * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
3826780d9eSBradley Grove  *
3926780d9eSBradley Grove  * You should have received a copy of the GNU General Public License
4026780d9eSBradley Grove  * along with this program; if not, write to the Free Software
4126780d9eSBradley Grove  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
4226780d9eSBradley Grove  * USA.
4326780d9eSBradley Grove  */
4426780d9eSBradley Grove 
4526780d9eSBradley Grove #include "esas2r.h"
4626780d9eSBradley Grove 
4726780d9eSBradley Grove /* local macro defs */
4826780d9eSBradley Grove #define esas2r_nvramcalc_cksum(n)     \
4926780d9eSBradley Grove 	(esas2r_calc_byte_cksum((u8 *)(n), sizeof(struct esas2r_sas_nvram), \
5026780d9eSBradley Grove 				SASNVR_CKSUM_SEED))
5126780d9eSBradley Grove #define esas2r_nvramcalc_xor_cksum(n)  \
5226780d9eSBradley Grove 	(esas2r_calc_byte_xor_cksum((u8 *)(n), \
5326780d9eSBradley Grove 				    sizeof(struct esas2r_sas_nvram), 0))
5426780d9eSBradley Grove 
5526780d9eSBradley Grove #define ESAS2R_FS_DRVR_VER 2
5626780d9eSBradley Grove 
5726780d9eSBradley Grove static struct esas2r_sas_nvram default_sas_nvram = {
5826780d9eSBradley Grove 	{ 'E',	'S',  'A',  'S'			     }, /* signature          */
5926780d9eSBradley Grove 	SASNVR_VERSION,                                 /* version            */
6026780d9eSBradley Grove 	0,                                              /* checksum           */
6126780d9eSBradley Grove 	31,                                             /* max_lun_for_target */
6226780d9eSBradley Grove 	SASNVR_PCILAT_MAX,                              /* pci_latency        */
6326780d9eSBradley Grove 	SASNVR1_BOOT_DRVR,                              /* options1           */
6426780d9eSBradley Grove 	SASNVR2_HEARTBEAT   | SASNVR2_SINGLE_BUS        /* options2           */
6526780d9eSBradley Grove 	| SASNVR2_SW_MUX_CTRL,
6626780d9eSBradley Grove 	SASNVR_COAL_DIS,                                /* int_coalescing     */
6726780d9eSBradley Grove 	SASNVR_CMDTHR_NONE,                             /* cmd_throttle       */
6826780d9eSBradley Grove 	3,                                              /* dev_wait_time      */
6926780d9eSBradley Grove 	1,                                              /* dev_wait_count     */
7026780d9eSBradley Grove 	0,                                              /* spin_up_delay      */
7126780d9eSBradley Grove 	0,                                              /* ssp_align_rate     */
7226780d9eSBradley Grove 	{ 0x50, 0x01, 0x08, 0x60,                       /* sas_addr           */
7326780d9eSBradley Grove 	  0x00, 0x00, 0x00, 0x00 },
7426780d9eSBradley Grove 	{ SASNVR_SPEED_AUTO },                          /* phy_speed          */
7526780d9eSBradley Grove 	{ SASNVR_MUX_DISABLED },                        /* SAS multiplexing   */
7626780d9eSBradley Grove 	{ 0 },                                          /* phy_flags          */
7726780d9eSBradley Grove 	SASNVR_SORT_SAS_ADDR,                           /* sort_type          */
7826780d9eSBradley Grove 	3,                                              /* dpm_reqcmd_lmt     */
7926780d9eSBradley Grove 	3,                                              /* dpm_stndby_time    */
8026780d9eSBradley Grove 	0,                                              /* dpm_active_time    */
8126780d9eSBradley Grove 	{ 0 },                                          /* phy_target_id      */
8226780d9eSBradley Grove 	SASNVR_VSMH_DISABLED,                           /* virt_ses_mode      */
8326780d9eSBradley Grove 	SASNVR_RWM_DEFAULT,                             /* read_write_mode    */
8426780d9eSBradley Grove 	0,                                              /* link down timeout  */
8526780d9eSBradley Grove 	{ 0 }                                           /* reserved           */
8626780d9eSBradley Grove };
8726780d9eSBradley Grove 
8826780d9eSBradley Grove static u8 cmd_to_fls_func[] = {
8926780d9eSBradley Grove 	0xFF,
9026780d9eSBradley Grove 	VDA_FLASH_READ,
9126780d9eSBradley Grove 	VDA_FLASH_BEGINW,
9226780d9eSBradley Grove 	VDA_FLASH_WRITE,
9326780d9eSBradley Grove 	VDA_FLASH_COMMIT,
9426780d9eSBradley Grove 	VDA_FLASH_CANCEL
9526780d9eSBradley Grove };
9626780d9eSBradley Grove 
esas2r_calc_byte_xor_cksum(u8 * addr,u32 len,u8 seed)9726780d9eSBradley Grove static u8 esas2r_calc_byte_xor_cksum(u8 *addr, u32 len, u8 seed)
9826780d9eSBradley Grove {
9926780d9eSBradley Grove 	u32 cksum = seed;
10026780d9eSBradley Grove 	u8 *p = (u8 *)&cksum;
10126780d9eSBradley Grove 
10226780d9eSBradley Grove 	while (len) {
10326780d9eSBradley Grove 		if (((uintptr_t)addr & 3) == 0)
10426780d9eSBradley Grove 			break;
10526780d9eSBradley Grove 
10626780d9eSBradley Grove 		cksum = cksum ^ *addr;
10726780d9eSBradley Grove 		addr++;
10826780d9eSBradley Grove 		len--;
10926780d9eSBradley Grove 	}
11026780d9eSBradley Grove 	while (len >= sizeof(u32)) {
11126780d9eSBradley Grove 		cksum = cksum ^ *(u32 *)addr;
11226780d9eSBradley Grove 		addr += 4;
11326780d9eSBradley Grove 		len -= 4;
11426780d9eSBradley Grove 	}
11526780d9eSBradley Grove 	while (len--) {
11626780d9eSBradley Grove 		cksum = cksum ^ *addr;
11726780d9eSBradley Grove 		addr++;
11826780d9eSBradley Grove 	}
11926780d9eSBradley Grove 	return p[0] ^ p[1] ^ p[2] ^ p[3];
12026780d9eSBradley Grove }
12126780d9eSBradley Grove 
esas2r_calc_byte_cksum(void * addr,u32 len,u8 seed)12226780d9eSBradley Grove static u8 esas2r_calc_byte_cksum(void *addr, u32 len, u8 seed)
12326780d9eSBradley Grove {
12426780d9eSBradley Grove 	u8 *p = (u8 *)addr;
12526780d9eSBradley Grove 	u8 cksum = seed;
12626780d9eSBradley Grove 
12726780d9eSBradley Grove 	while (len--)
12826780d9eSBradley Grove 		cksum = cksum + p[len];
12926780d9eSBradley Grove 	return cksum;
13026780d9eSBradley Grove }
13126780d9eSBradley Grove 
13226780d9eSBradley Grove /* Interrupt callback to process FM API write requests. */
esas2r_fmapi_callback(struct esas2r_adapter * a,struct esas2r_request * rq)13326780d9eSBradley Grove static void esas2r_fmapi_callback(struct esas2r_adapter *a,
13426780d9eSBradley Grove 				  struct esas2r_request *rq)
13526780d9eSBradley Grove {
13626780d9eSBradley Grove 	struct atto_vda_flash_req *vrq = &rq->vrq->flash;
13726780d9eSBradley Grove 	struct esas2r_flash_context *fc =
13826780d9eSBradley Grove 		(struct esas2r_flash_context *)rq->interrupt_cx;
13926780d9eSBradley Grove 
14026780d9eSBradley Grove 	if (rq->req_stat == RS_SUCCESS) {
14126780d9eSBradley Grove 		/* Last request was successful.  See what to do now. */
14226780d9eSBradley Grove 		switch (vrq->sub_func) {
14326780d9eSBradley Grove 		case VDA_FLASH_BEGINW:
14426780d9eSBradley Grove 			if (fc->sgc.cur_offset == NULL)
14526780d9eSBradley Grove 				goto commit;
14626780d9eSBradley Grove 
14726780d9eSBradley Grove 			vrq->sub_func = VDA_FLASH_WRITE;
14826780d9eSBradley Grove 			rq->req_stat = RS_PENDING;
14926780d9eSBradley Grove 			break;
15026780d9eSBradley Grove 
15126780d9eSBradley Grove 		case VDA_FLASH_WRITE:
15226780d9eSBradley Grove commit:
15326780d9eSBradley Grove 			vrq->sub_func = VDA_FLASH_COMMIT;
15426780d9eSBradley Grove 			rq->req_stat = RS_PENDING;
15526780d9eSBradley Grove 			rq->interrupt_cb = fc->interrupt_cb;
15626780d9eSBradley Grove 			break;
15726780d9eSBradley Grove 
15826780d9eSBradley Grove 		default:
15926780d9eSBradley Grove 			break;
16026780d9eSBradley Grove 		}
16126780d9eSBradley Grove 	}
16226780d9eSBradley Grove 
16326780d9eSBradley Grove 	if (rq->req_stat != RS_PENDING)
16426780d9eSBradley Grove 		/*
16526780d9eSBradley Grove 		 * All done. call the real callback to complete the FM API
16626780d9eSBradley Grove 		 * request.  We should only get here if a BEGINW or WRITE
16726780d9eSBradley Grove 		 * operation failed.
16826780d9eSBradley Grove 		 */
16926780d9eSBradley Grove 		(*fc->interrupt_cb)(a, rq);
17026780d9eSBradley Grove }
17126780d9eSBradley Grove 
17226780d9eSBradley Grove /*
17326780d9eSBradley Grove  * Build a flash request based on the flash context.  The request status
17426780d9eSBradley Grove  * is filled in on an error.
17526780d9eSBradley Grove  */
build_flash_msg(struct esas2r_adapter * a,struct esas2r_request * rq)17626780d9eSBradley Grove static void build_flash_msg(struct esas2r_adapter *a,
17726780d9eSBradley Grove 			    struct esas2r_request *rq)
17826780d9eSBradley Grove {
17926780d9eSBradley Grove 	struct esas2r_flash_context *fc =
18026780d9eSBradley Grove 		(struct esas2r_flash_context *)rq->interrupt_cx;
18126780d9eSBradley Grove 	struct esas2r_sg_context *sgc = &fc->sgc;
18226780d9eSBradley Grove 	u8 cksum = 0;
18326780d9eSBradley Grove 
18426780d9eSBradley Grove 	/* calculate the checksum */
18526780d9eSBradley Grove 	if (fc->func == VDA_FLASH_BEGINW) {
18626780d9eSBradley Grove 		if (sgc->cur_offset)
18726780d9eSBradley Grove 			cksum = esas2r_calc_byte_xor_cksum(sgc->cur_offset,
18826780d9eSBradley Grove 							   sgc->length,
18926780d9eSBradley Grove 							   0);
19026780d9eSBradley Grove 		rq->interrupt_cb = esas2r_fmapi_callback;
19126780d9eSBradley Grove 	} else {
19226780d9eSBradley Grove 		rq->interrupt_cb = fc->interrupt_cb;
19326780d9eSBradley Grove 	}
19426780d9eSBradley Grove 	esas2r_build_flash_req(a,
19526780d9eSBradley Grove 			       rq,
19626780d9eSBradley Grove 			       fc->func,
19726780d9eSBradley Grove 			       cksum,
19826780d9eSBradley Grove 			       fc->flsh_addr,
19926780d9eSBradley Grove 			       sgc->length);
20026780d9eSBradley Grove 
20126780d9eSBradley Grove 	esas2r_rq_free_sg_lists(rq, a);
20226780d9eSBradley Grove 
20326780d9eSBradley Grove 	/*
20426780d9eSBradley Grove 	 * remember the length we asked for.  we have to keep track of
20526780d9eSBradley Grove 	 * the current amount done so we know how much to compare when
20626780d9eSBradley Grove 	 * doing the verification phase.
20726780d9eSBradley Grove 	 */
20826780d9eSBradley Grove 	fc->curr_len = fc->sgc.length;
20926780d9eSBradley Grove 
21026780d9eSBradley Grove 	if (sgc->cur_offset) {
21126780d9eSBradley Grove 		/* setup the S/G context to build the S/G table  */
21226780d9eSBradley Grove 		esas2r_sgc_init(sgc, a, rq, &rq->vrq->flash.data.sge[0]);
21326780d9eSBradley Grove 
21426780d9eSBradley Grove 		if (!esas2r_build_sg_list(a, rq, sgc)) {
21526780d9eSBradley Grove 			rq->req_stat = RS_BUSY;
21626780d9eSBradley Grove 			return;
21726780d9eSBradley Grove 		}
21826780d9eSBradley Grove 	} else {
21926780d9eSBradley Grove 		fc->sgc.length = 0;
22026780d9eSBradley Grove 	}
22126780d9eSBradley Grove 
22226780d9eSBradley Grove 	/* update the flsh_addr to the next one to write to  */
22326780d9eSBradley Grove 	fc->flsh_addr += fc->curr_len;
22426780d9eSBradley Grove }
22526780d9eSBradley Grove 
22626780d9eSBradley Grove /* determine the method to process the flash request */
load_image(struct esas2r_adapter * a,struct esas2r_request * rq)22726780d9eSBradley Grove static bool load_image(struct esas2r_adapter *a, struct esas2r_request *rq)
22826780d9eSBradley Grove {
22926780d9eSBradley Grove 	/*
23026780d9eSBradley Grove 	 * assume we have more to do.  if we return with the status set to
23126780d9eSBradley Grove 	 * RS_PENDING, FM API tasks will continue.
23226780d9eSBradley Grove 	 */
23326780d9eSBradley Grove 	rq->req_stat = RS_PENDING;
2349588d24eSBradley Grove 	if (test_bit(AF_DEGRADED_MODE, &a->flags))
235*0676f275SJulia Lawall 		/* not supported for now */;
23626780d9eSBradley Grove 	else
23726780d9eSBradley Grove 		build_flash_msg(a, rq);
23826780d9eSBradley Grove 
23926780d9eSBradley Grove 	return rq->req_stat == RS_PENDING;
24026780d9eSBradley Grove }
24126780d9eSBradley Grove 
24226780d9eSBradley Grove /*  boot image fixer uppers called before downloading the image. */
fix_bios(struct esas2r_adapter * a,struct esas2r_flash_img * fi)24326780d9eSBradley Grove static void fix_bios(struct esas2r_adapter *a, struct esas2r_flash_img *fi)
24426780d9eSBradley Grove {
24526780d9eSBradley Grove 	struct esas2r_component_header *ch = &fi->cmp_hdr[CH_IT_BIOS];
24626780d9eSBradley Grove 	struct esas2r_pc_image *pi;
24726780d9eSBradley Grove 	struct esas2r_boot_header *bh;
24826780d9eSBradley Grove 
24926780d9eSBradley Grove 	pi = (struct esas2r_pc_image *)((u8 *)fi + ch->image_offset);
25026780d9eSBradley Grove 	bh =
25126780d9eSBradley Grove 		(struct esas2r_boot_header *)((u8 *)pi +
25226780d9eSBradley Grove 					      le16_to_cpu(pi->header_offset));
25326780d9eSBradley Grove 	bh->device_id = cpu_to_le16(a->pcid->device);
25426780d9eSBradley Grove 
25526780d9eSBradley Grove 	/* Recalculate the checksum in the PNP header if there  */
25626780d9eSBradley Grove 	if (pi->pnp_offset) {
25726780d9eSBradley Grove 		u8 *pnp_header_bytes =
25826780d9eSBradley Grove 			((u8 *)pi + le16_to_cpu(pi->pnp_offset));
25926780d9eSBradley Grove 
26026780d9eSBradley Grove 		/* Identifier - dword that starts at byte 10 */
26126780d9eSBradley Grove 		*((u32 *)&pnp_header_bytes[10]) =
26226780d9eSBradley Grove 			cpu_to_le32(MAKEDWORD(a->pcid->subsystem_vendor,
26326780d9eSBradley Grove 					      a->pcid->subsystem_device));
26426780d9eSBradley Grove 
26526780d9eSBradley Grove 		/* Checksum - byte 9 */
26626780d9eSBradley Grove 		pnp_header_bytes[9] -= esas2r_calc_byte_cksum(pnp_header_bytes,
26726780d9eSBradley Grove 							      32, 0);
26826780d9eSBradley Grove 	}
26926780d9eSBradley Grove 
27026780d9eSBradley Grove 	/* Recalculate the checksum needed by the PC */
27126780d9eSBradley Grove 	pi->checksum = pi->checksum -
27226780d9eSBradley Grove 		       esas2r_calc_byte_cksum((u8 *)pi, ch->length, 0);
27326780d9eSBradley Grove }
27426780d9eSBradley Grove 
fix_efi(struct esas2r_adapter * a,struct esas2r_flash_img * fi)27526780d9eSBradley Grove static void fix_efi(struct esas2r_adapter *a, struct esas2r_flash_img *fi)
27626780d9eSBradley Grove {
27726780d9eSBradley Grove 	struct esas2r_component_header *ch = &fi->cmp_hdr[CH_IT_EFI];
27826780d9eSBradley Grove 	u32 len = ch->length;
27926780d9eSBradley Grove 	u32 offset = ch->image_offset;
28026780d9eSBradley Grove 	struct esas2r_efi_image *ei;
28126780d9eSBradley Grove 	struct esas2r_boot_header *bh;
28226780d9eSBradley Grove 
28326780d9eSBradley Grove 	while (len) {
28426780d9eSBradley Grove 		u32 thislen;
28526780d9eSBradley Grove 
28626780d9eSBradley Grove 		ei = (struct esas2r_efi_image *)((u8 *)fi + offset);
28726780d9eSBradley Grove 		bh = (struct esas2r_boot_header *)((u8 *)ei +
28826780d9eSBradley Grove 						   le16_to_cpu(
28926780d9eSBradley Grove 							   ei->header_offset));
29026780d9eSBradley Grove 		bh->device_id = cpu_to_le16(a->pcid->device);
29126780d9eSBradley Grove 		thislen = (u32)le16_to_cpu(bh->image_length) * 512;
29226780d9eSBradley Grove 
29326780d9eSBradley Grove 		if (thislen > len)
29426780d9eSBradley Grove 			break;
29526780d9eSBradley Grove 
29626780d9eSBradley Grove 		len -= thislen;
29726780d9eSBradley Grove 		offset += thislen;
29826780d9eSBradley Grove 	}
29926780d9eSBradley Grove }
30026780d9eSBradley Grove 
30126780d9eSBradley Grove /* Complete a FM API request with the specified status. */
complete_fmapi_req(struct esas2r_adapter * a,struct esas2r_request * rq,u8 fi_stat)30226780d9eSBradley Grove static bool complete_fmapi_req(struct esas2r_adapter *a,
30326780d9eSBradley Grove 			       struct esas2r_request *rq, u8 fi_stat)
30426780d9eSBradley Grove {
30526780d9eSBradley Grove 	struct esas2r_flash_context *fc =
30626780d9eSBradley Grove 		(struct esas2r_flash_context *)rq->interrupt_cx;
30726780d9eSBradley Grove 	struct esas2r_flash_img *fi = fc->fi;
30826780d9eSBradley Grove 
30926780d9eSBradley Grove 	fi->status = fi_stat;
31026780d9eSBradley Grove 	fi->driver_error = rq->req_stat;
31126780d9eSBradley Grove 	rq->interrupt_cb = NULL;
31226780d9eSBradley Grove 	rq->req_stat = RS_SUCCESS;
31326780d9eSBradley Grove 
31426780d9eSBradley Grove 	if (fi_stat != FI_STAT_IMG_VER)
31526780d9eSBradley Grove 		memset(fc->scratch, 0, FM_BUF_SZ);
31626780d9eSBradley Grove 
31726780d9eSBradley Grove 	esas2r_enable_heartbeat(a);
3189588d24eSBradley Grove 	clear_bit(AF_FLASH_LOCK, &a->flags);
31926780d9eSBradley Grove 	return false;
32026780d9eSBradley Grove }
32126780d9eSBradley Grove 
32226780d9eSBradley Grove /* Process each phase of the flash download process. */
fw_download_proc(struct esas2r_adapter * a,struct esas2r_request * rq)32326780d9eSBradley Grove static void fw_download_proc(struct esas2r_adapter *a,
32426780d9eSBradley Grove 			     struct esas2r_request *rq)
32526780d9eSBradley Grove {
32626780d9eSBradley Grove 	struct esas2r_flash_context *fc =
32726780d9eSBradley Grove 		(struct esas2r_flash_context *)rq->interrupt_cx;
32826780d9eSBradley Grove 	struct esas2r_flash_img *fi = fc->fi;
32926780d9eSBradley Grove 	struct esas2r_component_header *ch;
33026780d9eSBradley Grove 	u32 len;
33126780d9eSBradley Grove 	u8 *p, *q;
33226780d9eSBradley Grove 
33326780d9eSBradley Grove 	/* If the previous operation failed, just return. */
33426780d9eSBradley Grove 	if (rq->req_stat != RS_SUCCESS)
33526780d9eSBradley Grove 		goto error;
33626780d9eSBradley Grove 
33726780d9eSBradley Grove 	/*
33826780d9eSBradley Grove 	 * If an upload just completed and the compare length is non-zero,
33926780d9eSBradley Grove 	 * then we just read back part of the image we just wrote.  verify the
34026780d9eSBradley Grove 	 * section and continue reading until the entire image is verified.
34126780d9eSBradley Grove 	 */
34226780d9eSBradley Grove 	if (fc->func == VDA_FLASH_READ
34326780d9eSBradley Grove 	    && fc->cmp_len) {
34426780d9eSBradley Grove 		ch = &fi->cmp_hdr[fc->comp_typ];
34526780d9eSBradley Grove 
34626780d9eSBradley Grove 		p = fc->scratch;
34726780d9eSBradley Grove 		q = (u8 *)fi                    /* start of the whole gob     */
34826780d9eSBradley Grove 		    + ch->image_offset          /* start of the current image */
34926780d9eSBradley Grove 		    + ch->length                /* end of the current image   */
35026780d9eSBradley Grove 		    - fc->cmp_len;              /* where we are now           */
35126780d9eSBradley Grove 
35226780d9eSBradley Grove 		/*
35326780d9eSBradley Grove 		 * NOTE - curr_len is the exact count of bytes for the read
35426780d9eSBradley Grove 		 *        even when the end is read and its not a full buffer
35526780d9eSBradley Grove 		 */
35626780d9eSBradley Grove 		for (len = fc->curr_len; len; len--)
35726780d9eSBradley Grove 			if (*p++ != *q++)
35826780d9eSBradley Grove 				goto error;
35926780d9eSBradley Grove 
36026780d9eSBradley Grove 		fc->cmp_len -= fc->curr_len; /* # left to compare    */
36126780d9eSBradley Grove 
36226780d9eSBradley Grove 		/* Update fc and determine the length for the next upload */
36326780d9eSBradley Grove 		if (fc->cmp_len > FM_BUF_SZ)
36426780d9eSBradley Grove 			fc->sgc.length = FM_BUF_SZ;
36526780d9eSBradley Grove 		else
36626780d9eSBradley Grove 			fc->sgc.length = fc->cmp_len;
36726780d9eSBradley Grove 
36826780d9eSBradley Grove 		fc->sgc.cur_offset = fc->sgc_offset +
36926780d9eSBradley Grove 				     ((u8 *)fc->scratch - (u8 *)fi);
37026780d9eSBradley Grove 	}
37126780d9eSBradley Grove 
37226780d9eSBradley Grove 	/*
37326780d9eSBradley Grove 	 * This code uses a 'while' statement since the next component may
37426780d9eSBradley Grove 	 * have a length = zero.  This can happen since some components are
37526780d9eSBradley Grove 	 * not required.  At the end of this 'while' we set up the length
37626780d9eSBradley Grove 	 * for the next request and therefore sgc.length can be = 0.
37726780d9eSBradley Grove 	 */
37826780d9eSBradley Grove 	while (fc->sgc.length == 0) {
37926780d9eSBradley Grove 		ch = &fi->cmp_hdr[fc->comp_typ];
38026780d9eSBradley Grove 
38126780d9eSBradley Grove 		switch (fc->task) {
38226780d9eSBradley Grove 		case FMTSK_ERASE_BOOT:
38326780d9eSBradley Grove 			/* the BIOS image is written next */
38426780d9eSBradley Grove 			ch = &fi->cmp_hdr[CH_IT_BIOS];
38526780d9eSBradley Grove 			if (ch->length == 0)
38626780d9eSBradley Grove 				goto no_bios;
38726780d9eSBradley Grove 
38826780d9eSBradley Grove 			fc->task = FMTSK_WRTBIOS;
38926780d9eSBradley Grove 			fc->func = VDA_FLASH_BEGINW;
39026780d9eSBradley Grove 			fc->comp_typ = CH_IT_BIOS;
39126780d9eSBradley Grove 			fc->flsh_addr = FLS_OFFSET_BOOT;
39226780d9eSBradley Grove 			fc->sgc.length = ch->length;
39326780d9eSBradley Grove 			fc->sgc.cur_offset = fc->sgc_offset +
39426780d9eSBradley Grove 					     ch->image_offset;
39526780d9eSBradley Grove 			break;
39626780d9eSBradley Grove 
39726780d9eSBradley Grove 		case FMTSK_WRTBIOS:
39826780d9eSBradley Grove 			/*
39926780d9eSBradley Grove 			 * The BIOS image has been written - read it and
40026780d9eSBradley Grove 			 * verify it
40126780d9eSBradley Grove 			 */
40226780d9eSBradley Grove 			fc->task = FMTSK_READBIOS;
40326780d9eSBradley Grove 			fc->func = VDA_FLASH_READ;
40426780d9eSBradley Grove 			fc->flsh_addr = FLS_OFFSET_BOOT;
40526780d9eSBradley Grove 			fc->cmp_len = ch->length;
40626780d9eSBradley Grove 			fc->sgc.length = FM_BUF_SZ;
40726780d9eSBradley Grove 			fc->sgc.cur_offset = fc->sgc_offset
40826780d9eSBradley Grove 					     + ((u8 *)fc->scratch -
40926780d9eSBradley Grove 						(u8 *)fi);
41026780d9eSBradley Grove 			break;
41126780d9eSBradley Grove 
41226780d9eSBradley Grove 		case FMTSK_READBIOS:
41326780d9eSBradley Grove no_bios:
41426780d9eSBradley Grove 			/*
41526780d9eSBradley Grove 			 * Mark the component header status for the image
41626780d9eSBradley Grove 			 * completed
41726780d9eSBradley Grove 			 */
41826780d9eSBradley Grove 			ch->status = CH_STAT_SUCCESS;
41926780d9eSBradley Grove 
42026780d9eSBradley Grove 			/* The MAC image is written next */
42126780d9eSBradley Grove 			ch = &fi->cmp_hdr[CH_IT_MAC];
42226780d9eSBradley Grove 			if (ch->length == 0)
42326780d9eSBradley Grove 				goto no_mac;
42426780d9eSBradley Grove 
42526780d9eSBradley Grove 			fc->task = FMTSK_WRTMAC;
42626780d9eSBradley Grove 			fc->func = VDA_FLASH_BEGINW;
42726780d9eSBradley Grove 			fc->comp_typ = CH_IT_MAC;
42826780d9eSBradley Grove 			fc->flsh_addr = FLS_OFFSET_BOOT
42926780d9eSBradley Grove 					+ fi->cmp_hdr[CH_IT_BIOS].length;
43026780d9eSBradley Grove 			fc->sgc.length = ch->length;
43126780d9eSBradley Grove 			fc->sgc.cur_offset = fc->sgc_offset +
43226780d9eSBradley Grove 					     ch->image_offset;
43326780d9eSBradley Grove 			break;
43426780d9eSBradley Grove 
43526780d9eSBradley Grove 		case FMTSK_WRTMAC:
43626780d9eSBradley Grove 			/* The MAC image has been written - read and verify */
43726780d9eSBradley Grove 			fc->task = FMTSK_READMAC;
43826780d9eSBradley Grove 			fc->func = VDA_FLASH_READ;
43926780d9eSBradley Grove 			fc->flsh_addr -= ch->length;
44026780d9eSBradley Grove 			fc->cmp_len = ch->length;
44126780d9eSBradley Grove 			fc->sgc.length = FM_BUF_SZ;
44226780d9eSBradley Grove 			fc->sgc.cur_offset = fc->sgc_offset
44326780d9eSBradley Grove 					     + ((u8 *)fc->scratch -
44426780d9eSBradley Grove 						(u8 *)fi);
44526780d9eSBradley Grove 			break;
44626780d9eSBradley Grove 
44726780d9eSBradley Grove 		case FMTSK_READMAC:
44826780d9eSBradley Grove no_mac:
44926780d9eSBradley Grove 			/*
45026780d9eSBradley Grove 			 * Mark the component header status for the image
45126780d9eSBradley Grove 			 * completed
45226780d9eSBradley Grove 			 */
45326780d9eSBradley Grove 			ch->status = CH_STAT_SUCCESS;
45426780d9eSBradley Grove 
45526780d9eSBradley Grove 			/* The EFI image is written next */
45626780d9eSBradley Grove 			ch = &fi->cmp_hdr[CH_IT_EFI];
45726780d9eSBradley Grove 			if (ch->length == 0)
45826780d9eSBradley Grove 				goto no_efi;
45926780d9eSBradley Grove 
46026780d9eSBradley Grove 			fc->task = FMTSK_WRTEFI;
46126780d9eSBradley Grove 			fc->func = VDA_FLASH_BEGINW;
46226780d9eSBradley Grove 			fc->comp_typ = CH_IT_EFI;
46326780d9eSBradley Grove 			fc->flsh_addr = FLS_OFFSET_BOOT
46426780d9eSBradley Grove 					+ fi->cmp_hdr[CH_IT_BIOS].length
46526780d9eSBradley Grove 					+ fi->cmp_hdr[CH_IT_MAC].length;
46626780d9eSBradley Grove 			fc->sgc.length = ch->length;
46726780d9eSBradley Grove 			fc->sgc.cur_offset = fc->sgc_offset +
46826780d9eSBradley Grove 					     ch->image_offset;
46926780d9eSBradley Grove 			break;
47026780d9eSBradley Grove 
47126780d9eSBradley Grove 		case FMTSK_WRTEFI:
47226780d9eSBradley Grove 			/* The EFI image has been written - read and verify */
47326780d9eSBradley Grove 			fc->task = FMTSK_READEFI;
47426780d9eSBradley Grove 			fc->func = VDA_FLASH_READ;
47526780d9eSBradley Grove 			fc->flsh_addr -= ch->length;
47626780d9eSBradley Grove 			fc->cmp_len = ch->length;
47726780d9eSBradley Grove 			fc->sgc.length = FM_BUF_SZ;
47826780d9eSBradley Grove 			fc->sgc.cur_offset = fc->sgc_offset
47926780d9eSBradley Grove 					     + ((u8 *)fc->scratch -
48026780d9eSBradley Grove 						(u8 *)fi);
48126780d9eSBradley Grove 			break;
48226780d9eSBradley Grove 
48326780d9eSBradley Grove 		case FMTSK_READEFI:
48426780d9eSBradley Grove no_efi:
48526780d9eSBradley Grove 			/*
48626780d9eSBradley Grove 			 * Mark the component header status for the image
48726780d9eSBradley Grove 			 * completed
48826780d9eSBradley Grove 			 */
48926780d9eSBradley Grove 			ch->status = CH_STAT_SUCCESS;
49026780d9eSBradley Grove 
49126780d9eSBradley Grove 			/* The CFG image is written next */
49226780d9eSBradley Grove 			ch = &fi->cmp_hdr[CH_IT_CFG];
49326780d9eSBradley Grove 
49426780d9eSBradley Grove 			if (ch->length == 0)
49526780d9eSBradley Grove 				goto no_cfg;
49626780d9eSBradley Grove 			fc->task = FMTSK_WRTCFG;
49726780d9eSBradley Grove 			fc->func = VDA_FLASH_BEGINW;
49826780d9eSBradley Grove 			fc->comp_typ = CH_IT_CFG;
49926780d9eSBradley Grove 			fc->flsh_addr = FLS_OFFSET_CPYR - ch->length;
50026780d9eSBradley Grove 			fc->sgc.length = ch->length;
50126780d9eSBradley Grove 			fc->sgc.cur_offset = fc->sgc_offset +
50226780d9eSBradley Grove 					     ch->image_offset;
50326780d9eSBradley Grove 			break;
50426780d9eSBradley Grove 
50526780d9eSBradley Grove 		case FMTSK_WRTCFG:
50626780d9eSBradley Grove 			/* The CFG image has been written - read and verify */
50726780d9eSBradley Grove 			fc->task = FMTSK_READCFG;
50826780d9eSBradley Grove 			fc->func = VDA_FLASH_READ;
50926780d9eSBradley Grove 			fc->flsh_addr = FLS_OFFSET_CPYR - ch->length;
51026780d9eSBradley Grove 			fc->cmp_len = ch->length;
51126780d9eSBradley Grove 			fc->sgc.length = FM_BUF_SZ;
51226780d9eSBradley Grove 			fc->sgc.cur_offset = fc->sgc_offset
51326780d9eSBradley Grove 					     + ((u8 *)fc->scratch -
51426780d9eSBradley Grove 						(u8 *)fi);
51526780d9eSBradley Grove 			break;
51626780d9eSBradley Grove 
51726780d9eSBradley Grove 		case FMTSK_READCFG:
51826780d9eSBradley Grove no_cfg:
51926780d9eSBradley Grove 			/*
52026780d9eSBradley Grove 			 * Mark the component header status for the image
52126780d9eSBradley Grove 			 * completed
52226780d9eSBradley Grove 			 */
52326780d9eSBradley Grove 			ch->status = CH_STAT_SUCCESS;
52426780d9eSBradley Grove 
52526780d9eSBradley Grove 			/*
52626780d9eSBradley Grove 			 * The download is complete.  If in degraded mode,
52726780d9eSBradley Grove 			 * attempt a chip reset.
52826780d9eSBradley Grove 			 */
5299588d24eSBradley Grove 			if (test_bit(AF_DEGRADED_MODE, &a->flags))
53026780d9eSBradley Grove 				esas2r_local_reset_adapter(a);
53126780d9eSBradley Grove 
53226780d9eSBradley Grove 			a->flash_ver = fi->cmp_hdr[CH_IT_BIOS].version;
53326780d9eSBradley Grove 			esas2r_print_flash_rev(a);
53426780d9eSBradley Grove 
53526780d9eSBradley Grove 			/* Update the type of boot image on the card */
53626780d9eSBradley Grove 			memcpy(a->image_type, fi->rel_version,
53726780d9eSBradley Grove 			       sizeof(fi->rel_version));
53826780d9eSBradley Grove 			complete_fmapi_req(a, rq, FI_STAT_SUCCESS);
53926780d9eSBradley Grove 			return;
54026780d9eSBradley Grove 		}
54126780d9eSBradley Grove 
54226780d9eSBradley Grove 		/* If verifying, don't try reading more than what's there */
54326780d9eSBradley Grove 		if (fc->func == VDA_FLASH_READ
54426780d9eSBradley Grove 		    && fc->sgc.length > fc->cmp_len)
54526780d9eSBradley Grove 			fc->sgc.length = fc->cmp_len;
54626780d9eSBradley Grove 	}
54726780d9eSBradley Grove 
54826780d9eSBradley Grove 	/* Build the request to perform the next action */
54926780d9eSBradley Grove 	if (!load_image(a, rq)) {
55026780d9eSBradley Grove error:
55126780d9eSBradley Grove 		if (fc->comp_typ < fi->num_comps) {
55226780d9eSBradley Grove 			ch = &fi->cmp_hdr[fc->comp_typ];
55326780d9eSBradley Grove 			ch->status = CH_STAT_FAILED;
55426780d9eSBradley Grove 		}
55526780d9eSBradley Grove 
55626780d9eSBradley Grove 		complete_fmapi_req(a, rq, FI_STAT_FAILED);
55726780d9eSBradley Grove 	}
55826780d9eSBradley Grove }
55926780d9eSBradley Grove 
56026780d9eSBradley Grove /* Determine the flash image adaptyp for this adapter */
get_fi_adap_type(struct esas2r_adapter * a)56126780d9eSBradley Grove static u8 get_fi_adap_type(struct esas2r_adapter *a)
56226780d9eSBradley Grove {
56326780d9eSBradley Grove 	u8 type;
56426780d9eSBradley Grove 
56526780d9eSBradley Grove 	/* use the device ID to get the correct adap_typ for this HBA */
56626780d9eSBradley Grove 	switch (a->pcid->device) {
56726780d9eSBradley Grove 	case ATTO_DID_INTEL_IOP348:
56826780d9eSBradley Grove 		type = FI_AT_SUN_LAKE;
56926780d9eSBradley Grove 		break;
57026780d9eSBradley Grove 
57126780d9eSBradley Grove 	case ATTO_DID_MV_88RC9580:
57226780d9eSBradley Grove 	case ATTO_DID_MV_88RC9580TS:
57326780d9eSBradley Grove 	case ATTO_DID_MV_88RC9580TSE:
57426780d9eSBradley Grove 	case ATTO_DID_MV_88RC9580TL:
57526780d9eSBradley Grove 		type = FI_AT_MV_9580;
57626780d9eSBradley Grove 		break;
57726780d9eSBradley Grove 
57826780d9eSBradley Grove 	default:
57926780d9eSBradley Grove 		type = FI_AT_UNKNWN;
58026780d9eSBradley Grove 		break;
58126780d9eSBradley Grove 	}
58226780d9eSBradley Grove 
58326780d9eSBradley Grove 	return type;
58426780d9eSBradley Grove }
58526780d9eSBradley Grove 
58626780d9eSBradley Grove /* Size of config + copyright + flash_ver images, 0 for failure. */
chk_cfg(u8 * cfg,u32 length,u32 * flash_ver)58726780d9eSBradley Grove static u32 chk_cfg(u8 *cfg, u32 length, u32 *flash_ver)
58826780d9eSBradley Grove {
58926780d9eSBradley Grove 	u16 *pw = (u16 *)cfg - 1;
59026780d9eSBradley Grove 	u32 sz = 0;
59126780d9eSBradley Grove 	u32 len = length;
59226780d9eSBradley Grove 
59326780d9eSBradley Grove 	if (len == 0)
59426780d9eSBradley Grove 		len = FM_BUF_SZ;
59526780d9eSBradley Grove 
59626780d9eSBradley Grove 	if (flash_ver)
59726780d9eSBradley Grove 		*flash_ver = 0;
59826780d9eSBradley Grove 
59926780d9eSBradley Grove 	while (true) {
60026780d9eSBradley Grove 		u16 type;
60126780d9eSBradley Grove 		u16 size;
60226780d9eSBradley Grove 
60326780d9eSBradley Grove 		type = le16_to_cpu(*pw--);
60426780d9eSBradley Grove 		size = le16_to_cpu(*pw--);
60526780d9eSBradley Grove 
60626780d9eSBradley Grove 		if (type != FBT_CPYR
60726780d9eSBradley Grove 		    && type != FBT_SETUP
60826780d9eSBradley Grove 		    && type != FBT_FLASH_VER)
60926780d9eSBradley Grove 			break;
61026780d9eSBradley Grove 
61126780d9eSBradley Grove 		if (type == FBT_FLASH_VER
61226780d9eSBradley Grove 		    && flash_ver)
61326780d9eSBradley Grove 			*flash_ver = le32_to_cpu(*(u32 *)(pw - 1));
61426780d9eSBradley Grove 
61526780d9eSBradley Grove 		sz += size + (2 * sizeof(u16));
61626780d9eSBradley Grove 		pw -= size / sizeof(u16);
61726780d9eSBradley Grove 
61826780d9eSBradley Grove 		if (sz > len - (2 * sizeof(u16)))
61926780d9eSBradley Grove 			break;
62026780d9eSBradley Grove 	}
62126780d9eSBradley Grove 
62226780d9eSBradley Grove 	/* See if we are comparing the size to the specified length */
62326780d9eSBradley Grove 	if (length && sz != length)
62426780d9eSBradley Grove 		return 0;
62526780d9eSBradley Grove 
62626780d9eSBradley Grove 	return sz;
62726780d9eSBradley Grove }
62826780d9eSBradley Grove 
62926780d9eSBradley Grove /* Verify that the boot image is valid */
chk_boot(u8 * boot_img,u32 length)63026780d9eSBradley Grove static u8 chk_boot(u8 *boot_img, u32 length)
63126780d9eSBradley Grove {
63226780d9eSBradley Grove 	struct esas2r_boot_image *bi = (struct esas2r_boot_image *)boot_img;
63326780d9eSBradley Grove 	u16 hdroffset = le16_to_cpu(bi->header_offset);
63426780d9eSBradley Grove 	struct esas2r_boot_header *bh;
63526780d9eSBradley Grove 
63626780d9eSBradley Grove 	if (bi->signature != le16_to_cpu(0xaa55)
63726780d9eSBradley Grove 	    || (long)hdroffset >
63826780d9eSBradley Grove 	    (long)(65536L - sizeof(struct esas2r_boot_header))
63926780d9eSBradley Grove 	    || (hdroffset & 3)
64026780d9eSBradley Grove 	    || (hdroffset < sizeof(struct esas2r_boot_image))
64126780d9eSBradley Grove 	    || ((u32)hdroffset + sizeof(struct esas2r_boot_header) > length))
64226780d9eSBradley Grove 		return 0xff;
64326780d9eSBradley Grove 
64426780d9eSBradley Grove 	bh = (struct esas2r_boot_header *)((char *)bi + hdroffset);
64526780d9eSBradley Grove 
64626780d9eSBradley Grove 	if (bh->signature[0] != 'P'
64726780d9eSBradley Grove 	    || bh->signature[1] != 'C'
64826780d9eSBradley Grove 	    || bh->signature[2] != 'I'
64926780d9eSBradley Grove 	    || bh->signature[3] != 'R'
65026780d9eSBradley Grove 	    || le16_to_cpu(bh->struct_length) <
65126780d9eSBradley Grove 	    (u16)sizeof(struct esas2r_boot_header)
65226780d9eSBradley Grove 	    || bh->class_code[2] != 0x01
65326780d9eSBradley Grove 	    || bh->class_code[1] != 0x04
65426780d9eSBradley Grove 	    || bh->class_code[0] != 0x00
65526780d9eSBradley Grove 	    || (bh->code_type != CODE_TYPE_PC
65626780d9eSBradley Grove 		&& bh->code_type != CODE_TYPE_OPEN
65726780d9eSBradley Grove 		&& bh->code_type != CODE_TYPE_EFI))
65826780d9eSBradley Grove 		return 0xff;
65926780d9eSBradley Grove 
66026780d9eSBradley Grove 	return bh->code_type;
66126780d9eSBradley Grove }
66226780d9eSBradley Grove 
66326780d9eSBradley Grove /* The sum of all the WORDS of the image */
calc_fi_checksum(struct esas2r_flash_context * fc)66426780d9eSBradley Grove static u16 calc_fi_checksum(struct esas2r_flash_context *fc)
66526780d9eSBradley Grove {
66626780d9eSBradley Grove 	struct esas2r_flash_img *fi = fc->fi;
66726780d9eSBradley Grove 	u16 cksum;
66826780d9eSBradley Grove 	u32 len;
66926780d9eSBradley Grove 	u16 *pw;
67026780d9eSBradley Grove 
67126780d9eSBradley Grove 	for (len = (fi->length - fc->fi_hdr_len) / 2,
67226780d9eSBradley Grove 	     pw = (u16 *)((u8 *)fi + fc->fi_hdr_len),
67326780d9eSBradley Grove 	     cksum = 0;
67426780d9eSBradley Grove 	     len;
67526780d9eSBradley Grove 	     len--, pw++)
67626780d9eSBradley Grove 		cksum = cksum + le16_to_cpu(*pw);
67726780d9eSBradley Grove 
67826780d9eSBradley Grove 	return cksum;
67926780d9eSBradley Grove }
68026780d9eSBradley Grove 
68126780d9eSBradley Grove /*
68226780d9eSBradley Grove  * Verify the flash image structure.  The following verifications will
68326780d9eSBradley Grove  * be performed:
68426780d9eSBradley Grove  *              1)  verify the fi_version is correct
68526780d9eSBradley Grove  *              2)  verify the checksum of the entire image.
68626780d9eSBradley Grove  *              3)  validate the adap_typ, action and length fields.
68751879016SBoris Bodemann  *              4)  validate each component header. check the img_type and
68826780d9eSBradley Grove  *                  length fields
68951879016SBoris Bodemann  *              5)  validate each component image.  validate signatures and
69026780d9eSBradley Grove  *                  local checksums
69126780d9eSBradley Grove  */
verify_fi(struct esas2r_adapter * a,struct esas2r_flash_context * fc)69226780d9eSBradley Grove static bool verify_fi(struct esas2r_adapter *a,
69326780d9eSBradley Grove 		      struct esas2r_flash_context *fc)
69426780d9eSBradley Grove {
69526780d9eSBradley Grove 	struct esas2r_flash_img *fi = fc->fi;
69626780d9eSBradley Grove 	u8 type;
69726780d9eSBradley Grove 	bool imgerr;
69826780d9eSBradley Grove 	u16 i;
69926780d9eSBradley Grove 	u32 len;
70026780d9eSBradley Grove 	struct esas2r_component_header *ch;
70126780d9eSBradley Grove 
70226780d9eSBradley Grove 	/* Verify the length - length must even since we do a word checksum */
70326780d9eSBradley Grove 	len = fi->length;
70426780d9eSBradley Grove 
70526780d9eSBradley Grove 	if ((len & 1)
70626780d9eSBradley Grove 	    || len < fc->fi_hdr_len) {
70726780d9eSBradley Grove 		fi->status = FI_STAT_LENGTH;
70826780d9eSBradley Grove 		return false;
70926780d9eSBradley Grove 	}
71026780d9eSBradley Grove 
71126780d9eSBradley Grove 	/* Get adapter type and verify type in flash image */
71226780d9eSBradley Grove 	type = get_fi_adap_type(a);
71326780d9eSBradley Grove 	if ((type == FI_AT_UNKNWN) || (fi->adap_typ != type)) {
71426780d9eSBradley Grove 		fi->status = FI_STAT_ADAPTYP;
71526780d9eSBradley Grove 		return false;
71626780d9eSBradley Grove 	}
71726780d9eSBradley Grove 
71826780d9eSBradley Grove 	/*
71926780d9eSBradley Grove 	 * Loop through each component and verify the img_type and length
72026780d9eSBradley Grove 	 * fields.  Keep a running count of the sizes sooze we can verify total
72126780d9eSBradley Grove 	 * size to additive size.
72226780d9eSBradley Grove 	 */
72326780d9eSBradley Grove 	imgerr = false;
72426780d9eSBradley Grove 
72526780d9eSBradley Grove 	for (i = 0, len = 0, ch = fi->cmp_hdr;
72626780d9eSBradley Grove 	     i < fi->num_comps;
72726780d9eSBradley Grove 	     i++, ch++) {
72826780d9eSBradley Grove 		bool cmperr = false;
72926780d9eSBradley Grove 
73026780d9eSBradley Grove 		/*
73126780d9eSBradley Grove 		 * Verify that the component header has the same index as the
73226780d9eSBradley Grove 		 * image type.  The headers must be ordered correctly
73326780d9eSBradley Grove 		 */
73426780d9eSBradley Grove 		if (i != ch->img_type) {
73526780d9eSBradley Grove 			imgerr = true;
73626780d9eSBradley Grove 			ch->status = CH_STAT_INVALID;
73726780d9eSBradley Grove 			continue;
73826780d9eSBradley Grove 		}
73926780d9eSBradley Grove 
74026780d9eSBradley Grove 		switch (ch->img_type) {
74126780d9eSBradley Grove 		case CH_IT_BIOS:
74226780d9eSBradley Grove 			type = CODE_TYPE_PC;
74326780d9eSBradley Grove 			break;
74426780d9eSBradley Grove 
74526780d9eSBradley Grove 		case CH_IT_MAC:
74626780d9eSBradley Grove 			type = CODE_TYPE_OPEN;
74726780d9eSBradley Grove 			break;
74826780d9eSBradley Grove 
74926780d9eSBradley Grove 		case CH_IT_EFI:
75026780d9eSBradley Grove 			type = CODE_TYPE_EFI;
75126780d9eSBradley Grove 			break;
75226780d9eSBradley Grove 		}
75326780d9eSBradley Grove 
75426780d9eSBradley Grove 		switch (ch->img_type) {
75526780d9eSBradley Grove 		case CH_IT_FW:
75626780d9eSBradley Grove 		case CH_IT_NVR:
75726780d9eSBradley Grove 			break;
75826780d9eSBradley Grove 
75926780d9eSBradley Grove 		case CH_IT_BIOS:
76026780d9eSBradley Grove 		case CH_IT_MAC:
76126780d9eSBradley Grove 		case CH_IT_EFI:
76226780d9eSBradley Grove 			if (ch->length & 0x1ff)
76326780d9eSBradley Grove 				cmperr = true;
76426780d9eSBradley Grove 
76526780d9eSBradley Grove 			/* Test if component image is present  */
76626780d9eSBradley Grove 			if (ch->length == 0)
76726780d9eSBradley Grove 				break;
76826780d9eSBradley Grove 
76926780d9eSBradley Grove 			/* Image is present - verify the image */
77026780d9eSBradley Grove 			if (chk_boot((u8 *)fi + ch->image_offset, ch->length)
77126780d9eSBradley Grove 			    != type)
77226780d9eSBradley Grove 				cmperr = true;
77326780d9eSBradley Grove 
77426780d9eSBradley Grove 			break;
77526780d9eSBradley Grove 
77626780d9eSBradley Grove 		case CH_IT_CFG:
77726780d9eSBradley Grove 
77826780d9eSBradley Grove 			/* Test if component image is present */
77926780d9eSBradley Grove 			if (ch->length == 0) {
78026780d9eSBradley Grove 				cmperr = true;
78126780d9eSBradley Grove 				break;
78226780d9eSBradley Grove 			}
78326780d9eSBradley Grove 
78426780d9eSBradley Grove 			/* Image is present - verify the image */
78526780d9eSBradley Grove 			if (!chk_cfg((u8 *)fi + ch->image_offset + ch->length,
78626780d9eSBradley Grove 				     ch->length, NULL))
78726780d9eSBradley Grove 				cmperr = true;
78826780d9eSBradley Grove 
78926780d9eSBradley Grove 			break;
79026780d9eSBradley Grove 
79126780d9eSBradley Grove 		default:
79226780d9eSBradley Grove 
79326780d9eSBradley Grove 			fi->status = FI_STAT_UNKNOWN;
79426780d9eSBradley Grove 			return false;
79526780d9eSBradley Grove 		}
79626780d9eSBradley Grove 
79726780d9eSBradley Grove 		if (cmperr) {
79826780d9eSBradley Grove 			imgerr = true;
79926780d9eSBradley Grove 			ch->status = CH_STAT_INVALID;
80026780d9eSBradley Grove 		} else {
80126780d9eSBradley Grove 			ch->status = CH_STAT_PENDING;
80226780d9eSBradley Grove 			len += ch->length;
80326780d9eSBradley Grove 		}
80426780d9eSBradley Grove 	}
80526780d9eSBradley Grove 
80626780d9eSBradley Grove 	if (imgerr) {
80726780d9eSBradley Grove 		fi->status = FI_STAT_MISSING;
80826780d9eSBradley Grove 		return false;
80926780d9eSBradley Grove 	}
81026780d9eSBradley Grove 
81126780d9eSBradley Grove 	/* Compare fi->length to the sum of ch->length fields */
81226780d9eSBradley Grove 	if (len != fi->length - fc->fi_hdr_len) {
81326780d9eSBradley Grove 		fi->status = FI_STAT_LENGTH;
81426780d9eSBradley Grove 		return false;
81526780d9eSBradley Grove 	}
81626780d9eSBradley Grove 
81726780d9eSBradley Grove 	/* Compute the checksum - it should come out zero */
81826780d9eSBradley Grove 	if (fi->checksum != calc_fi_checksum(fc)) {
81926780d9eSBradley Grove 		fi->status = FI_STAT_CHKSUM;
82026780d9eSBradley Grove 		return false;
82126780d9eSBradley Grove 	}
82226780d9eSBradley Grove 
82326780d9eSBradley Grove 	return true;
82426780d9eSBradley Grove }
82526780d9eSBradley Grove 
82626780d9eSBradley Grove /* Fill in the FS IOCTL response data from a completed request. */
esas2r_complete_fs_ioctl(struct esas2r_adapter * a,struct esas2r_request * rq)82726780d9eSBradley Grove static void esas2r_complete_fs_ioctl(struct esas2r_adapter *a,
82826780d9eSBradley Grove 				     struct esas2r_request *rq)
82926780d9eSBradley Grove {
83026780d9eSBradley Grove 	struct esas2r_ioctl_fs *fs =
83126780d9eSBradley Grove 		(struct esas2r_ioctl_fs *)rq->interrupt_cx;
83226780d9eSBradley Grove 
83326780d9eSBradley Grove 	if (rq->vrq->flash.sub_func == VDA_FLASH_COMMIT)
83426780d9eSBradley Grove 		esas2r_enable_heartbeat(a);
83526780d9eSBradley Grove 
83626780d9eSBradley Grove 	fs->driver_error = rq->req_stat;
83726780d9eSBradley Grove 
83826780d9eSBradley Grove 	if (fs->driver_error == RS_SUCCESS)
83926780d9eSBradley Grove 		fs->status = ATTO_STS_SUCCESS;
84026780d9eSBradley Grove 	else
84126780d9eSBradley Grove 		fs->status = ATTO_STS_FAILED;
84226780d9eSBradley Grove }
84326780d9eSBradley Grove 
84426780d9eSBradley Grove /* Prepare an FS IOCTL request to be sent to the firmware. */
esas2r_process_fs_ioctl(struct esas2r_adapter * a,struct esas2r_ioctl_fs * fs,struct esas2r_request * rq,struct esas2r_sg_context * sgc)84526780d9eSBradley Grove bool esas2r_process_fs_ioctl(struct esas2r_adapter *a,
84626780d9eSBradley Grove 			     struct esas2r_ioctl_fs *fs,
84726780d9eSBradley Grove 			     struct esas2r_request *rq,
84826780d9eSBradley Grove 			     struct esas2r_sg_context *sgc)
84926780d9eSBradley Grove {
85026780d9eSBradley Grove 	u8 cmdcnt = (u8)ARRAY_SIZE(cmd_to_fls_func);
85126780d9eSBradley Grove 	struct esas2r_ioctlfs_command *fsc = &fs->command;
85226780d9eSBradley Grove 	u8 func = 0;
85326780d9eSBradley Grove 	u32 datalen;
85426780d9eSBradley Grove 
85526780d9eSBradley Grove 	fs->status = ATTO_STS_FAILED;
85626780d9eSBradley Grove 	fs->driver_error = RS_PENDING;
85726780d9eSBradley Grove 
85826780d9eSBradley Grove 	if (fs->version > ESAS2R_FS_VER) {
85926780d9eSBradley Grove 		fs->status = ATTO_STS_INV_VERSION;
86026780d9eSBradley Grove 		return false;
86126780d9eSBradley Grove 	}
86226780d9eSBradley Grove 
86364d29bd8SBradley Grove 	if (fsc->command >= cmdcnt) {
86464d29bd8SBradley Grove 		fs->status = ATTO_STS_INV_FUNC;
86564d29bd8SBradley Grove 		return false;
86664d29bd8SBradley Grove 	}
86764d29bd8SBradley Grove 
86826780d9eSBradley Grove 	func = cmd_to_fls_func[fsc->command];
86964d29bd8SBradley Grove 	if (func == 0xFF) {
87026780d9eSBradley Grove 		fs->status = ATTO_STS_INV_FUNC;
87126780d9eSBradley Grove 		return false;
87226780d9eSBradley Grove 	}
87326780d9eSBradley Grove 
87426780d9eSBradley Grove 	if (fsc->command != ESAS2R_FS_CMD_CANCEL) {
87526780d9eSBradley Grove 		if ((a->pcid->device != ATTO_DID_MV_88RC9580
87626780d9eSBradley Grove 		     || fs->adap_type != ESAS2R_FS_AT_ESASRAID2)
87726780d9eSBradley Grove 		    && (a->pcid->device != ATTO_DID_MV_88RC9580TS
87826780d9eSBradley Grove 			|| fs->adap_type != ESAS2R_FS_AT_TSSASRAID2)
87926780d9eSBradley Grove 		    && (a->pcid->device != ATTO_DID_MV_88RC9580TSE
88026780d9eSBradley Grove 			|| fs->adap_type != ESAS2R_FS_AT_TSSASRAID2E)
88126780d9eSBradley Grove 		    && (a->pcid->device != ATTO_DID_MV_88RC9580TL
88226780d9eSBradley Grove 			|| fs->adap_type != ESAS2R_FS_AT_TLSASHBA)) {
88326780d9eSBradley Grove 			fs->status = ATTO_STS_INV_ADAPTER;
88426780d9eSBradley Grove 			return false;
88526780d9eSBradley Grove 		}
88626780d9eSBradley Grove 
88726780d9eSBradley Grove 		if (fs->driver_ver > ESAS2R_FS_DRVR_VER) {
88826780d9eSBradley Grove 			fs->status = ATTO_STS_INV_DRVR_VER;
88926780d9eSBradley Grove 			return false;
89026780d9eSBradley Grove 		}
89126780d9eSBradley Grove 	}
89226780d9eSBradley Grove 
8939588d24eSBradley Grove 	if (test_bit(AF_DEGRADED_MODE, &a->flags)) {
89426780d9eSBradley Grove 		fs->status = ATTO_STS_DEGRADED;
89526780d9eSBradley Grove 		return false;
89626780d9eSBradley Grove 	}
89726780d9eSBradley Grove 
89826780d9eSBradley Grove 	rq->interrupt_cb = esas2r_complete_fs_ioctl;
89926780d9eSBradley Grove 	rq->interrupt_cx = fs;
90026780d9eSBradley Grove 	datalen = le32_to_cpu(fsc->length);
90126780d9eSBradley Grove 	esas2r_build_flash_req(a,
90226780d9eSBradley Grove 			       rq,
90326780d9eSBradley Grove 			       func,
90426780d9eSBradley Grove 			       fsc->checksum,
90526780d9eSBradley Grove 			       le32_to_cpu(fsc->flash_addr),
90626780d9eSBradley Grove 			       datalen);
90726780d9eSBradley Grove 
90826780d9eSBradley Grove 	if (func == VDA_FLASH_WRITE
90926780d9eSBradley Grove 	    || func == VDA_FLASH_READ) {
91026780d9eSBradley Grove 		if (datalen == 0) {
91126780d9eSBradley Grove 			fs->status = ATTO_STS_INV_FUNC;
91226780d9eSBradley Grove 			return false;
91326780d9eSBradley Grove 		}
91426780d9eSBradley Grove 
91526780d9eSBradley Grove 		esas2r_sgc_init(sgc, a, rq, rq->vrq->flash.data.sge);
91626780d9eSBradley Grove 		sgc->length = datalen;
91726780d9eSBradley Grove 
91826780d9eSBradley Grove 		if (!esas2r_build_sg_list(a, rq, sgc)) {
91926780d9eSBradley Grove 			fs->status = ATTO_STS_OUT_OF_RSRC;
92026780d9eSBradley Grove 			return false;
92126780d9eSBradley Grove 		}
92226780d9eSBradley Grove 	}
92326780d9eSBradley Grove 
92426780d9eSBradley Grove 	if (func == VDA_FLASH_COMMIT)
92526780d9eSBradley Grove 		esas2r_disable_heartbeat(a);
92626780d9eSBradley Grove 
92726780d9eSBradley Grove 	esas2r_start_request(a, rq);
92826780d9eSBradley Grove 
92926780d9eSBradley Grove 	return true;
93026780d9eSBradley Grove }
93126780d9eSBradley Grove 
esas2r_flash_access(struct esas2r_adapter * a,u32 function)93226780d9eSBradley Grove static bool esas2r_flash_access(struct esas2r_adapter *a, u32 function)
93326780d9eSBradley Grove {
93426780d9eSBradley Grove 	u32 starttime;
93526780d9eSBradley Grove 	u32 timeout;
93626780d9eSBradley Grove 	u32 intstat;
93726780d9eSBradley Grove 	u32 doorbell;
93826780d9eSBradley Grove 
93926780d9eSBradley Grove 	/* Disable chip interrupts awhile */
94026780d9eSBradley Grove 	if (function == DRBL_FLASH_REQ)
94126780d9eSBradley Grove 		esas2r_disable_chip_interrupts(a);
94226780d9eSBradley Grove 
94326780d9eSBradley Grove 	/* Issue the request to the firmware */
94426780d9eSBradley Grove 	esas2r_write_register_dword(a, MU_DOORBELL_IN, function);
94526780d9eSBradley Grove 
94626780d9eSBradley Grove 	/* Now wait for the firmware to process it */
94726780d9eSBradley Grove 	starttime = jiffies_to_msecs(jiffies);
9489588d24eSBradley Grove 
9499588d24eSBradley Grove 	if (test_bit(AF_CHPRST_PENDING, &a->flags) ||
9509588d24eSBradley Grove 	    test_bit(AF_DISC_PENDING, &a->flags))
9519588d24eSBradley Grove 		timeout = 40000;
9529588d24eSBradley Grove 	else
9539588d24eSBradley Grove 		timeout = 5000;
95426780d9eSBradley Grove 
95526780d9eSBradley Grove 	while (true) {
95626780d9eSBradley Grove 		intstat = esas2r_read_register_dword(a, MU_INT_STATUS_OUT);
95726780d9eSBradley Grove 
95826780d9eSBradley Grove 		if (intstat & MU_INTSTAT_DRBL) {
95926780d9eSBradley Grove 			/* Got a doorbell interrupt.  Check for the function */
96026780d9eSBradley Grove 			doorbell =
96126780d9eSBradley Grove 				esas2r_read_register_dword(a, MU_DOORBELL_OUT);
96226780d9eSBradley Grove 			esas2r_write_register_dword(a, MU_DOORBELL_OUT,
96326780d9eSBradley Grove 						    doorbell);
96426780d9eSBradley Grove 			if (doorbell & function)
96526780d9eSBradley Grove 				break;
96626780d9eSBradley Grove 		}
96726780d9eSBradley Grove 
96826780d9eSBradley Grove 		schedule_timeout_interruptible(msecs_to_jiffies(100));
96926780d9eSBradley Grove 
97026780d9eSBradley Grove 		if ((jiffies_to_msecs(jiffies) - starttime) > timeout) {
97126780d9eSBradley Grove 			/*
97226780d9eSBradley Grove 			 * Iimeout.  If we were requesting flash access,
97326780d9eSBradley Grove 			 * indicate we are done so the firmware knows we gave
97426780d9eSBradley Grove 			 * up.  If this was a REQ, we also need to re-enable
97526780d9eSBradley Grove 			 * chip interrupts.
97626780d9eSBradley Grove 			 */
97726780d9eSBradley Grove 			if (function == DRBL_FLASH_REQ) {
97826780d9eSBradley Grove 				esas2r_hdebug("flash access timeout");
97926780d9eSBradley Grove 				esas2r_write_register_dword(a, MU_DOORBELL_IN,
98026780d9eSBradley Grove 							    DRBL_FLASH_DONE);
98126780d9eSBradley Grove 				esas2r_enable_chip_interrupts(a);
98226780d9eSBradley Grove 			} else {
98326780d9eSBradley Grove 				esas2r_hdebug("flash release timeout");
98426780d9eSBradley Grove 			}
98526780d9eSBradley Grove 
98626780d9eSBradley Grove 			return false;
98726780d9eSBradley Grove 		}
98826780d9eSBradley Grove 	}
98926780d9eSBradley Grove 
99026780d9eSBradley Grove 	/* if we're done, re-enable chip interrupts */
99126780d9eSBradley Grove 	if (function == DRBL_FLASH_DONE)
99226780d9eSBradley Grove 		esas2r_enable_chip_interrupts(a);
99326780d9eSBradley Grove 
99426780d9eSBradley Grove 	return true;
99526780d9eSBradley Grove }
99626780d9eSBradley Grove 
99726780d9eSBradley Grove #define WINDOW_SIZE ((signed int)MW_DATA_WINDOW_SIZE)
99826780d9eSBradley Grove 
esas2r_read_flash_block(struct esas2r_adapter * a,void * to,u32 from,u32 size)99926780d9eSBradley Grove bool esas2r_read_flash_block(struct esas2r_adapter *a,
100026780d9eSBradley Grove 			     void *to,
100126780d9eSBradley Grove 			     u32 from,
100226780d9eSBradley Grove 			     u32 size)
100326780d9eSBradley Grove {
100426780d9eSBradley Grove 	u8 *end = (u8 *)to;
100526780d9eSBradley Grove 
100626780d9eSBradley Grove 	/* Try to acquire access to the flash */
100726780d9eSBradley Grove 	if (!esas2r_flash_access(a, DRBL_FLASH_REQ))
100826780d9eSBradley Grove 		return false;
100926780d9eSBradley Grove 
101026780d9eSBradley Grove 	while (size) {
101126780d9eSBradley Grove 		u32 len;
101226780d9eSBradley Grove 		u32 offset;
101326780d9eSBradley Grove 		u32 iatvr;
101426780d9eSBradley Grove 
10159588d24eSBradley Grove 		if (test_bit(AF2_SERIAL_FLASH, &a->flags2))
101626780d9eSBradley Grove 			iatvr = MW_DATA_ADDR_SER_FLASH + (from & -WINDOW_SIZE);
101726780d9eSBradley Grove 		else
101826780d9eSBradley Grove 			iatvr = MW_DATA_ADDR_PAR_FLASH + (from & -WINDOW_SIZE);
101926780d9eSBradley Grove 
102026780d9eSBradley Grove 		esas2r_map_data_window(a, iatvr);
102126780d9eSBradley Grove 		offset = from & (WINDOW_SIZE - 1);
102226780d9eSBradley Grove 		len = size;
102326780d9eSBradley Grove 
102426780d9eSBradley Grove 		if (len > WINDOW_SIZE - offset)
102526780d9eSBradley Grove 			len = WINDOW_SIZE - offset;
102626780d9eSBradley Grove 
102726780d9eSBradley Grove 		from += len;
102826780d9eSBradley Grove 		size -= len;
102926780d9eSBradley Grove 
103026780d9eSBradley Grove 		while (len--) {
103126780d9eSBradley Grove 			*end++ = esas2r_read_data_byte(a, offset);
103226780d9eSBradley Grove 			offset++;
103326780d9eSBradley Grove 		}
103426780d9eSBradley Grove 	}
103526780d9eSBradley Grove 
103626780d9eSBradley Grove 	/* Release flash access */
103726780d9eSBradley Grove 	esas2r_flash_access(a, DRBL_FLASH_DONE);
103826780d9eSBradley Grove 	return true;
103926780d9eSBradley Grove }
104026780d9eSBradley Grove 
esas2r_read_flash_rev(struct esas2r_adapter * a)104126780d9eSBradley Grove bool esas2r_read_flash_rev(struct esas2r_adapter *a)
104226780d9eSBradley Grove {
104326780d9eSBradley Grove 	u8 bytes[256];
104426780d9eSBradley Grove 	u16 *pw;
104526780d9eSBradley Grove 	u16 *pwstart;
104626780d9eSBradley Grove 	u16 type;
104726780d9eSBradley Grove 	u16 size;
104826780d9eSBradley Grove 	u32 sz;
104926780d9eSBradley Grove 
105026780d9eSBradley Grove 	sz = sizeof(bytes);
105126780d9eSBradley Grove 	pw = (u16 *)(bytes + sz);
105226780d9eSBradley Grove 	pwstart = (u16 *)bytes + 2;
105326780d9eSBradley Grove 
105426780d9eSBradley Grove 	if (!esas2r_read_flash_block(a, bytes, FLS_OFFSET_CPYR - sz, sz))
105526780d9eSBradley Grove 		goto invalid_rev;
105626780d9eSBradley Grove 
105726780d9eSBradley Grove 	while (pw >= pwstart) {
105826780d9eSBradley Grove 		pw--;
105926780d9eSBradley Grove 		type = le16_to_cpu(*pw);
106026780d9eSBradley Grove 		pw--;
106126780d9eSBradley Grove 		size = le16_to_cpu(*pw);
106226780d9eSBradley Grove 		pw -= size / 2;
106326780d9eSBradley Grove 
106426780d9eSBradley Grove 		if (type == FBT_CPYR
106526780d9eSBradley Grove 		    || type == FBT_SETUP
106626780d9eSBradley Grove 		    || pw < pwstart)
106726780d9eSBradley Grove 			continue;
106826780d9eSBradley Grove 
106926780d9eSBradley Grove 		if (type == FBT_FLASH_VER)
107026780d9eSBradley Grove 			a->flash_ver = le32_to_cpu(*(u32 *)pw);
107126780d9eSBradley Grove 
107226780d9eSBradley Grove 		break;
107326780d9eSBradley Grove 	}
107426780d9eSBradley Grove 
107526780d9eSBradley Grove invalid_rev:
107626780d9eSBradley Grove 	return esas2r_print_flash_rev(a);
107726780d9eSBradley Grove }
107826780d9eSBradley Grove 
esas2r_print_flash_rev(struct esas2r_adapter * a)107926780d9eSBradley Grove bool esas2r_print_flash_rev(struct esas2r_adapter *a)
108026780d9eSBradley Grove {
108126780d9eSBradley Grove 	u16 year = LOWORD(a->flash_ver);
108226780d9eSBradley Grove 	u8 day = LOBYTE(HIWORD(a->flash_ver));
108326780d9eSBradley Grove 	u8 month = HIBYTE(HIWORD(a->flash_ver));
108426780d9eSBradley Grove 
108526780d9eSBradley Grove 	if (day == 0
108626780d9eSBradley Grove 	    || month == 0
108726780d9eSBradley Grove 	    || day > 31
108826780d9eSBradley Grove 	    || month > 12
108926780d9eSBradley Grove 	    || year < 2006
109026780d9eSBradley Grove 	    || year > 9999) {
109126780d9eSBradley Grove 		strcpy(a->flash_rev, "not found");
109226780d9eSBradley Grove 		a->flash_ver = 0;
109326780d9eSBradley Grove 		return false;
109426780d9eSBradley Grove 	}
109526780d9eSBradley Grove 
109626780d9eSBradley Grove 	sprintf(a->flash_rev, "%02d/%02d/%04d", month, day, year);
109726780d9eSBradley Grove 	esas2r_hdebug("flash version: %s", a->flash_rev);
109826780d9eSBradley Grove 	return true;
109926780d9eSBradley Grove }
110026780d9eSBradley Grove 
110126780d9eSBradley Grove /*
110226780d9eSBradley Grove  * Find the type of boot image type that is currently in the flash.
110326780d9eSBradley Grove  * The chip only has a 64 KB PCI-e expansion ROM
110426780d9eSBradley Grove  * size so only one image can be flashed at a time.
110526780d9eSBradley Grove  */
esas2r_read_image_type(struct esas2r_adapter * a)110626780d9eSBradley Grove bool esas2r_read_image_type(struct esas2r_adapter *a)
110726780d9eSBradley Grove {
110826780d9eSBradley Grove 	u8 bytes[256];
110926780d9eSBradley Grove 	struct esas2r_boot_image *bi;
111026780d9eSBradley Grove 	struct esas2r_boot_header *bh;
111126780d9eSBradley Grove 	u32 sz;
111226780d9eSBradley Grove 	u32 len;
111326780d9eSBradley Grove 	u32 offset;
111426780d9eSBradley Grove 
111526780d9eSBradley Grove 	/* Start at the base of the boot images and look for a valid image */
111626780d9eSBradley Grove 	sz = sizeof(bytes);
111726780d9eSBradley Grove 	len = FLS_LENGTH_BOOT;
111826780d9eSBradley Grove 	offset = 0;
111926780d9eSBradley Grove 
112026780d9eSBradley Grove 	while (true) {
112126780d9eSBradley Grove 		if (!esas2r_read_flash_block(a, bytes, FLS_OFFSET_BOOT +
112226780d9eSBradley Grove 					     offset,
112326780d9eSBradley Grove 					     sz))
112426780d9eSBradley Grove 			goto invalid_rev;
112526780d9eSBradley Grove 
112626780d9eSBradley Grove 		bi = (struct esas2r_boot_image *)bytes;
112726780d9eSBradley Grove 		bh = (struct esas2r_boot_header *)((u8 *)bi +
112826780d9eSBradley Grove 						   le16_to_cpu(
112926780d9eSBradley Grove 							   bi->header_offset));
113026780d9eSBradley Grove 		if (bi->signature != cpu_to_le16(0xAA55))
113126780d9eSBradley Grove 			goto invalid_rev;
113226780d9eSBradley Grove 
113326780d9eSBradley Grove 		if (bh->code_type == CODE_TYPE_PC) {
113426780d9eSBradley Grove 			strcpy(a->image_type, "BIOS");
113526780d9eSBradley Grove 
113626780d9eSBradley Grove 			return true;
113726780d9eSBradley Grove 		} else if (bh->code_type == CODE_TYPE_EFI) {
113826780d9eSBradley Grove 			struct esas2r_efi_image *ei;
113926780d9eSBradley Grove 
114026780d9eSBradley Grove 			/*
114126780d9eSBradley Grove 			 * So we have an EFI image.  There are several types
114226780d9eSBradley Grove 			 * so see which architecture we have.
114326780d9eSBradley Grove 			 */
114426780d9eSBradley Grove 			ei = (struct esas2r_efi_image *)bytes;
114526780d9eSBradley Grove 
114626780d9eSBradley Grove 			switch (le16_to_cpu(ei->machine_type)) {
114726780d9eSBradley Grove 			case EFI_MACHINE_IA32:
114826780d9eSBradley Grove 				strcpy(a->image_type, "EFI 32-bit");
114926780d9eSBradley Grove 				return true;
115026780d9eSBradley Grove 
115126780d9eSBradley Grove 			case EFI_MACHINE_IA64:
115226780d9eSBradley Grove 				strcpy(a->image_type, "EFI itanium");
115326780d9eSBradley Grove 				return true;
115426780d9eSBradley Grove 
115526780d9eSBradley Grove 			case EFI_MACHINE_X64:
115626780d9eSBradley Grove 				strcpy(a->image_type, "EFI 64-bit");
115726780d9eSBradley Grove 				return true;
115826780d9eSBradley Grove 
115926780d9eSBradley Grove 			case EFI_MACHINE_EBC:
116026780d9eSBradley Grove 				strcpy(a->image_type, "EFI EBC");
116126780d9eSBradley Grove 				return true;
116226780d9eSBradley Grove 
116326780d9eSBradley Grove 			default:
116426780d9eSBradley Grove 				goto invalid_rev;
116526780d9eSBradley Grove 			}
116626780d9eSBradley Grove 		} else {
116726780d9eSBradley Grove 			u32 thislen;
116826780d9eSBradley Grove 
116926780d9eSBradley Grove 			/* jump to the next image */
117026780d9eSBradley Grove 			thislen = (u32)le16_to_cpu(bh->image_length) * 512;
117126780d9eSBradley Grove 			if (thislen == 0
117226780d9eSBradley Grove 			    || thislen + offset > len
117326780d9eSBradley Grove 			    || bh->indicator == INDICATOR_LAST)
117426780d9eSBradley Grove 				break;
117526780d9eSBradley Grove 
117626780d9eSBradley Grove 			offset += thislen;
117726780d9eSBradley Grove 		}
117826780d9eSBradley Grove 	}
117926780d9eSBradley Grove 
118026780d9eSBradley Grove invalid_rev:
118126780d9eSBradley Grove 	strcpy(a->image_type, "no boot images");
118226780d9eSBradley Grove 	return false;
118326780d9eSBradley Grove }
118426780d9eSBradley Grove 
118526780d9eSBradley Grove /*
118626780d9eSBradley Grove  *  Read and validate current NVRAM parameters by accessing
118726780d9eSBradley Grove  *  physical NVRAM directly.  if currently stored parameters are
118826780d9eSBradley Grove  *  invalid, use the defaults.
118926780d9eSBradley Grove  */
esas2r_nvram_read_direct(struct esas2r_adapter * a)119026780d9eSBradley Grove bool esas2r_nvram_read_direct(struct esas2r_adapter *a)
119126780d9eSBradley Grove {
119226780d9eSBradley Grove 	bool result;
119326780d9eSBradley Grove 
119426780d9eSBradley Grove 	if (down_interruptible(&a->nvram_semaphore))
119526780d9eSBradley Grove 		return false;
119626780d9eSBradley Grove 
119726780d9eSBradley Grove 	if (!esas2r_read_flash_block(a, a->nvram, FLS_OFFSET_NVR,
119826780d9eSBradley Grove 				     sizeof(struct esas2r_sas_nvram))) {
119926780d9eSBradley Grove 		esas2r_hdebug("NVRAM read failed, using defaults");
1200906ca635SDan Carpenter 		up(&a->nvram_semaphore);
120126780d9eSBradley Grove 		return false;
120226780d9eSBradley Grove 	}
120326780d9eSBradley Grove 
120426780d9eSBradley Grove 	result = esas2r_nvram_validate(a);
120526780d9eSBradley Grove 
120626780d9eSBradley Grove 	up(&a->nvram_semaphore);
120726780d9eSBradley Grove 
120826780d9eSBradley Grove 	return result;
120926780d9eSBradley Grove }
121026780d9eSBradley Grove 
121126780d9eSBradley Grove /* Interrupt callback to process NVRAM completions. */
esas2r_nvram_callback(struct esas2r_adapter * a,struct esas2r_request * rq)121226780d9eSBradley Grove static void esas2r_nvram_callback(struct esas2r_adapter *a,
121326780d9eSBradley Grove 				  struct esas2r_request *rq)
121426780d9eSBradley Grove {
121526780d9eSBradley Grove 	struct atto_vda_flash_req *vrq = &rq->vrq->flash;
121626780d9eSBradley Grove 
121726780d9eSBradley Grove 	if (rq->req_stat == RS_SUCCESS) {
121826780d9eSBradley Grove 		/* last request was successful.  see what to do now. */
121926780d9eSBradley Grove 
122026780d9eSBradley Grove 		switch (vrq->sub_func) {
122126780d9eSBradley Grove 		case VDA_FLASH_BEGINW:
122226780d9eSBradley Grove 			vrq->sub_func = VDA_FLASH_WRITE;
122326780d9eSBradley Grove 			rq->req_stat = RS_PENDING;
122426780d9eSBradley Grove 			break;
122526780d9eSBradley Grove 
122626780d9eSBradley Grove 		case VDA_FLASH_WRITE:
122726780d9eSBradley Grove 			vrq->sub_func = VDA_FLASH_COMMIT;
122826780d9eSBradley Grove 			rq->req_stat = RS_PENDING;
122926780d9eSBradley Grove 			break;
123026780d9eSBradley Grove 
123126780d9eSBradley Grove 		case VDA_FLASH_READ:
123226780d9eSBradley Grove 			esas2r_nvram_validate(a);
123326780d9eSBradley Grove 			break;
123426780d9eSBradley Grove 
123526780d9eSBradley Grove 		case VDA_FLASH_COMMIT:
123626780d9eSBradley Grove 		default:
123726780d9eSBradley Grove 			break;
123826780d9eSBradley Grove 		}
123926780d9eSBradley Grove 	}
124026780d9eSBradley Grove 
124126780d9eSBradley Grove 	if (rq->req_stat != RS_PENDING) {
124226780d9eSBradley Grove 		/* update the NVRAM state */
124326780d9eSBradley Grove 		if (rq->req_stat == RS_SUCCESS)
12449588d24eSBradley Grove 			set_bit(AF_NVR_VALID, &a->flags);
124526780d9eSBradley Grove 		else
12469588d24eSBradley Grove 			clear_bit(AF_NVR_VALID, &a->flags);
124726780d9eSBradley Grove 
124826780d9eSBradley Grove 		esas2r_enable_heartbeat(a);
124926780d9eSBradley Grove 
125026780d9eSBradley Grove 		up(&a->nvram_semaphore);
125126780d9eSBradley Grove 	}
125226780d9eSBradley Grove }
125326780d9eSBradley Grove 
125426780d9eSBradley Grove /*
125526780d9eSBradley Grove  * Write the contents of nvram to the adapter's physical NVRAM.
125626780d9eSBradley Grove  * The cached copy of the NVRAM is also updated.
125726780d9eSBradley Grove  */
esas2r_nvram_write(struct esas2r_adapter * a,struct esas2r_request * rq,struct esas2r_sas_nvram * nvram)125826780d9eSBradley Grove bool esas2r_nvram_write(struct esas2r_adapter *a, struct esas2r_request *rq,
125926780d9eSBradley Grove 			struct esas2r_sas_nvram *nvram)
126026780d9eSBradley Grove {
126126780d9eSBradley Grove 	struct esas2r_sas_nvram *n = nvram;
126226780d9eSBradley Grove 	u8 sas_address_bytes[8];
126326780d9eSBradley Grove 	u32 *sas_address_dwords = (u32 *)&sas_address_bytes[0];
126426780d9eSBradley Grove 	struct atto_vda_flash_req *vrq = &rq->vrq->flash;
126526780d9eSBradley Grove 
12669588d24eSBradley Grove 	if (test_bit(AF_DEGRADED_MODE, &a->flags))
126726780d9eSBradley Grove 		return false;
126826780d9eSBradley Grove 
126926780d9eSBradley Grove 	if (down_interruptible(&a->nvram_semaphore))
127026780d9eSBradley Grove 		return false;
127126780d9eSBradley Grove 
127226780d9eSBradley Grove 	if (n == NULL)
127326780d9eSBradley Grove 		n = a->nvram;
127426780d9eSBradley Grove 
127526780d9eSBradley Grove 	/* check the validity of the settings */
127626780d9eSBradley Grove 	if (n->version > SASNVR_VERSION) {
127726780d9eSBradley Grove 		up(&a->nvram_semaphore);
127826780d9eSBradley Grove 		return false;
127926780d9eSBradley Grove 	}
128026780d9eSBradley Grove 
128126780d9eSBradley Grove 	memcpy(&sas_address_bytes[0], n->sas_addr, 8);
128226780d9eSBradley Grove 
128326780d9eSBradley Grove 	if (sas_address_bytes[0] != 0x50
128426780d9eSBradley Grove 	    || sas_address_bytes[1] != 0x01
128526780d9eSBradley Grove 	    || sas_address_bytes[2] != 0x08
128626780d9eSBradley Grove 	    || (sas_address_bytes[3] & 0xF0) != 0x60
128726780d9eSBradley Grove 	    || ((sas_address_bytes[3] & 0x0F) | sas_address_dwords[1]) == 0) {
128826780d9eSBradley Grove 		up(&a->nvram_semaphore);
128926780d9eSBradley Grove 		return false;
129026780d9eSBradley Grove 	}
129126780d9eSBradley Grove 
129226780d9eSBradley Grove 	if (n->spin_up_delay > SASNVR_SPINUP_MAX)
129326780d9eSBradley Grove 		n->spin_up_delay = SASNVR_SPINUP_MAX;
129426780d9eSBradley Grove 
129526780d9eSBradley Grove 	n->version = SASNVR_VERSION;
129626780d9eSBradley Grove 	n->checksum = n->checksum - esas2r_nvramcalc_cksum(n);
129726780d9eSBradley Grove 	memcpy(a->nvram, n, sizeof(struct esas2r_sas_nvram));
129826780d9eSBradley Grove 
129926780d9eSBradley Grove 	/* write the NVRAM */
130026780d9eSBradley Grove 	n = a->nvram;
130126780d9eSBradley Grove 	esas2r_disable_heartbeat(a);
130226780d9eSBradley Grove 
130326780d9eSBradley Grove 	esas2r_build_flash_req(a,
130426780d9eSBradley Grove 			       rq,
130526780d9eSBradley Grove 			       VDA_FLASH_BEGINW,
130626780d9eSBradley Grove 			       esas2r_nvramcalc_xor_cksum(n),
130726780d9eSBradley Grove 			       FLS_OFFSET_NVR,
130826780d9eSBradley Grove 			       sizeof(struct esas2r_sas_nvram));
130926780d9eSBradley Grove 
13109588d24eSBradley Grove 	if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) {
131126780d9eSBradley Grove 
131226780d9eSBradley Grove 		vrq->data.sge[0].length =
131326780d9eSBradley Grove 			cpu_to_le32(SGE_LAST |
131426780d9eSBradley Grove 				    sizeof(struct esas2r_sas_nvram));
131526780d9eSBradley Grove 		vrq->data.sge[0].address = cpu_to_le64(
131626780d9eSBradley Grove 			a->uncached_phys + (u64)((u8 *)n - a->uncached));
131726780d9eSBradley Grove 	} else {
131826780d9eSBradley Grove 		vrq->data.prde[0].ctl_len =
131926780d9eSBradley Grove 			cpu_to_le32(sizeof(struct esas2r_sas_nvram));
132026780d9eSBradley Grove 		vrq->data.prde[0].address = cpu_to_le64(
132126780d9eSBradley Grove 			a->uncached_phys
132226780d9eSBradley Grove 			+ (u64)((u8 *)n - a->uncached));
132326780d9eSBradley Grove 	}
132426780d9eSBradley Grove 	rq->interrupt_cb = esas2r_nvram_callback;
132526780d9eSBradley Grove 	esas2r_start_request(a, rq);
132626780d9eSBradley Grove 	return true;
132726780d9eSBradley Grove }
132826780d9eSBradley Grove 
132926780d9eSBradley Grove /* Validate the cached NVRAM.  if the NVRAM is invalid, load the defaults. */
esas2r_nvram_validate(struct esas2r_adapter * a)133026780d9eSBradley Grove bool esas2r_nvram_validate(struct esas2r_adapter *a)
133126780d9eSBradley Grove {
133226780d9eSBradley Grove 	struct esas2r_sas_nvram *n = a->nvram;
133326780d9eSBradley Grove 	bool rslt = false;
133426780d9eSBradley Grove 
133526780d9eSBradley Grove 	if (n->signature[0] != 'E'
133626780d9eSBradley Grove 	    || n->signature[1] != 'S'
133726780d9eSBradley Grove 	    || n->signature[2] != 'A'
133826780d9eSBradley Grove 	    || n->signature[3] != 'S') {
133926780d9eSBradley Grove 		esas2r_hdebug("invalid NVRAM signature");
134026780d9eSBradley Grove 	} else if (esas2r_nvramcalc_cksum(n)) {
134126780d9eSBradley Grove 		esas2r_hdebug("invalid NVRAM checksum");
134226780d9eSBradley Grove 	} else if (n->version > SASNVR_VERSION) {
134326780d9eSBradley Grove 		esas2r_hdebug("invalid NVRAM version");
134426780d9eSBradley Grove 	} else {
13459588d24eSBradley Grove 		set_bit(AF_NVR_VALID, &a->flags);
134626780d9eSBradley Grove 		rslt = true;
134726780d9eSBradley Grove 	}
134826780d9eSBradley Grove 
134926780d9eSBradley Grove 	if (rslt == false) {
135026780d9eSBradley Grove 		esas2r_hdebug("using defaults");
135126780d9eSBradley Grove 		esas2r_nvram_set_defaults(a);
135226780d9eSBradley Grove 	}
135326780d9eSBradley Grove 
135426780d9eSBradley Grove 	return rslt;
135526780d9eSBradley Grove }
135626780d9eSBradley Grove 
135726780d9eSBradley Grove /*
135826780d9eSBradley Grove  * Set the cached NVRAM to defaults.  note that this function sets the default
135926780d9eSBradley Grove  * NVRAM when it has been determined that the physical NVRAM is invalid.
136026780d9eSBradley Grove  * In this case, the SAS address is fabricated.
136126780d9eSBradley Grove  */
esas2r_nvram_set_defaults(struct esas2r_adapter * a)136226780d9eSBradley Grove void esas2r_nvram_set_defaults(struct esas2r_adapter *a)
136326780d9eSBradley Grove {
136426780d9eSBradley Grove 	struct esas2r_sas_nvram *n = a->nvram;
136526780d9eSBradley Grove 	u32 time = jiffies_to_msecs(jiffies);
136626780d9eSBradley Grove 
13679588d24eSBradley Grove 	clear_bit(AF_NVR_VALID, &a->flags);
1368ef11851bSBradley Grove 	*n = default_sas_nvram;
136926780d9eSBradley Grove 	n->sas_addr[3] |= 0x0F;
137026780d9eSBradley Grove 	n->sas_addr[4] = HIBYTE(LOWORD(time));
137126780d9eSBradley Grove 	n->sas_addr[5] = LOBYTE(LOWORD(time));
137226780d9eSBradley Grove 	n->sas_addr[6] = a->pcid->bus->number;
137326780d9eSBradley Grove 	n->sas_addr[7] = a->pcid->devfn;
137426780d9eSBradley Grove }
137526780d9eSBradley Grove 
esas2r_nvram_get_defaults(struct esas2r_adapter * a,struct esas2r_sas_nvram * nvram)137626780d9eSBradley Grove void esas2r_nvram_get_defaults(struct esas2r_adapter *a,
137726780d9eSBradley Grove 			       struct esas2r_sas_nvram *nvram)
137826780d9eSBradley Grove {
137926780d9eSBradley Grove 	u8 sas_addr[8];
138026780d9eSBradley Grove 
138126780d9eSBradley Grove 	/*
138226780d9eSBradley Grove 	 * in case we are copying the defaults into the adapter, copy the SAS
138326780d9eSBradley Grove 	 * address out first.
138426780d9eSBradley Grove 	 */
138526780d9eSBradley Grove 	memcpy(&sas_addr[0], a->nvram->sas_addr, 8);
1386ef11851bSBradley Grove 	*nvram = default_sas_nvram;
138726780d9eSBradley Grove 	memcpy(&nvram->sas_addr[0], &sas_addr[0], 8);
138826780d9eSBradley Grove }
138926780d9eSBradley Grove 
esas2r_fm_api(struct esas2r_adapter * a,struct esas2r_flash_img * fi,struct esas2r_request * rq,struct esas2r_sg_context * sgc)139026780d9eSBradley Grove bool esas2r_fm_api(struct esas2r_adapter *a, struct esas2r_flash_img *fi,
139126780d9eSBradley Grove 		   struct esas2r_request *rq, struct esas2r_sg_context *sgc)
139226780d9eSBradley Grove {
139326780d9eSBradley Grove 	struct esas2r_flash_context *fc = &a->flash_context;
139426780d9eSBradley Grove 	u8 j;
139526780d9eSBradley Grove 	struct esas2r_component_header *ch;
139626780d9eSBradley Grove 
13979588d24eSBradley Grove 	if (test_and_set_bit(AF_FLASH_LOCK, &a->flags)) {
139826780d9eSBradley Grove 		/* flag was already set */
139926780d9eSBradley Grove 		fi->status = FI_STAT_BUSY;
140026780d9eSBradley Grove 		return false;
140126780d9eSBradley Grove 	}
140226780d9eSBradley Grove 
140326780d9eSBradley Grove 	memcpy(&fc->sgc, sgc, sizeof(struct esas2r_sg_context));
140426780d9eSBradley Grove 	sgc = &fc->sgc;
140526780d9eSBradley Grove 	fc->fi = fi;
140626780d9eSBradley Grove 	fc->sgc_offset = sgc->cur_offset;
140726780d9eSBradley Grove 	rq->req_stat = RS_SUCCESS;
140826780d9eSBradley Grove 	rq->interrupt_cx = fc;
140926780d9eSBradley Grove 
141026780d9eSBradley Grove 	switch (fi->fi_version) {
141126780d9eSBradley Grove 	case FI_VERSION_1:
141226780d9eSBradley Grove 		fc->scratch = ((struct esas2r_flash_img *)fi)->scratch_buf;
141326780d9eSBradley Grove 		fc->num_comps = FI_NUM_COMPS_V1;
141426780d9eSBradley Grove 		fc->fi_hdr_len = sizeof(struct esas2r_flash_img);
141526780d9eSBradley Grove 		break;
141626780d9eSBradley Grove 
141726780d9eSBradley Grove 	default:
141826780d9eSBradley Grove 		return complete_fmapi_req(a, rq, FI_STAT_IMG_VER);
141926780d9eSBradley Grove 	}
142026780d9eSBradley Grove 
14219588d24eSBradley Grove 	if (test_bit(AF_DEGRADED_MODE, &a->flags))
142226780d9eSBradley Grove 		return complete_fmapi_req(a, rq, FI_STAT_DEGRADED);
142326780d9eSBradley Grove 
142426780d9eSBradley Grove 	switch (fi->action) {
142526780d9eSBradley Grove 	case FI_ACT_DOWN: /* Download the components */
142626780d9eSBradley Grove 		/* Verify the format of the flash image */
142726780d9eSBradley Grove 		if (!verify_fi(a, fc))
142826780d9eSBradley Grove 			return complete_fmapi_req(a, rq, fi->status);
142926780d9eSBradley Grove 
143026780d9eSBradley Grove 		/* Adjust the BIOS fields that are dependent on the HBA */
143126780d9eSBradley Grove 		ch = &fi->cmp_hdr[CH_IT_BIOS];
143226780d9eSBradley Grove 
143326780d9eSBradley Grove 		if (ch->length)
143426780d9eSBradley Grove 			fix_bios(a, fi);
143526780d9eSBradley Grove 
143626780d9eSBradley Grove 		/* Adjust the EFI fields that are dependent on the HBA */
143726780d9eSBradley Grove 		ch = &fi->cmp_hdr[CH_IT_EFI];
143826780d9eSBradley Grove 
143926780d9eSBradley Grove 		if (ch->length)
144026780d9eSBradley Grove 			fix_efi(a, fi);
144126780d9eSBradley Grove 
144226780d9eSBradley Grove 		/*
144326780d9eSBradley Grove 		 * Since the image was just modified, compute the checksum on
144426780d9eSBradley Grove 		 * the modified image.  First update the CRC for the composite
144526780d9eSBradley Grove 		 * expansion ROM image.
144626780d9eSBradley Grove 		 */
144726780d9eSBradley Grove 		fi->checksum = calc_fi_checksum(fc);
144826780d9eSBradley Grove 
144926780d9eSBradley Grove 		/* Disable the heartbeat */
145026780d9eSBradley Grove 		esas2r_disable_heartbeat(a);
145126780d9eSBradley Grove 
145226780d9eSBradley Grove 		/* Now start up the download sequence */
145326780d9eSBradley Grove 		fc->task = FMTSK_ERASE_BOOT;
145426780d9eSBradley Grove 		fc->func = VDA_FLASH_BEGINW;
145526780d9eSBradley Grove 		fc->comp_typ = CH_IT_CFG;
145626780d9eSBradley Grove 		fc->flsh_addr = FLS_OFFSET_BOOT;
145726780d9eSBradley Grove 		fc->sgc.length = FLS_LENGTH_BOOT;
145826780d9eSBradley Grove 		fc->sgc.cur_offset = NULL;
145926780d9eSBradley Grove 
146026780d9eSBradley Grove 		/* Setup the callback address */
146126780d9eSBradley Grove 		fc->interrupt_cb = fw_download_proc;
146226780d9eSBradley Grove 		break;
146326780d9eSBradley Grove 
146426780d9eSBradley Grove 	case FI_ACT_UPSZ: /* Get upload sizes */
146526780d9eSBradley Grove 		fi->adap_typ = get_fi_adap_type(a);
146626780d9eSBradley Grove 		fi->flags = 0;
146726780d9eSBradley Grove 		fi->num_comps = fc->num_comps;
146826780d9eSBradley Grove 		fi->length = fc->fi_hdr_len;
146926780d9eSBradley Grove 
147026780d9eSBradley Grove 		/* Report the type of boot image in the rel_version string */
147126780d9eSBradley Grove 		memcpy(fi->rel_version, a->image_type,
147226780d9eSBradley Grove 		       sizeof(fi->rel_version));
147326780d9eSBradley Grove 
147426780d9eSBradley Grove 		/* Build the component headers */
147526780d9eSBradley Grove 		for (j = 0, ch = fi->cmp_hdr;
147626780d9eSBradley Grove 		     j < fi->num_comps;
147726780d9eSBradley Grove 		     j++, ch++) {
147826780d9eSBradley Grove 			ch->img_type = j;
147926780d9eSBradley Grove 			ch->status = CH_STAT_PENDING;
148026780d9eSBradley Grove 			ch->length = 0;
148126780d9eSBradley Grove 			ch->version = 0xffffffff;
148226780d9eSBradley Grove 			ch->image_offset = 0;
148326780d9eSBradley Grove 			ch->pad[0] = 0;
148426780d9eSBradley Grove 			ch->pad[1] = 0;
148526780d9eSBradley Grove 		}
148626780d9eSBradley Grove 
148726780d9eSBradley Grove 		if (a->flash_ver != 0) {
148826780d9eSBradley Grove 			fi->cmp_hdr[CH_IT_BIOS].version =
148926780d9eSBradley Grove 				fi->cmp_hdr[CH_IT_MAC].version =
149026780d9eSBradley Grove 					fi->cmp_hdr[CH_IT_EFI].version =
149126780d9eSBradley Grove 						fi->cmp_hdr[CH_IT_CFG].version
149226780d9eSBradley Grove 							= a->flash_ver;
149326780d9eSBradley Grove 
149426780d9eSBradley Grove 			fi->cmp_hdr[CH_IT_BIOS].status =
149526780d9eSBradley Grove 				fi->cmp_hdr[CH_IT_MAC].status =
149626780d9eSBradley Grove 					fi->cmp_hdr[CH_IT_EFI].status =
149726780d9eSBradley Grove 						fi->cmp_hdr[CH_IT_CFG].status =
149826780d9eSBradley Grove 							CH_STAT_SUCCESS;
149926780d9eSBradley Grove 
150026780d9eSBradley Grove 			return complete_fmapi_req(a, rq, FI_STAT_SUCCESS);
150126780d9eSBradley Grove 		}
150226780d9eSBradley Grove 
1503df561f66SGustavo A. R. Silva 		fallthrough;
150426780d9eSBradley Grove 
150526780d9eSBradley Grove 	case FI_ACT_UP: /* Upload the components */
150626780d9eSBradley Grove 	default:
150726780d9eSBradley Grove 		return complete_fmapi_req(a, rq, FI_STAT_INVALID);
150826780d9eSBradley Grove 	}
150926780d9eSBradley Grove 
151026780d9eSBradley Grove 	/*
151126780d9eSBradley Grove 	 * If we make it here, fc has been setup to do the first task.  Call
151226780d9eSBradley Grove 	 * load_image to format the request, start it, and get out.  The
151326780d9eSBradley Grove 	 * interrupt code will call the callback when the first message is
151426780d9eSBradley Grove 	 * complete.
151526780d9eSBradley Grove 	 */
151626780d9eSBradley Grove 	if (!load_image(a, rq))
151726780d9eSBradley Grove 		return complete_fmapi_req(a, rq, FI_STAT_FAILED);
151826780d9eSBradley Grove 
151926780d9eSBradley Grove 	esas2r_start_request(a, rq);
152026780d9eSBradley Grove 
152126780d9eSBradley Grove 	return true;
152226780d9eSBradley Grove }
1523