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