xref: /onnv-gate/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_nvram.c (revision 12698:119eaf3c5982)
110696SDavid.Hollister@Sun.COM /*
210696SDavid.Hollister@Sun.COM  * CDDL HEADER START
310696SDavid.Hollister@Sun.COM  *
410696SDavid.Hollister@Sun.COM  * The contents of this file are subject to the terms of the
510696SDavid.Hollister@Sun.COM  * Common Development and Distribution License (the "License").
610696SDavid.Hollister@Sun.COM  * You may not use this file except in compliance with the License.
710696SDavid.Hollister@Sun.COM  *
810696SDavid.Hollister@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910696SDavid.Hollister@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1010696SDavid.Hollister@Sun.COM  * See the License for the specific language governing permissions
1110696SDavid.Hollister@Sun.COM  * and limitations under the License.
1210696SDavid.Hollister@Sun.COM  *
1310696SDavid.Hollister@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1410696SDavid.Hollister@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510696SDavid.Hollister@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1610696SDavid.Hollister@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1710696SDavid.Hollister@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1810696SDavid.Hollister@Sun.COM  *
1910696SDavid.Hollister@Sun.COM  * CDDL HEADER END
2012060SDavid.Hollister@Sun.COM  */
2112060SDavid.Hollister@Sun.COM /*
2212060SDavid.Hollister@Sun.COM  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2310696SDavid.Hollister@Sun.COM  */
2410696SDavid.Hollister@Sun.COM 
2510696SDavid.Hollister@Sun.COM /*
2610696SDavid.Hollister@Sun.COM  * This file contains various support routines.
2710696SDavid.Hollister@Sun.COM  */
2810696SDavid.Hollister@Sun.COM 
2910696SDavid.Hollister@Sun.COM #include <sys/scsi/adapters/pmcs/pmcs.h>
3010696SDavid.Hollister@Sun.COM 
3110696SDavid.Hollister@Sun.COM /*
3210696SDavid.Hollister@Sun.COM  * SAS Topology Configuration
3310696SDavid.Hollister@Sun.COM  */
3410696SDavid.Hollister@Sun.COM static int pmcs_flash_chunk(pmcs_hw_t *, uint8_t *);
3510696SDavid.Hollister@Sun.COM 
3610696SDavid.Hollister@Sun.COM /*
3710696SDavid.Hollister@Sun.COM  * Check current firmware version for correctness
3810696SDavid.Hollister@Sun.COM  * and try to flash the correct firmware if what is
3910696SDavid.Hollister@Sun.COM  * running isn't correct.
4010696SDavid.Hollister@Sun.COM  *
4110696SDavid.Hollister@Sun.COM  * Must be called after setup and MPI setup and
4210696SDavid.Hollister@Sun.COM  * interrupts are enabled.
4310696SDavid.Hollister@Sun.COM  */
4410696SDavid.Hollister@Sun.COM 
4510696SDavid.Hollister@Sun.COM int
pmcs_firmware_update(pmcs_hw_t * pwp)4610696SDavid.Hollister@Sun.COM pmcs_firmware_update(pmcs_hw_t *pwp)
4710696SDavid.Hollister@Sun.COM {
4810696SDavid.Hollister@Sun.COM 	ddi_modhandle_t modhp;
4911980SDavid.Hollister@Sun.COM 	char buf[64], *bufp;
5010696SDavid.Hollister@Sun.COM 	int errno;
5110696SDavid.Hollister@Sun.COM 	uint8_t *cstart, *cend;		/* Firmware image file */
5210696SDavid.Hollister@Sun.COM 	uint8_t *istart, *iend; 	/* ila */
5310696SDavid.Hollister@Sun.COM 	uint8_t *sstart, *send;		/* SPCBoot */
5410696SDavid.Hollister@Sun.COM 	uint32_t *fwvp;
5510696SDavid.Hollister@Sun.COM 	int defret = 0;
5612119SJesse.Butler@Sun.COM 	int first_pass = 1;
5711980SDavid.Hollister@Sun.COM 	long fw_version, ila_version;
5811980SDavid.Hollister@Sun.COM 	uint8_t *fw_verp, *ila_verp;
5910696SDavid.Hollister@Sun.COM 
6010696SDavid.Hollister@Sun.COM 	/*
6110696SDavid.Hollister@Sun.COM 	 * If updating is disabled, we're done.
6210696SDavid.Hollister@Sun.COM 	 */
6310696SDavid.Hollister@Sun.COM 	if (pwp->fw_disable_update) {
6411048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6510696SDavid.Hollister@Sun.COM 		    "Firmware update disabled by conf file");
6610696SDavid.Hollister@Sun.COM 		return (0);
6710696SDavid.Hollister@Sun.COM 	}
6810696SDavid.Hollister@Sun.COM 
6910696SDavid.Hollister@Sun.COM 	/*
7010696SDavid.Hollister@Sun.COM 	 * If we're already running the right firmware, we're done.
7110696SDavid.Hollister@Sun.COM 	 */
7210696SDavid.Hollister@Sun.COM 	if (pwp->fw == PMCS_FIRMWARE_VERSION) {
7310696SDavid.Hollister@Sun.COM 		if (pwp->fw_force_update == 0) {
7410696SDavid.Hollister@Sun.COM 			return (0);
7510696SDavid.Hollister@Sun.COM 		}
7610696SDavid.Hollister@Sun.COM 
7711048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
7810696SDavid.Hollister@Sun.COM 		    "Firmware version matches, but still forcing update");
7910696SDavid.Hollister@Sun.COM 	}
8010696SDavid.Hollister@Sun.COM 
8110696SDavid.Hollister@Sun.COM 	modhp = ddi_modopen(PMCS_FIRMWARE_FILENAME, KRTLD_MODE_FIRST, &errno);
8210696SDavid.Hollister@Sun.COM 	if (errno) {
8311048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8410696SDavid.Hollister@Sun.COM 		    "%s: Firmware module not available; will not upgrade",
8510696SDavid.Hollister@Sun.COM 		    __func__);
8610696SDavid.Hollister@Sun.COM 		return (defret);
8710696SDavid.Hollister@Sun.COM 	}
8810696SDavid.Hollister@Sun.COM 
8910696SDavid.Hollister@Sun.COM 	fwvp = ddi_modsym(modhp, PMCS_FIRMWARE_VERSION_NAME, &errno);
9010696SDavid.Hollister@Sun.COM 	if (errno) {
9111048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
9211048SDavid.Hollister@Sun.COM 		    "%s: unable to find symbol '%s'",
9310696SDavid.Hollister@Sun.COM 		    __func__, PMCS_FIRMWARE_VERSION_NAME);
9410696SDavid.Hollister@Sun.COM 		(void) ddi_modclose(modhp);
9510696SDavid.Hollister@Sun.COM 		return (defret);
9610696SDavid.Hollister@Sun.COM 	}
9710696SDavid.Hollister@Sun.COM 
9810696SDavid.Hollister@Sun.COM 	/*
9910696SDavid.Hollister@Sun.COM 	 * If the firmware version from the module isn't what we expect,
10010696SDavid.Hollister@Sun.COM 	 * and force updating is disabled, return the default (for this
10110696SDavid.Hollister@Sun.COM 	 * mode of operation) value.
10210696SDavid.Hollister@Sun.COM 	 */
10310696SDavid.Hollister@Sun.COM 	if (*fwvp != PMCS_FIRMWARE_VERSION) {
10410696SDavid.Hollister@Sun.COM 		if (pwp->fw_force_update == 0) {
10511048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
10610696SDavid.Hollister@Sun.COM 			    "%s: firmware module version wrong (0x%x)",
10710696SDavid.Hollister@Sun.COM 			    __func__, *fwvp);
10810696SDavid.Hollister@Sun.COM 			(void) ddi_modclose(modhp);
10910696SDavid.Hollister@Sun.COM 			return (defret);
11010696SDavid.Hollister@Sun.COM 		}
11111048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
11210696SDavid.Hollister@Sun.COM 		    "%s: firmware module version wrong (0x%x) - update forced",
11310696SDavid.Hollister@Sun.COM 		    __func__, *fwvp);
11410696SDavid.Hollister@Sun.COM 	}
11510696SDavid.Hollister@Sun.COM 
11610696SDavid.Hollister@Sun.COM 	(void) snprintf(buf, sizeof (buf),
11710696SDavid.Hollister@Sun.COM 	    PMCS_FIRMWARE_CODE_NAME PMCS_FIRMWARE_START_SUF);
11810696SDavid.Hollister@Sun.COM 	cstart = ddi_modsym(modhp, buf, &errno);
11910696SDavid.Hollister@Sun.COM 	if (errno) {
12011048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
12111048SDavid.Hollister@Sun.COM 		    "%s: unable to find symbol '%s'", __func__, buf);
12210696SDavid.Hollister@Sun.COM 		(void) ddi_modclose(modhp);
12310696SDavid.Hollister@Sun.COM 		return (defret);
12410696SDavid.Hollister@Sun.COM 	}
12510696SDavid.Hollister@Sun.COM 
12610696SDavid.Hollister@Sun.COM 	(void) snprintf(buf, sizeof (buf),
12710696SDavid.Hollister@Sun.COM 	    PMCS_FIRMWARE_CODE_NAME PMCS_FIRMWARE_END_SUF);
12810696SDavid.Hollister@Sun.COM 	cend = ddi_modsym(modhp, buf, &errno);
12910696SDavid.Hollister@Sun.COM 	if (errno) {
13011048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
13111048SDavid.Hollister@Sun.COM 		    "%s: unable to find symbol '%s'", __func__, buf);
13210696SDavid.Hollister@Sun.COM 		(void) ddi_modclose(modhp);
13310696SDavid.Hollister@Sun.COM 		return (defret);
13410696SDavid.Hollister@Sun.COM 	}
13510696SDavid.Hollister@Sun.COM 
13610696SDavid.Hollister@Sun.COM 	(void) snprintf(buf, sizeof (buf),
13710696SDavid.Hollister@Sun.COM 	    PMCS_FIRMWARE_ILA_NAME PMCS_FIRMWARE_START_SUF);
13810696SDavid.Hollister@Sun.COM 	istart = ddi_modsym(modhp, buf, &errno);
13910696SDavid.Hollister@Sun.COM 	if (errno) {
14011048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
14111048SDavid.Hollister@Sun.COM 		    "%s: unable to find symbol '%s'", __func__, buf);
14210696SDavid.Hollister@Sun.COM 		(void) ddi_modclose(modhp);
14310696SDavid.Hollister@Sun.COM 		return (defret);
14410696SDavid.Hollister@Sun.COM 	}
14510696SDavid.Hollister@Sun.COM 
14610696SDavid.Hollister@Sun.COM 	(void) snprintf(buf, sizeof (buf),
14710696SDavid.Hollister@Sun.COM 	    PMCS_FIRMWARE_ILA_NAME PMCS_FIRMWARE_END_SUF);
14810696SDavid.Hollister@Sun.COM 	iend = ddi_modsym(modhp, buf, &errno);
14910696SDavid.Hollister@Sun.COM 	if (errno) {
15011048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
15111048SDavid.Hollister@Sun.COM 		    "%s: unable to find symbol '%s'", __func__, buf);
15210696SDavid.Hollister@Sun.COM 		(void) ddi_modclose(modhp);
15310696SDavid.Hollister@Sun.COM 		return (defret);
15410696SDavid.Hollister@Sun.COM 	}
15510696SDavid.Hollister@Sun.COM 
15610696SDavid.Hollister@Sun.COM 	(void) snprintf(buf, sizeof (buf),
15710696SDavid.Hollister@Sun.COM 	    PMCS_FIRMWARE_SPCBOOT_NAME PMCS_FIRMWARE_START_SUF);
15810696SDavid.Hollister@Sun.COM 	sstart = ddi_modsym(modhp, buf, &errno);
15910696SDavid.Hollister@Sun.COM 	if (errno) {
16011048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
16111048SDavid.Hollister@Sun.COM 		    "%s: unable to find symbol '%s'", __func__, buf);
16210696SDavid.Hollister@Sun.COM 		(void) ddi_modclose(modhp);
16310696SDavid.Hollister@Sun.COM 		return (defret);
16410696SDavid.Hollister@Sun.COM 	}
16510696SDavid.Hollister@Sun.COM 
16610696SDavid.Hollister@Sun.COM 	(void) snprintf(buf, sizeof (buf),
16710696SDavid.Hollister@Sun.COM 	    PMCS_FIRMWARE_SPCBOOT_NAME PMCS_FIRMWARE_END_SUF);
16810696SDavid.Hollister@Sun.COM 	send = ddi_modsym(modhp, buf, &errno);
16910696SDavid.Hollister@Sun.COM 	if (errno) {
17011048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
17111048SDavid.Hollister@Sun.COM 		    "%s: unable to find symbol '%s'", __func__, buf);
17210696SDavid.Hollister@Sun.COM 		(void) ddi_modclose(modhp);
17310696SDavid.Hollister@Sun.COM 		return (defret);
17410696SDavid.Hollister@Sun.COM 	}
17510696SDavid.Hollister@Sun.COM 
17610696SDavid.Hollister@Sun.COM 	/*
17711980SDavid.Hollister@Sun.COM 	 * Get the ILA and firmware versions from the modules themselves
17811980SDavid.Hollister@Sun.COM 	 */
17911980SDavid.Hollister@Sun.COM 	ila_verp = iend - PMCS_ILA_VER_OFFSET;
18011980SDavid.Hollister@Sun.COM 	(void) ddi_strtol((const char *)ila_verp, &bufp, 16, &ila_version);
18111980SDavid.Hollister@Sun.COM 	fw_verp = cend - PMCS_FW_VER_OFFSET;
18211980SDavid.Hollister@Sun.COM 	(void) ddi_strtol((const char *)fw_verp, &bufp, 16, &fw_version);
18311980SDavid.Hollister@Sun.COM 
18411980SDavid.Hollister@Sun.COM 	/*
18511980SDavid.Hollister@Sun.COM 	 * If force update is not set, verify that what we're loading is
18611980SDavid.Hollister@Sun.COM 	 * what we expect.
18711980SDavid.Hollister@Sun.COM 	 */
18811980SDavid.Hollister@Sun.COM 	if (pwp->fw_force_update == 0) {
18911980SDavid.Hollister@Sun.COM 		if (fw_version != PMCS_FIRMWARE_VERSION) {
19011980SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
19111980SDavid.Hollister@Sun.COM 			    "Expected fw version 0x%x, not 0x%lx: not "
19211980SDavid.Hollister@Sun.COM 			    "updating", PMCS_FIRMWARE_VERSION, fw_version);
19311980SDavid.Hollister@Sun.COM 			(void) ddi_modclose(modhp);
19411980SDavid.Hollister@Sun.COM 			return (defret);
19511980SDavid.Hollister@Sun.COM 		}
19611980SDavid.Hollister@Sun.COM 	}
19711980SDavid.Hollister@Sun.COM 
19811980SDavid.Hollister@Sun.COM 	pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
19911980SDavid.Hollister@Sun.COM 	    "Upgrading firmware on card from 0x%x to 0x%lx (ILA version 0x%lx)",
20011980SDavid.Hollister@Sun.COM 	    pwp->fw, fw_version, ila_version);
20111980SDavid.Hollister@Sun.COM 
20211980SDavid.Hollister@Sun.COM 	/*
20310696SDavid.Hollister@Sun.COM 	 * The SPCBoot image must be updated first, and this is written to
20410696SDavid.Hollister@Sun.COM 	 * SEEPROM, not flash.
20510696SDavid.Hollister@Sun.COM 	 */
20610696SDavid.Hollister@Sun.COM 	if (pmcs_set_nvmd(pwp, PMCS_NVMD_SPCBOOT, sstart,
20710696SDavid.Hollister@Sun.COM 	    (size_t)((size_t)send - (size_t)sstart)) == B_FALSE) {
20811048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
20910696SDavid.Hollister@Sun.COM 		    "%s: unable to flash '%s' segment",
21010696SDavid.Hollister@Sun.COM 		    __func__, PMCS_FIRMWARE_SPCBOOT_NAME);
21110696SDavid.Hollister@Sun.COM 		(void) ddi_modclose(modhp);
21210696SDavid.Hollister@Sun.COM 		return (-1);
21310696SDavid.Hollister@Sun.COM 	}
21410696SDavid.Hollister@Sun.COM 
21512119SJesse.Butler@Sun.COM repeat:
21612119SJesse.Butler@Sun.COM 	pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
21712119SJesse.Butler@Sun.COM 	    "%s: Beginning firmware update of %s image.",
21812119SJesse.Butler@Sun.COM 	    __func__, (first_pass ? "first" : "second"));
21912119SJesse.Butler@Sun.COM 
22010696SDavid.Hollister@Sun.COM 	if (pmcs_fw_flash(pwp, (void *)istart,
22110696SDavid.Hollister@Sun.COM 	    (uint32_t)((size_t)iend - (size_t)istart))) {
22211048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
22310696SDavid.Hollister@Sun.COM 		    "%s: unable to flash '%s' segment",
22410696SDavid.Hollister@Sun.COM 		    __func__, PMCS_FIRMWARE_ILA_NAME);
22510696SDavid.Hollister@Sun.COM 		(void) ddi_modclose(modhp);
22610696SDavid.Hollister@Sun.COM 		return (-1);
22710696SDavid.Hollister@Sun.COM 	}
22810696SDavid.Hollister@Sun.COM 
22910696SDavid.Hollister@Sun.COM 	if (pmcs_fw_flash(pwp, (void *)cstart,
23010696SDavid.Hollister@Sun.COM 	    (uint32_t)((size_t)cend - (size_t)cstart))) {
23111048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
23210696SDavid.Hollister@Sun.COM 		    "%s: unable to flash '%s' segment",
23310696SDavid.Hollister@Sun.COM 		    __func__, PMCS_FIRMWARE_CODE_NAME);
23410696SDavid.Hollister@Sun.COM 		(void) ddi_modclose(modhp);
23510696SDavid.Hollister@Sun.COM 		return (-1);
23610696SDavid.Hollister@Sun.COM 	}
23710696SDavid.Hollister@Sun.COM 
23810696SDavid.Hollister@Sun.COM 	if (pmcs_soft_reset(pwp, B_FALSE)) {
23911048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
24010696SDavid.Hollister@Sun.COM 		    "%s: soft reset after flash update failed", __func__);
24112119SJesse.Butler@Sun.COM 		(void) ddi_modclose(modhp);
24210696SDavid.Hollister@Sun.COM 		return (-1);
24310696SDavid.Hollister@Sun.COM 	} else {
24412119SJesse.Butler@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
24512119SJesse.Butler@Sun.COM 		    "%s: %s image successfully upgraded.",
24612119SJesse.Butler@Sun.COM 		    __func__, (first_pass ? "First" : "Second"));
24711692SJesse.Butler@Sun.COM 		pwp->last_reset_reason = PMCS_LAST_RST_FW_UPGRADE;
24810696SDavid.Hollister@Sun.COM 	}
24912119SJesse.Butler@Sun.COM 
25012119SJesse.Butler@Sun.COM 	if (first_pass) {
25112119SJesse.Butler@Sun.COM 		first_pass = 0;
25212119SJesse.Butler@Sun.COM 		goto repeat;
25312119SJesse.Butler@Sun.COM 	}
25412119SJesse.Butler@Sun.COM 
25512119SJesse.Butler@Sun.COM 	pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
25612119SJesse.Butler@Sun.COM 	    "%s: Firmware successfully upgraded", __func__);
25712119SJesse.Butler@Sun.COM 
25812119SJesse.Butler@Sun.COM 	(void) ddi_modclose(modhp);
25910696SDavid.Hollister@Sun.COM 	return (0);
26010696SDavid.Hollister@Sun.COM }
26110696SDavid.Hollister@Sun.COM 
26210696SDavid.Hollister@Sun.COM /*
26310696SDavid.Hollister@Sun.COM  * Flash firmware support
26410696SDavid.Hollister@Sun.COM  * Called unlocked.
26510696SDavid.Hollister@Sun.COM  */
26610696SDavid.Hollister@Sun.COM int
pmcs_fw_flash(pmcs_hw_t * pwp,pmcs_fw_hdr_t * hdr,uint32_t length)26710696SDavid.Hollister@Sun.COM pmcs_fw_flash(pmcs_hw_t *pwp, pmcs_fw_hdr_t *hdr, uint32_t length)
26810696SDavid.Hollister@Sun.COM {
26910696SDavid.Hollister@Sun.COM 	pmcs_fw_hdr_t *hp;
27010696SDavid.Hollister@Sun.COM 	uint8_t *wrk, *base;
27110696SDavid.Hollister@Sun.COM 
27210696SDavid.Hollister@Sun.COM 	/*
27310696SDavid.Hollister@Sun.COM 	 * Step 1- Validate firmware chunks within passed pointer.
27410696SDavid.Hollister@Sun.COM 	 */
27510696SDavid.Hollister@Sun.COM 	hp = hdr;
27610696SDavid.Hollister@Sun.COM 	wrk = (uint8_t *)hdr;
27710696SDavid.Hollister@Sun.COM 	base = wrk;
27810696SDavid.Hollister@Sun.COM 	for (;;) {
27911048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
28010696SDavid.Hollister@Sun.COM 		    "%s: partition 0x%x, Length 0x%x", __func__,
28110696SDavid.Hollister@Sun.COM 		    hp->destination_partition, ntohl(hp->firmware_length));
28210696SDavid.Hollister@Sun.COM 		if (ntohl(hp->firmware_length) == 0) {
28311048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
28410696SDavid.Hollister@Sun.COM 			    "%s: bad firmware length 0x%x",
28510696SDavid.Hollister@Sun.COM 			    __func__, ntohl(hp->firmware_length));
28610696SDavid.Hollister@Sun.COM 			return (EINVAL);
28710696SDavid.Hollister@Sun.COM 		}
28810696SDavid.Hollister@Sun.COM 		wrk += (sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length));
28910696SDavid.Hollister@Sun.COM 		if (wrk == base + length) {
29010696SDavid.Hollister@Sun.COM 			break;
29110696SDavid.Hollister@Sun.COM 		}
29210696SDavid.Hollister@Sun.COM 		if (wrk > base + length) {
29311048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
29410696SDavid.Hollister@Sun.COM 			    "%s: out of bounds firmware length", __func__);
29510696SDavid.Hollister@Sun.COM 			return (EINVAL);
29610696SDavid.Hollister@Sun.COM 		}
29710696SDavid.Hollister@Sun.COM 		hp = (void *)wrk;
29810696SDavid.Hollister@Sun.COM 	}
29910696SDavid.Hollister@Sun.COM 
30010696SDavid.Hollister@Sun.COM 	/*
30110696SDavid.Hollister@Sun.COM 	 * Step 2- acquire scratch
30210696SDavid.Hollister@Sun.COM 	 */
30310696SDavid.Hollister@Sun.COM 	(void) pmcs_acquire_scratch(pwp, B_TRUE);
30410696SDavid.Hollister@Sun.COM 
30510696SDavid.Hollister@Sun.COM 	/*
30610696SDavid.Hollister@Sun.COM 	 * Step 3- loop through firmware chunks and send each one
30710696SDavid.Hollister@Sun.COM 	 * down to be flashed.
30810696SDavid.Hollister@Sun.COM 	 */
30910696SDavid.Hollister@Sun.COM 	hp = hdr;
31010696SDavid.Hollister@Sun.COM 	wrk = (uint8_t *)hdr;
31110696SDavid.Hollister@Sun.COM 	base = wrk;
31210696SDavid.Hollister@Sun.COM 	for (;;) {
31310696SDavid.Hollister@Sun.COM 		if (pmcs_flash_chunk(pwp, wrk)) {
31410696SDavid.Hollister@Sun.COM 			pmcs_release_scratch(pwp);
31510696SDavid.Hollister@Sun.COM 			return (EIO);
31610696SDavid.Hollister@Sun.COM 		}
31710696SDavid.Hollister@Sun.COM 		wrk += (sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length));
31810696SDavid.Hollister@Sun.COM 		if (wrk == base + length) {
31910696SDavid.Hollister@Sun.COM 			break;
32010696SDavid.Hollister@Sun.COM 		}
32110696SDavid.Hollister@Sun.COM 		hp = (void *) wrk;
32210696SDavid.Hollister@Sun.COM 	}
32310696SDavid.Hollister@Sun.COM 	pmcs_release_scratch(pwp);
32410696SDavid.Hollister@Sun.COM 	return (0);
32510696SDavid.Hollister@Sun.COM }
32610696SDavid.Hollister@Sun.COM 
32710696SDavid.Hollister@Sun.COM static int
pmcs_flash_chunk(pmcs_hw_t * pwp,uint8_t * chunk)32810696SDavid.Hollister@Sun.COM pmcs_flash_chunk(pmcs_hw_t *pwp, uint8_t *chunk)
32910696SDavid.Hollister@Sun.COM {
33010696SDavid.Hollister@Sun.COM 	pmcs_fw_hdr_t *hp;
33110696SDavid.Hollister@Sun.COM 	pmcwork_t *pwrk;
33210696SDavid.Hollister@Sun.COM 	uint32_t len, seg, off, result, amt, msg[PMCS_MSG_SIZE], *ptr;
33310696SDavid.Hollister@Sun.COM 
33410696SDavid.Hollister@Sun.COM 	hp = (void *)chunk;
33510696SDavid.Hollister@Sun.COM 	len = sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length);
33610696SDavid.Hollister@Sun.COM 
33710696SDavid.Hollister@Sun.COM 	seg = off = 0;
33810696SDavid.Hollister@Sun.COM 	while (off < len) {
33910696SDavid.Hollister@Sun.COM 		amt = PMCS_SCRATCH_SIZE;
34010696SDavid.Hollister@Sun.COM 		if (off + amt > len) {
34110696SDavid.Hollister@Sun.COM 			amt = len - off;
34210696SDavid.Hollister@Sun.COM 		}
34311048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
34410696SDavid.Hollister@Sun.COM 		    "%s: segment %d offset %u length %u",
34510696SDavid.Hollister@Sun.COM 		    __func__, seg, off, amt);
34610696SDavid.Hollister@Sun.COM 		(void) memcpy(pwp->scratch, &chunk[off], amt);
34710696SDavid.Hollister@Sun.COM 		pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
34810696SDavid.Hollister@Sun.COM 		if (pwrk == NULL) {
34910696SDavid.Hollister@Sun.COM 			return (ENOMEM);
35010696SDavid.Hollister@Sun.COM 		}
35110696SDavid.Hollister@Sun.COM 		pwrk->arg = msg;
35210696SDavid.Hollister@Sun.COM 		msg[0] = LE_32(PMCS_HIPRI(pwp,
35310696SDavid.Hollister@Sun.COM 		    PMCS_OQ_EVENTS, PMCIN_FW_FLASH_UPDATE));
35410696SDavid.Hollister@Sun.COM 		msg[1] = LE_32(pwrk->htag);
35510696SDavid.Hollister@Sun.COM 		msg[2] = LE_32(off);
35610696SDavid.Hollister@Sun.COM 		msg[3] = LE_32(amt);
35710696SDavid.Hollister@Sun.COM 		if (off == 0) {
35810696SDavid.Hollister@Sun.COM 			msg[4] = LE_32(len);
35910696SDavid.Hollister@Sun.COM 		} else {
36010696SDavid.Hollister@Sun.COM 			msg[4] = 0;
36110696SDavid.Hollister@Sun.COM 		}
36210696SDavid.Hollister@Sun.COM 		msg[5] = 0;
36310696SDavid.Hollister@Sun.COM 		msg[6] = 0;
36410696SDavid.Hollister@Sun.COM 		msg[7] = 0;
36510696SDavid.Hollister@Sun.COM 		msg[8] = 0;
36610696SDavid.Hollister@Sun.COM 		msg[9] = 0;
36710696SDavid.Hollister@Sun.COM 		msg[10] = 0;
36810696SDavid.Hollister@Sun.COM 		msg[11] = 0;
36910696SDavid.Hollister@Sun.COM 		msg[12] = LE_32(DWORD0(pwp->scratch_dma));
37010696SDavid.Hollister@Sun.COM 		msg[13] = LE_32(DWORD1(pwp->scratch_dma));
37110696SDavid.Hollister@Sun.COM 		msg[14] = LE_32(amt);
37210696SDavid.Hollister@Sun.COM 		msg[15] = 0;
37310696SDavid.Hollister@Sun.COM 		mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
37410696SDavid.Hollister@Sun.COM 		ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
37510696SDavid.Hollister@Sun.COM 		if (ptr == NULL) {
37610696SDavid.Hollister@Sun.COM 			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
37710696SDavid.Hollister@Sun.COM 			pmcs_pwork(pwp, pwrk);
37811048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
37911048SDavid.Hollister@Sun.COM 			    pmcs_nomsg, __func__);
38010696SDavid.Hollister@Sun.COM 			return (ENOMEM);
38110696SDavid.Hollister@Sun.COM 		}
38210696SDavid.Hollister@Sun.COM 		COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
38310696SDavid.Hollister@Sun.COM 		(void) memset(msg, 0xaf, sizeof (msg));
38410696SDavid.Hollister@Sun.COM 		pwrk->state = PMCS_WORK_STATE_ONCHIP;
38510696SDavid.Hollister@Sun.COM 		INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
38612120SDavid.Hollister@Sun.COM 		WAIT_FOR(pwrk, PMCS_FLASH_WAIT_TIME, result);
38710696SDavid.Hollister@Sun.COM 		pmcs_pwork(pwp, pwrk);
38810696SDavid.Hollister@Sun.COM 		if (result) {
38911267SJesse.Butler@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
39011048SDavid.Hollister@Sun.COM 			    pmcs_timeo, __func__);
39110696SDavid.Hollister@Sun.COM 			return (EIO);
39210696SDavid.Hollister@Sun.COM 		}
39310696SDavid.Hollister@Sun.COM 		switch (LE_32(msg[2])) {
39410696SDavid.Hollister@Sun.COM 		case FLASH_UPDATE_COMPLETE_PENDING_REBOOT:
39511048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
39610696SDavid.Hollister@Sun.COM 			    "%s: segment %d complete pending reboot",
39710696SDavid.Hollister@Sun.COM 			    __func__, seg);
39810696SDavid.Hollister@Sun.COM 			break;
39910696SDavid.Hollister@Sun.COM 		case FLASH_UPDATE_IN_PROGRESS:
40011048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
40110696SDavid.Hollister@Sun.COM 			    "%s: segment %d downloaded", __func__, seg);
40210696SDavid.Hollister@Sun.COM 			break;
40310696SDavid.Hollister@Sun.COM 		case FLASH_UPDATE_HDR_ERR:
40411048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
40510696SDavid.Hollister@Sun.COM 			    "%s: segment %d header error", __func__, seg);
40610696SDavid.Hollister@Sun.COM 			return (EIO);
40710696SDavid.Hollister@Sun.COM 		case FLASH_UPDATE_OFFSET_ERR:
40811048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
40910696SDavid.Hollister@Sun.COM 			    "%s: segment %d offset error", __func__, seg);
41010696SDavid.Hollister@Sun.COM 			return (EIO);
41110696SDavid.Hollister@Sun.COM 		case FLASH_UPDATE_UPDATE_CRC_ERR:
41211048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
41310696SDavid.Hollister@Sun.COM 			    "%s: segment %d update crc error", __func__, seg);
41410696SDavid.Hollister@Sun.COM 			return (EIO);
41510696SDavid.Hollister@Sun.COM 		case FLASH_UPDATE_LENGTH_ERR:
41611048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
41710696SDavid.Hollister@Sun.COM 			    "%s: segment %d length error", __func__, seg);
41810696SDavid.Hollister@Sun.COM 			return (EIO);
41910696SDavid.Hollister@Sun.COM 		case FLASH_UPDATE_HW_ERR:
42011048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
42110696SDavid.Hollister@Sun.COM 			    "%s: segment %d hw error", __func__, seg);
42210696SDavid.Hollister@Sun.COM 			return (EIO);
42310696SDavid.Hollister@Sun.COM 		case FLASH_UPDATE_DNLD_NOT_SUPPORTED:
42411048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
42510696SDavid.Hollister@Sun.COM 			    "%s: segment %d download not supported error",
42610696SDavid.Hollister@Sun.COM 			    __func__, seg);
42710696SDavid.Hollister@Sun.COM 			return (EIO);
42810696SDavid.Hollister@Sun.COM 		case FLASH_UPDATE_DISABLED:
42911048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
43010696SDavid.Hollister@Sun.COM 			    "%s: segment %d update disabled error",
43110696SDavid.Hollister@Sun.COM 			    __func__, seg);
43210696SDavid.Hollister@Sun.COM 			return (EIO);
43310696SDavid.Hollister@Sun.COM 		default:
43411048SDavid.Hollister@Sun.COM 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
43510696SDavid.Hollister@Sun.COM 			    "%s: segment %d unknown error %x",
43610696SDavid.Hollister@Sun.COM 			    __func__, seg, msg[2]);
43710696SDavid.Hollister@Sun.COM 			return (EIO);
43810696SDavid.Hollister@Sun.COM 		}
43910696SDavid.Hollister@Sun.COM 		off += amt;
44010696SDavid.Hollister@Sun.COM 		seg++;
44110696SDavid.Hollister@Sun.COM 	}
44210696SDavid.Hollister@Sun.COM 	return (0);
44310696SDavid.Hollister@Sun.COM }
44410696SDavid.Hollister@Sun.COM 
44510696SDavid.Hollister@Sun.COM /*
44610696SDavid.Hollister@Sun.COM  * pmcs_validate_vpd
44710696SDavid.Hollister@Sun.COM  *
44810696SDavid.Hollister@Sun.COM  * Input: softstate pointer and pointer to vpd data buffer
44910696SDavid.Hollister@Sun.COM  * Returns: B_TRUE if VPD data looks OK, B_FALSE otherwise
45010696SDavid.Hollister@Sun.COM  */
45110696SDavid.Hollister@Sun.COM static boolean_t
pmcs_validate_vpd(pmcs_hw_t * pwp,uint8_t * data)45210696SDavid.Hollister@Sun.COM pmcs_validate_vpd(pmcs_hw_t *pwp, uint8_t *data)
45310696SDavid.Hollister@Sun.COM {
45410696SDavid.Hollister@Sun.COM 	pmcs_vpd_header_t *vpd_header;
45510696SDavid.Hollister@Sun.COM 	uint8_t *bufp, kv_len, *chksump, chksum = 0;
45610696SDavid.Hollister@Sun.COM 	char tbuf[80];
45710696SDavid.Hollister@Sun.COM 	char prop[24];
45810696SDavid.Hollister@Sun.COM 	int idx, str_len;
45910696SDavid.Hollister@Sun.COM 	uint16_t strid_length, chksum_len;
46010696SDavid.Hollister@Sun.COM 	uint64_t wwid;
46110696SDavid.Hollister@Sun.COM 	pmcs_vpd_kv_t *vkvp;
46210696SDavid.Hollister@Sun.COM 
46310696SDavid.Hollister@Sun.COM 	vpd_header = (pmcs_vpd_header_t *)data;
46410696SDavid.Hollister@Sun.COM 
46510696SDavid.Hollister@Sun.COM 	/*
46610696SDavid.Hollister@Sun.COM 	 * Make sure we understand the format of this data
46710696SDavid.Hollister@Sun.COM 	 */
46810696SDavid.Hollister@Sun.COM 
469*12698Ssrikanth.suravajhala@oracle.com 	/*
470*12698Ssrikanth.suravajhala@oracle.com 	 * Only VPD version 1 is VALID for Thebe-INT cards and
471*12698Ssrikanth.suravajhala@oracle.com 	 * Only VPD version 2 is valid for Thebe-EXT cards
472*12698Ssrikanth.suravajhala@oracle.com 	 */
473*12698Ssrikanth.suravajhala@oracle.com 	if ((vpd_header->eeprom_version == PMCS_EEPROM_INT_VERSION &&
474*12698Ssrikanth.suravajhala@oracle.com 	    vpd_header->subsys_pid[0] == PMCS_EEPROM_INT_SSID_BYTE1 &&
475*12698Ssrikanth.suravajhala@oracle.com 	    vpd_header->subsys_pid[1] == PMCS_EEPROM_INT_SSID_BYTE2) ||
476*12698Ssrikanth.suravajhala@oracle.com 	    (vpd_header->eeprom_version == PMCS_EEPROM_EXT_VERSION &&
477*12698Ssrikanth.suravajhala@oracle.com 	    vpd_header->subsys_pid[0] == PMCS_EEPROM_EXT_SSID_BYTE1 &&
478*12698Ssrikanth.suravajhala@oracle.com 	    vpd_header->subsys_pid[1] == PMCS_EEPROM_EXT_SSID_BYTE2)) {
479*12698Ssrikanth.suravajhala@oracle.com 			goto valid_version;
480*12698Ssrikanth.suravajhala@oracle.com 	} else {
48111048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
482*12698Ssrikanth.suravajhala@oracle.com 		    "%s: Detected Thebe card with SSID(%02x%02x)", __func__,
483*12698Ssrikanth.suravajhala@oracle.com 		    vpd_header->subsys_pid[0], vpd_header->subsys_pid[1]);
484*12698Ssrikanth.suravajhala@oracle.com 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
485*12698Ssrikanth.suravajhala@oracle.com 		    "%s: EEPROM(%d) unsupported; requires %d for INT(%02x%02x) "
486*12698Ssrikanth.suravajhala@oracle.com 		    " and %d for EXT(%02x%02x) cards.", __func__,
487*12698Ssrikanth.suravajhala@oracle.com 		    vpd_header->eeprom_version,
488*12698Ssrikanth.suravajhala@oracle.com 		    PMCS_EEPROM_INT_VERSION, PMCS_EEPROM_INT_SSID_BYTE1,
489*12698Ssrikanth.suravajhala@oracle.com 		    PMCS_EEPROM_INT_SSID_BYTE2, PMCS_EEPROM_EXT_VERSION,
490*12698Ssrikanth.suravajhala@oracle.com 		    PMCS_EEPROM_EXT_SSID_BYTE1, PMCS_EEPROM_EXT_SSID_BYTE2);
49110696SDavid.Hollister@Sun.COM 		return (B_FALSE);
49210696SDavid.Hollister@Sun.COM 	}
49310696SDavid.Hollister@Sun.COM 
494*12698Ssrikanth.suravajhala@oracle.com valid_version:
49510696SDavid.Hollister@Sun.COM 	/*
49610696SDavid.Hollister@Sun.COM 	 * Do we have a valid SAS WWID?
49710696SDavid.Hollister@Sun.COM 	 */
49810696SDavid.Hollister@Sun.COM 	if (((vpd_header->hba_sas_wwid[0] & 0xf0) >> 4) != NAA_IEEE_REG) {
49911048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
50010696SDavid.Hollister@Sun.COM 		    "%s: SAS WWN has invalid NAA (%d)", __func__,
50110696SDavid.Hollister@Sun.COM 		    ((vpd_header->hba_sas_wwid[0] & 0xf0) >> 4));
50210696SDavid.Hollister@Sun.COM 		return (B_FALSE);
50310696SDavid.Hollister@Sun.COM 	}
50410696SDavid.Hollister@Sun.COM 	wwid = pmcs_barray2wwn(vpd_header->hba_sas_wwid);
50510696SDavid.Hollister@Sun.COM 	for (idx = 0; idx < PMCS_MAX_PORTS; idx++) {
50610696SDavid.Hollister@Sun.COM 		pwp->sas_wwns[idx] = wwid + idx;
50710696SDavid.Hollister@Sun.COM 	}
50810696SDavid.Hollister@Sun.COM 
50910696SDavid.Hollister@Sun.COM 	if (vpd_header->vpd_start_byte != PMCS_VPD_START) {
51011048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
51110696SDavid.Hollister@Sun.COM 		    "%s: Didn't see VPD start byte", __func__);
51210696SDavid.Hollister@Sun.COM 		return (B_FALSE);
51310696SDavid.Hollister@Sun.COM 	}
51410696SDavid.Hollister@Sun.COM 
51510696SDavid.Hollister@Sun.COM 	/*
51610696SDavid.Hollister@Sun.COM 	 * We only checksum the VPD data between (and including) VPD Start byte
51710696SDavid.Hollister@Sun.COM 	 * and the checksum value byte. The length of this data for CRC is
51810696SDavid.Hollister@Sun.COM 	 * 15 less than the length indicated in vpd_length field of the header.
51910696SDavid.Hollister@Sun.COM 	 * 8 (SAS WWN) + 2 (subsystem ID) + 2 (subsystem vendor ID) +
52010696SDavid.Hollister@Sun.COM 	 * 1 (end tag) + 2 (hex byte CRC, different from this one) = 15 bytes
52110696SDavid.Hollister@Sun.COM 	 */
52210696SDavid.Hollister@Sun.COM 	/*
52310696SDavid.Hollister@Sun.COM 	 * VPD length (little endian format) is represented as byte-array field
52410696SDavid.Hollister@Sun.COM 	 * & read the following way to avoid alignment issues (in SPARC)
52510696SDavid.Hollister@Sun.COM 	 */
52610696SDavid.Hollister@Sun.COM 	chksum_len = ((vpd_header->vpd_length[1] << 8) |
52710696SDavid.Hollister@Sun.COM 	    (vpd_header->vpd_length[0])) - 15;
52810696SDavid.Hollister@Sun.COM 	/* Validate VPD data checksum */
52910696SDavid.Hollister@Sun.COM 	chksump = (uint8_t *)&vpd_header->vpd_start_byte;
53010696SDavid.Hollister@Sun.COM 	ASSERT (*chksump == PMCS_VPD_START);
53110696SDavid.Hollister@Sun.COM 	for (idx = 0; idx < chksum_len; idx++, chksump++) {
53210696SDavid.Hollister@Sun.COM 		chksum += *chksump;
53310696SDavid.Hollister@Sun.COM 	}
53410696SDavid.Hollister@Sun.COM 	ASSERT (*chksump == PMCS_VPD_END);
53510696SDavid.Hollister@Sun.COM 	if (chksum) {
53612060SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
53712060SDavid.Hollister@Sun.COM 		    "%s: VPD checksum failure", __func__);
53812060SDavid.Hollister@Sun.COM 		return (B_FALSE);
53910696SDavid.Hollister@Sun.COM 	}
54010696SDavid.Hollister@Sun.COM 
54110696SDavid.Hollister@Sun.COM 	/*
54210696SDavid.Hollister@Sun.COM 	 * Get length of string ID tag and read it.
54310696SDavid.Hollister@Sun.COM 	 */
54410696SDavid.Hollister@Sun.COM 	bufp = (uint8_t *)&vpd_header->vpd_start_byte;
54510696SDavid.Hollister@Sun.COM 	bufp += 3;		/* Skip the start byte and length */
54610696SDavid.Hollister@Sun.COM 	/*
54710696SDavid.Hollister@Sun.COM 	 * String ID tag length (little endian format) is represented as
54810696SDavid.Hollister@Sun.COM 	 * byte-array & read the following way to avoid alignment issues
54910696SDavid.Hollister@Sun.COM 	 * (in SPARC)
55010696SDavid.Hollister@Sun.COM 	 */
55110696SDavid.Hollister@Sun.COM 	strid_length = (vpd_header->strid_length[1] << 8) |
55210696SDavid.Hollister@Sun.COM 	    (vpd_header->strid_length[0]);
55310696SDavid.Hollister@Sun.COM 	if (strid_length > 79) {
55410696SDavid.Hollister@Sun.COM 		strid_length = 79;
55510696SDavid.Hollister@Sun.COM 	}
55610696SDavid.Hollister@Sun.COM 	bcopy(bufp, tbuf, strid_length);
55710696SDavid.Hollister@Sun.COM 	tbuf[strid_length] = 0;
55810696SDavid.Hollister@Sun.COM 
55911048SDavid.Hollister@Sun.COM 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
56010696SDavid.Hollister@Sun.COM 	    "%s: Product Name: '%s'", __func__, tbuf);
56110696SDavid.Hollister@Sun.COM 	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_MODEL_NAME, tbuf);
56210696SDavid.Hollister@Sun.COM 
56310696SDavid.Hollister@Sun.COM 	/*
56410696SDavid.Hollister@Sun.COM 	 * Skip VPD-R tag and length of read-only tag, then start reading
56510696SDavid.Hollister@Sun.COM 	 * keyword/value pairs
56610696SDavid.Hollister@Sun.COM 	 */
56710696SDavid.Hollister@Sun.COM 	bufp += strid_length;	/* Skip to VPD-R tag */
56810696SDavid.Hollister@Sun.COM 	bufp += 3;		/* Skip VPD-R tag and length of VPD-R data */
56910696SDavid.Hollister@Sun.COM 
57010696SDavid.Hollister@Sun.COM 	vkvp = (pmcs_vpd_kv_t *)bufp;
57110696SDavid.Hollister@Sun.COM 
57210696SDavid.Hollister@Sun.COM 	while (vkvp->keyword[0] != PMCS_VPD_END) {
57310696SDavid.Hollister@Sun.COM 		tbuf[0] = 0;
57410696SDavid.Hollister@Sun.COM 		str_len = snprintf(tbuf, 80, "VPD: %c%c = <",
57510696SDavid.Hollister@Sun.COM 		    vkvp->keyword[0], vkvp->keyword[1]);
57610696SDavid.Hollister@Sun.COM 
57710696SDavid.Hollister@Sun.COM 		kv_len = vkvp->value_length;
57810696SDavid.Hollister@Sun.COM 		for (idx = 0; idx < kv_len; idx++) {
57910696SDavid.Hollister@Sun.COM 			tbuf[str_len + idx] = vkvp->value[idx];
58010696SDavid.Hollister@Sun.COM 			prop[idx] = vkvp->value[idx];
58110696SDavid.Hollister@Sun.COM 		}
58210696SDavid.Hollister@Sun.COM 		prop[idx] = '\0';
58310696SDavid.Hollister@Sun.COM 		str_len += kv_len;
58410696SDavid.Hollister@Sun.COM 		tbuf[str_len] = '>';
58510696SDavid.Hollister@Sun.COM 		tbuf[str_len + 1] = 0;
58611048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "%s (Len: 0x%x)",
58711048SDavid.Hollister@Sun.COM 		    tbuf, kv_len);
58810696SDavid.Hollister@Sun.COM 
58910696SDavid.Hollister@Sun.COM 		/* Keyword is Manufacturer */
59010696SDavid.Hollister@Sun.COM 		if ((vkvp->keyword[0] == 'M') && (vkvp->keyword[1] == 'N')) {
59110696SDavid.Hollister@Sun.COM 			pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING,
59210696SDavid.Hollister@Sun.COM 			    PMCS_MANUFACTURER, prop);
59310696SDavid.Hollister@Sun.COM 		}
59410696SDavid.Hollister@Sun.COM 		/* Keyword is Serial Number */
59510696SDavid.Hollister@Sun.COM 		if ((vkvp->keyword[0] == 'S') && (vkvp->keyword[1] == 'N')) {
59610696SDavid.Hollister@Sun.COM 			pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING,
59710696SDavid.Hollister@Sun.COM 			    PMCS_SERIAL_NUMBER, prop);
59810696SDavid.Hollister@Sun.COM 		}
59910696SDavid.Hollister@Sun.COM 
60010696SDavid.Hollister@Sun.COM 		vkvp = (pmcs_vpd_kv_t *)(bufp + 3 + kv_len);
60110696SDavid.Hollister@Sun.COM 		bufp += kv_len + 3;
60210696SDavid.Hollister@Sun.COM 	}
60310696SDavid.Hollister@Sun.COM 
60410696SDavid.Hollister@Sun.COM 	return (B_TRUE);
60510696SDavid.Hollister@Sun.COM }
60610696SDavid.Hollister@Sun.COM 
60710696SDavid.Hollister@Sun.COM /*
60810696SDavid.Hollister@Sun.COM  * pmcs_get_nvmd
60910696SDavid.Hollister@Sun.COM  *
61010696SDavid.Hollister@Sun.COM  * This function will read the requested data from the non-volatile
61110696SDavid.Hollister@Sun.COM  * storage on the card.  This could mean SEEPROM, VPD, or other areas
61210696SDavid.Hollister@Sun.COM  * as defined by the PM8001 programmer's manual.
61310696SDavid.Hollister@Sun.COM  *
61410696SDavid.Hollister@Sun.COM  * nvmd_type: The data type being requested
61510696SDavid.Hollister@Sun.COM  * nvmd: NVM device to access (IOP/AAP1)
61610696SDavid.Hollister@Sun.COM  * offset: Must be 4K alignment
61710696SDavid.Hollister@Sun.COM  * buf: Pointer to memory region for retrieved data
61810696SDavid.Hollister@Sun.COM  * size_left: Total available bytes left in buf
61910696SDavid.Hollister@Sun.COM  *
62010696SDavid.Hollister@Sun.COM  * Returns: non-negative on success, -1 on failure
62110696SDavid.Hollister@Sun.COM  */
62210696SDavid.Hollister@Sun.COM 
62310696SDavid.Hollister@Sun.COM /*ARGSUSED*/
62410696SDavid.Hollister@Sun.COM int
pmcs_get_nvmd(pmcs_hw_t * pwp,pmcs_nvmd_type_t nvmd_type,uint8_t nvmd,uint32_t offset,char * buf,uint32_t size_left)62510696SDavid.Hollister@Sun.COM pmcs_get_nvmd(pmcs_hw_t *pwp, pmcs_nvmd_type_t nvmd_type, uint8_t nvmd,
62610696SDavid.Hollister@Sun.COM     uint32_t offset, char *buf, uint32_t size_left)
62710696SDavid.Hollister@Sun.COM {
62810696SDavid.Hollister@Sun.COM 	pmcs_get_nvmd_cmd_t iomb;
62910696SDavid.Hollister@Sun.COM 	pmcwork_t *workp;
63010696SDavid.Hollister@Sun.COM 	uint8_t *chunkp;
63110696SDavid.Hollister@Sun.COM 	uint32_t *ptr, ibq, *iombp;
63210696SDavid.Hollister@Sun.COM 	uint32_t dlen;
63310696SDavid.Hollister@Sun.COM 	uint16_t status;
63410696SDavid.Hollister@Sun.COM 	uint8_t tdas_nvmd, ip, tda, tbn_tdps;
63510696SDavid.Hollister@Sun.COM 	uint8_t	doa[3];
63610696SDavid.Hollister@Sun.COM 	int32_t result = -1, i = 0;
63710696SDavid.Hollister@Sun.COM 
63810696SDavid.Hollister@Sun.COM 	switch (nvmd_type) {
63910696SDavid.Hollister@Sun.COM 	case PMCS_NVMD_VPD:
64010696SDavid.Hollister@Sun.COM 		tdas_nvmd = PMCIN_NVMD_TDPS_1 | PMCIN_NVMD_TWI;
64110696SDavid.Hollister@Sun.COM 		tda = PMCIN_TDA_PAGE(2);
64210696SDavid.Hollister@Sun.COM 		tbn_tdps = PMCIN_NVMD_TBN(0) | PMCIN_NVMD_TDPS_8;
64310696SDavid.Hollister@Sun.COM 		ip = PMCIN_NVMD_INDIRECT_PLD;
64410696SDavid.Hollister@Sun.COM 		dlen = LE_32(PMCS_SEEPROM_PAGE_SIZE);
64510696SDavid.Hollister@Sun.COM 		doa[0] = 0;
64610696SDavid.Hollister@Sun.COM 		doa[1] = 0;
64710696SDavid.Hollister@Sun.COM 		doa[2] = 0;
64810696SDavid.Hollister@Sun.COM 		break;
64910696SDavid.Hollister@Sun.COM 	case PMCS_NVMD_REG_DUMP:
65010696SDavid.Hollister@Sun.COM 		tdas_nvmd = nvmd;
65110696SDavid.Hollister@Sun.COM 		tda = 0;
65210696SDavid.Hollister@Sun.COM 		tbn_tdps = 0;
65310696SDavid.Hollister@Sun.COM 		ip = PMCIN_NVMD_INDIRECT_PLD;
65410696SDavid.Hollister@Sun.COM 		dlen = LE_32(PMCS_REGISTER_DUMP_BLOCK_SIZE);
65510696SDavid.Hollister@Sun.COM 		doa[0] = offset & 0xff;
65610696SDavid.Hollister@Sun.COM 		doa[1] = (offset >> 8) & 0xff;
65710696SDavid.Hollister@Sun.COM 		doa[2] = (offset >> 16) & 0xff;
65810696SDavid.Hollister@Sun.COM 		break;
65910696SDavid.Hollister@Sun.COM 	case PMCS_NVMD_EVENT_LOG:
66010696SDavid.Hollister@Sun.COM 		tdas_nvmd = nvmd;
66110696SDavid.Hollister@Sun.COM 		tda = 0;
66210696SDavid.Hollister@Sun.COM 		tbn_tdps = 0;
66310696SDavid.Hollister@Sun.COM 		ip = PMCIN_NVMD_INDIRECT_PLD;
66410696SDavid.Hollister@Sun.COM 		dlen = LE_32(PMCS_REGISTER_DUMP_BLOCK_SIZE);
66510696SDavid.Hollister@Sun.COM 		offset = offset + PMCS_NVMD_EVENT_LOG_OFFSET;
66610696SDavid.Hollister@Sun.COM 		doa[0] = offset & 0xff;
66710696SDavid.Hollister@Sun.COM 		doa[1] = (offset >> 8) & 0xff;
66810696SDavid.Hollister@Sun.COM 		doa[2] = (offset >> 16) & 0xff;
66910696SDavid.Hollister@Sun.COM 		break;
67010696SDavid.Hollister@Sun.COM 	default:
67111048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
67210696SDavid.Hollister@Sun.COM 		    "%s: Invalid nvmd type: %d", __func__, nvmd_type);
67310696SDavid.Hollister@Sun.COM 		return (-1);
67410696SDavid.Hollister@Sun.COM 	}
67510696SDavid.Hollister@Sun.COM 
67610696SDavid.Hollister@Sun.COM 	workp = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
67710696SDavid.Hollister@Sun.COM 	if (workp == NULL) {
67811048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
67910696SDavid.Hollister@Sun.COM 		    "%s: Unable to get work struct", __func__);
68010696SDavid.Hollister@Sun.COM 		return (-1);
68110696SDavid.Hollister@Sun.COM 	}
68210696SDavid.Hollister@Sun.COM 
68310696SDavid.Hollister@Sun.COM 	ptr = &iomb.header;
68410696SDavid.Hollister@Sun.COM 	bzero(ptr, sizeof (pmcs_get_nvmd_cmd_t));
68510696SDavid.Hollister@Sun.COM 	*ptr = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_GENERAL, PMCIN_GET_NVMD_DATA));
68610696SDavid.Hollister@Sun.COM 	workp->arg = (void *)&iomb;
68710696SDavid.Hollister@Sun.COM 	iomb.htag = LE_32(workp->htag);
68810696SDavid.Hollister@Sun.COM 	iomb.ip = ip;
68910696SDavid.Hollister@Sun.COM 	iomb.tbn_tdps = tbn_tdps;
69010696SDavid.Hollister@Sun.COM 	iomb.tda = tda;
69110696SDavid.Hollister@Sun.COM 	iomb.tdas_nvmd = tdas_nvmd;
69210696SDavid.Hollister@Sun.COM 	iomb.ipbal = LE_32(DWORD0(pwp->flash_chunk_addr));
69310696SDavid.Hollister@Sun.COM 	iomb.ipbah = LE_32(DWORD1(pwp->flash_chunk_addr));
69410696SDavid.Hollister@Sun.COM 	iomb.ipdl = dlen;
69510696SDavid.Hollister@Sun.COM 	iomb.doa[0] = doa[0];
69610696SDavid.Hollister@Sun.COM 	iomb.doa[1] = doa[1];
69710696SDavid.Hollister@Sun.COM 	iomb.doa[2] = doa[2];
69810696SDavid.Hollister@Sun.COM 
69910696SDavid.Hollister@Sun.COM 	/*
70010696SDavid.Hollister@Sun.COM 	 * ptr will now point to the inbound queue message
70110696SDavid.Hollister@Sun.COM 	 */
70210696SDavid.Hollister@Sun.COM 	GET_IO_IQ_ENTRY(pwp, ptr, 0, ibq);
70310696SDavid.Hollister@Sun.COM 	if (ptr == NULL) {
70411048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
70511048SDavid.Hollister@Sun.COM 		    "!%s: Unable to get IQ entry", __func__);
70610696SDavid.Hollister@Sun.COM 		pmcs_pwork(pwp, workp);
70710696SDavid.Hollister@Sun.COM 		return (-1);
70810696SDavid.Hollister@Sun.COM 	}
70910696SDavid.Hollister@Sun.COM 
71010696SDavid.Hollister@Sun.COM 	bzero(ptr, PMCS_MSG_SIZE << 2);	/* PMCS_MSG_SIZE is in dwords */
71110696SDavid.Hollister@Sun.COM 	iombp = (uint32_t *)&iomb;
71210696SDavid.Hollister@Sun.COM 	COPY_MESSAGE(ptr, iombp, sizeof (pmcs_get_nvmd_cmd_t) >> 2);
71310696SDavid.Hollister@Sun.COM 	workp->state = PMCS_WORK_STATE_ONCHIP;
71410696SDavid.Hollister@Sun.COM 	INC_IQ_ENTRY(pwp, ibq);
71510696SDavid.Hollister@Sun.COM 
71610696SDavid.Hollister@Sun.COM 	WAIT_FOR(workp, 1000, result);
71710696SDavid.Hollister@Sun.COM 	ptr = workp->arg;
71810696SDavid.Hollister@Sun.COM 	if (result) {
71910696SDavid.Hollister@Sun.COM 		pmcs_timed_out(pwp, workp->htag, __func__);
72010696SDavid.Hollister@Sun.COM 		pmcs_pwork(pwp, workp);
72110696SDavid.Hollister@Sun.COM 		return (-1);
72210696SDavid.Hollister@Sun.COM 	}
72310696SDavid.Hollister@Sun.COM 	status = LE_32(*(ptr + 3)) & 0xffff;
72410696SDavid.Hollister@Sun.COM 	if (status != PMCS_NVMD_STAT_SUCCESS) {
72511048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
72611048SDavid.Hollister@Sun.COM 		    "%s: Error, status = 0x%04x", __func__, status);
72710696SDavid.Hollister@Sun.COM 		pmcs_pwork(pwp, workp);
72810696SDavid.Hollister@Sun.COM 		return (-1);
72910696SDavid.Hollister@Sun.COM 	}
73010696SDavid.Hollister@Sun.COM 
73110696SDavid.Hollister@Sun.COM 	pmcs_pwork(pwp, workp);
73210696SDavid.Hollister@Sun.COM 
73310696SDavid.Hollister@Sun.COM 	if (ddi_dma_sync(pwp->cip_handles, 0, 0,
73410696SDavid.Hollister@Sun.COM 	    DDI_DMA_SYNC_FORKERNEL) != DDI_SUCCESS) {
73511048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
73611048SDavid.Hollister@Sun.COM 		    "Condition check failed at %s():%d", __func__, __LINE__);
73710696SDavid.Hollister@Sun.COM 	}
73810696SDavid.Hollister@Sun.COM 	chunkp = (uint8_t *)pwp->flash_chunkp;
73910696SDavid.Hollister@Sun.COM 
74010696SDavid.Hollister@Sun.COM 	switch (nvmd) {
74110696SDavid.Hollister@Sun.COM 	case PMCIN_NVMD_VPD:
74210696SDavid.Hollister@Sun.COM 		if (pmcs_validate_vpd(pwp, chunkp)) {
74310696SDavid.Hollister@Sun.COM 			result = 0;
74410696SDavid.Hollister@Sun.COM 		} else {
74510696SDavid.Hollister@Sun.COM 			result = -1;
74610696SDavid.Hollister@Sun.COM 		}
74710696SDavid.Hollister@Sun.COM 		break;
74810696SDavid.Hollister@Sun.COM 	case PMCIN_NVMD_AAP1:
74910696SDavid.Hollister@Sun.COM 	case PMCIN_NVMD_IOP:
75010696SDavid.Hollister@Sun.COM 		ASSERT(buf);
75110696SDavid.Hollister@Sun.COM 		i = 0;
75210696SDavid.Hollister@Sun.COM 		if (nvmd_type == PMCS_NVMD_REG_DUMP) {
75310696SDavid.Hollister@Sun.COM 			while ((i < PMCS_FLASH_CHUNK_SIZE) &&
75410696SDavid.Hollister@Sun.COM 			    (chunkp[i] != 0xff) && (chunkp[i] != '\0')) {
75510696SDavid.Hollister@Sun.COM 				(void) snprintf(&buf[i], (size_left - i),
75610696SDavid.Hollister@Sun.COM 				    "%c", chunkp[i]);
75710696SDavid.Hollister@Sun.COM 				i++;
75810696SDavid.Hollister@Sun.COM 			}
75910696SDavid.Hollister@Sun.COM 		} else if (nvmd_type == PMCS_NVMD_EVENT_LOG) {
76010696SDavid.Hollister@Sun.COM 			i = pmcs_dump_binary(pwp, pwp->flash_chunkp, 0,
76110696SDavid.Hollister@Sun.COM 			    (PMCS_FLASH_CHUNK_SIZE >> 2), buf, size_left);
76210696SDavid.Hollister@Sun.COM 		}
76310696SDavid.Hollister@Sun.COM 		result = i;
76410696SDavid.Hollister@Sun.COM 		break;
76510696SDavid.Hollister@Sun.COM 	default:
76611048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
76711048SDavid.Hollister@Sun.COM 		    "UNKNOWN NVMD DEVICE");
76810696SDavid.Hollister@Sun.COM 		return (-1);
76910696SDavid.Hollister@Sun.COM 	}
77010696SDavid.Hollister@Sun.COM 
77110696SDavid.Hollister@Sun.COM 	return (result);
77210696SDavid.Hollister@Sun.COM }
77310696SDavid.Hollister@Sun.COM 
77410696SDavid.Hollister@Sun.COM /*
77510696SDavid.Hollister@Sun.COM  * pmcs_set_nvmd
77610696SDavid.Hollister@Sun.COM  *
77710696SDavid.Hollister@Sun.COM  * This function will write the requested data to non-volatile storage
77810696SDavid.Hollister@Sun.COM  * on the HBA.  This could mean SEEPROM, VPD, or other areas as defined by
77910696SDavid.Hollister@Sun.COM  * the PM8001 programmer's manual.
78010696SDavid.Hollister@Sun.COM  *
78110696SDavid.Hollister@Sun.COM  * nvmd_type: The data type to be written
78210696SDavid.Hollister@Sun.COM  * buf: Pointer to memory region for data to write
78310696SDavid.Hollister@Sun.COM  * len: Length of the data buffer
78410696SDavid.Hollister@Sun.COM  *
78510696SDavid.Hollister@Sun.COM  * Returns: B_TRUE on success, B_FALSE on failure
78610696SDavid.Hollister@Sun.COM  */
78710696SDavid.Hollister@Sun.COM 
78810696SDavid.Hollister@Sun.COM boolean_t
pmcs_set_nvmd(pmcs_hw_t * pwp,pmcs_nvmd_type_t nvmd_type,uint8_t * buf,size_t len)78910696SDavid.Hollister@Sun.COM pmcs_set_nvmd(pmcs_hw_t *pwp, pmcs_nvmd_type_t nvmd_type, uint8_t *buf,
79010696SDavid.Hollister@Sun.COM     size_t len)
79110696SDavid.Hollister@Sun.COM {
79210696SDavid.Hollister@Sun.COM 	pmcs_set_nvmd_cmd_t iomb;
79310696SDavid.Hollister@Sun.COM 	pmcwork_t *workp;
79410696SDavid.Hollister@Sun.COM 	uint32_t *ptr, ibq, *iombp;
79510696SDavid.Hollister@Sun.COM 	uint32_t dlen;
79610696SDavid.Hollister@Sun.COM 	uint16_t status;
79710696SDavid.Hollister@Sun.COM 	uint8_t tdas_nvmd, ip;
79810696SDavid.Hollister@Sun.COM 	int result;
79910696SDavid.Hollister@Sun.COM 
80010696SDavid.Hollister@Sun.COM 	switch (nvmd_type) {
80110696SDavid.Hollister@Sun.COM 	case PMCS_NVMD_SPCBOOT:
80210696SDavid.Hollister@Sun.COM 		tdas_nvmd = PMCIN_NVMD_SEEPROM;
80310696SDavid.Hollister@Sun.COM 		ip = PMCIN_NVMD_INDIRECT_PLD;
80410696SDavid.Hollister@Sun.COM 		ASSERT((len >= PMCS_SPCBOOT_MIN_SIZE) &&
80510696SDavid.Hollister@Sun.COM 		    (len <= PMCS_SPCBOOT_MAX_SIZE));
80610696SDavid.Hollister@Sun.COM 		dlen = LE_32(len);
80710696SDavid.Hollister@Sun.COM 		break;
80810696SDavid.Hollister@Sun.COM 	default:
80911048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
81010696SDavid.Hollister@Sun.COM 		    "%s: Invalid nvmd type: %d", __func__, nvmd_type);
81110696SDavid.Hollister@Sun.COM 		return (B_FALSE);
81210696SDavid.Hollister@Sun.COM 	}
81310696SDavid.Hollister@Sun.COM 
81411048SDavid.Hollister@Sun.COM 	pmcs_prt(pwp, PMCS_PRT_DEBUG_DEVEL, NULL, NULL,
81511048SDavid.Hollister@Sun.COM 	    "%s: Request for nvmd type: %d", __func__, nvmd_type);
81610696SDavid.Hollister@Sun.COM 
81710696SDavid.Hollister@Sun.COM 	workp = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
81810696SDavid.Hollister@Sun.COM 	if (workp == NULL) {
81911048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
82010696SDavid.Hollister@Sun.COM 		    "%s: Unable to get work struct", __func__);
82110696SDavid.Hollister@Sun.COM 		return (B_FALSE);
82210696SDavid.Hollister@Sun.COM 	}
82310696SDavid.Hollister@Sun.COM 
82410696SDavid.Hollister@Sun.COM 	ptr = &iomb.header;
82510696SDavid.Hollister@Sun.COM 	bzero(ptr, sizeof (pmcs_set_nvmd_cmd_t));
82610696SDavid.Hollister@Sun.COM 	*ptr = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_GENERAL, PMCIN_SET_NVMD_DATA));
82710696SDavid.Hollister@Sun.COM 	workp->arg = (void *)&iomb;
82810696SDavid.Hollister@Sun.COM 	iomb.htag = LE_32(workp->htag);
82910696SDavid.Hollister@Sun.COM 	iomb.ip = ip;
83010696SDavid.Hollister@Sun.COM 	iomb.tdas_nvmd = tdas_nvmd;
83110696SDavid.Hollister@Sun.COM 	iomb.signature = LE_32(PMCS_SEEPROM_SIGNATURE);
83210696SDavid.Hollister@Sun.COM 	iomb.ipbal = LE_32(DWORD0(pwp->flash_chunk_addr));
83310696SDavid.Hollister@Sun.COM 	iomb.ipbah = LE_32(DWORD1(pwp->flash_chunk_addr));
83410696SDavid.Hollister@Sun.COM 	iomb.ipdl = dlen;
83510696SDavid.Hollister@Sun.COM 
83610696SDavid.Hollister@Sun.COM 	pmcs_print_entry(pwp, PMCS_PRT_DEBUG_DEVEL,
83710696SDavid.Hollister@Sun.COM 	    "PMCIN_SET_NVMD_DATA iomb", (void *)&iomb);
83810696SDavid.Hollister@Sun.COM 
83910696SDavid.Hollister@Sun.COM 	bcopy(buf, pwp->flash_chunkp, len);
84010696SDavid.Hollister@Sun.COM 	if (ddi_dma_sync(pwp->cip_handles, 0, 0,
84110696SDavid.Hollister@Sun.COM 	    DDI_DMA_SYNC_FORDEV) != DDI_SUCCESS) {
84211048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
84311048SDavid.Hollister@Sun.COM 		    "Condition check failed at %s():%d", __func__, __LINE__);
84410696SDavid.Hollister@Sun.COM 	}
84510696SDavid.Hollister@Sun.COM 
84610696SDavid.Hollister@Sun.COM 	/*
84710696SDavid.Hollister@Sun.COM 	 * ptr will now point to the inbound queue message
84810696SDavid.Hollister@Sun.COM 	 */
84910696SDavid.Hollister@Sun.COM 	GET_IO_IQ_ENTRY(pwp, ptr, 0, ibq);
85010696SDavid.Hollister@Sun.COM 	if (ptr == NULL) {
85111048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
85211048SDavid.Hollister@Sun.COM 		    "!%s: Unable to get IQ entry", __func__);
85310696SDavid.Hollister@Sun.COM 		pmcs_pwork(pwp, workp);
85410696SDavid.Hollister@Sun.COM 		return (B_FALSE);
85510696SDavid.Hollister@Sun.COM 	}
85610696SDavid.Hollister@Sun.COM 
85710696SDavid.Hollister@Sun.COM 	bzero(ptr, PMCS_MSG_SIZE << 2);	/* PMCS_MSG_SIZE is in dwords */
85810696SDavid.Hollister@Sun.COM 	iombp = (uint32_t *)&iomb;
85910696SDavid.Hollister@Sun.COM 	COPY_MESSAGE(ptr, iombp, sizeof (pmcs_set_nvmd_cmd_t) >> 2);
86010696SDavid.Hollister@Sun.COM 	workp->state = PMCS_WORK_STATE_ONCHIP;
86110696SDavid.Hollister@Sun.COM 	INC_IQ_ENTRY(pwp, ibq);
86210696SDavid.Hollister@Sun.COM 
86310696SDavid.Hollister@Sun.COM 	WAIT_FOR(workp, 2000, result);
86410696SDavid.Hollister@Sun.COM 
86510696SDavid.Hollister@Sun.COM 	if (result) {
86610696SDavid.Hollister@Sun.COM 		pmcs_timed_out(pwp, workp->htag, __func__);
86710696SDavid.Hollister@Sun.COM 		pmcs_pwork(pwp, workp);
86810696SDavid.Hollister@Sun.COM 		return (B_FALSE);
86910696SDavid.Hollister@Sun.COM 	}
87010696SDavid.Hollister@Sun.COM 
87110696SDavid.Hollister@Sun.COM 	pmcs_pwork(pwp, workp);
87210696SDavid.Hollister@Sun.COM 
87310696SDavid.Hollister@Sun.COM 	status = LE_32(*(ptr + 3)) & 0xffff;
87410696SDavid.Hollister@Sun.COM 	if (status != PMCS_NVMD_STAT_SUCCESS) {
87511048SDavid.Hollister@Sun.COM 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
87611048SDavid.Hollister@Sun.COM 		    "%s: Error, status = 0x%04x", __func__, status);
87710696SDavid.Hollister@Sun.COM 		return (B_FALSE);
87810696SDavid.Hollister@Sun.COM 	}
87910696SDavid.Hollister@Sun.COM 
88010696SDavid.Hollister@Sun.COM 	return (B_TRUE);
88110696SDavid.Hollister@Sun.COM }
882