14c06356bSdh /*
24c06356bSdh  * CDDL HEADER START
34c06356bSdh  *
44c06356bSdh  * The contents of this file are subject to the terms of the
54c06356bSdh  * Common Development and Distribution License (the "License").
64c06356bSdh  * You may not use this file except in compliance with the License.
74c06356bSdh  *
84c06356bSdh  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94c06356bSdh  * or http://www.opensolaris.org/os/licensing.
104c06356bSdh  * See the License for the specific language governing permissions
114c06356bSdh  * and limitations under the License.
124c06356bSdh  *
134c06356bSdh  * When distributing Covered Code, include this CDDL HEADER in each
144c06356bSdh  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154c06356bSdh  * If applicable, add the following below this CDDL HEADER, with the
164c06356bSdh  * fields enclosed by brackets "[]" replaced with your own identifying
174c06356bSdh  * information: Portions Copyright [yyyy] [name of copyright owner]
184c06356bSdh  *
194c06356bSdh  * CDDL HEADER END
20658280b6SDavid Hollister  */
21658280b6SDavid Hollister /*
22658280b6SDavid Hollister  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
234c06356bSdh  */
244c06356bSdh 
254c06356bSdh /*
264c06356bSdh  * This file contains various support routines.
274c06356bSdh  */
284c06356bSdh 
294c06356bSdh #include <sys/scsi/adapters/pmcs/pmcs.h>
304c06356bSdh 
314c06356bSdh /*
324c06356bSdh  * SAS Topology Configuration
334c06356bSdh  */
344c06356bSdh static int pmcs_flash_chunk(pmcs_hw_t *, uint8_t *);
354c06356bSdh 
364c06356bSdh /*
374c06356bSdh  * Check current firmware version for correctness
384c06356bSdh  * and try to flash the correct firmware if what is
394c06356bSdh  * running isn't correct.
404c06356bSdh  *
414c06356bSdh  * Must be called after setup and MPI setup and
424c06356bSdh  * interrupts are enabled.
434c06356bSdh  */
444c06356bSdh 
454c06356bSdh int
pmcs_firmware_update(pmcs_hw_t * pwp)464c06356bSdh pmcs_firmware_update(pmcs_hw_t *pwp)
474c06356bSdh {
484c06356bSdh 	ddi_modhandle_t modhp;
492ac4abe8SDavid Hollister 	char buf[64], *bufp;
504c06356bSdh 	int errno;
514c06356bSdh 	uint8_t *cstart, *cend;		/* Firmware image file */
524c06356bSdh 	uint8_t *istart, *iend; 	/* ila */
534c06356bSdh 	uint8_t *sstart, *send;		/* SPCBoot */
544c06356bSdh 	uint32_t *fwvp;
554c06356bSdh 	int defret = 0;
5683778f44SJesse Butler 	int first_pass = 1;
572ac4abe8SDavid Hollister 	long fw_version, ila_version;
582ac4abe8SDavid Hollister 	uint8_t *fw_verp, *ila_verp;
594c06356bSdh 
604c06356bSdh 	/*
614c06356bSdh 	 * If updating is disabled, we're done.
624c06356bSdh 	 */
634c06356bSdh 	if (pwp->fw_disable_update) {
64c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
654c06356bSdh 		    "Firmware update disabled by conf file");
664c06356bSdh 		return (0);
674c06356bSdh 	}
684c06356bSdh 
694c06356bSdh 	/*
704c06356bSdh 	 * If we're already running the right firmware, we're done.
714c06356bSdh 	 */
724c06356bSdh 	if (pwp->fw == PMCS_FIRMWARE_VERSION) {
734c06356bSdh 		if (pwp->fw_force_update == 0) {
744c06356bSdh 			return (0);
754c06356bSdh 		}
764c06356bSdh 
77c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
784c06356bSdh 		    "Firmware version matches, but still forcing update");
794c06356bSdh 	}
804c06356bSdh 
814c06356bSdh 	modhp = ddi_modopen(PMCS_FIRMWARE_FILENAME, KRTLD_MODE_FIRST, &errno);
824c06356bSdh 	if (errno) {
83c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
844c06356bSdh 		    "%s: Firmware module not available; will not upgrade",
854c06356bSdh 		    __func__);
864c06356bSdh 		return (defret);
874c06356bSdh 	}
884c06356bSdh 
894c06356bSdh 	fwvp = ddi_modsym(modhp, PMCS_FIRMWARE_VERSION_NAME, &errno);
904c06356bSdh 	if (errno) {
91c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
92c3bc407cSdh 		    "%s: unable to find symbol '%s'",
934c06356bSdh 		    __func__, PMCS_FIRMWARE_VERSION_NAME);
944c06356bSdh 		(void) ddi_modclose(modhp);
954c06356bSdh 		return (defret);
964c06356bSdh 	}
974c06356bSdh 
984c06356bSdh 	/*
994c06356bSdh 	 * If the firmware version from the module isn't what we expect,
1004c06356bSdh 	 * and force updating is disabled, return the default (for this
1014c06356bSdh 	 * mode of operation) value.
1024c06356bSdh 	 */
1034c06356bSdh 	if (*fwvp != PMCS_FIRMWARE_VERSION) {
1044c06356bSdh 		if (pwp->fw_force_update == 0) {
105c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1064c06356bSdh 			    "%s: firmware module version wrong (0x%x)",
1074c06356bSdh 			    __func__, *fwvp);
1084c06356bSdh 			(void) ddi_modclose(modhp);
1094c06356bSdh 			return (defret);
1104c06356bSdh 		}
111c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1124c06356bSdh 		    "%s: firmware module version wrong (0x%x) - update forced",
1134c06356bSdh 		    __func__, *fwvp);
1144c06356bSdh 	}
1154c06356bSdh 
1164c06356bSdh 	(void) snprintf(buf, sizeof (buf),
1174c06356bSdh 	    PMCS_FIRMWARE_CODE_NAME PMCS_FIRMWARE_START_SUF);
1184c06356bSdh 	cstart = ddi_modsym(modhp, buf, &errno);
1194c06356bSdh 	if (errno) {
120c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
121c3bc407cSdh 		    "%s: unable to find symbol '%s'", __func__, buf);
1224c06356bSdh 		(void) ddi_modclose(modhp);
1234c06356bSdh 		return (defret);
1244c06356bSdh 	}
1254c06356bSdh 
1264c06356bSdh 	(void) snprintf(buf, sizeof (buf),
1274c06356bSdh 	    PMCS_FIRMWARE_CODE_NAME PMCS_FIRMWARE_END_SUF);
1284c06356bSdh 	cend = ddi_modsym(modhp, buf, &errno);
1294c06356bSdh 	if (errno) {
130c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
131c3bc407cSdh 		    "%s: unable to find symbol '%s'", __func__, buf);
1324c06356bSdh 		(void) ddi_modclose(modhp);
1334c06356bSdh 		return (defret);
1344c06356bSdh 	}
1354c06356bSdh 
1364c06356bSdh 	(void) snprintf(buf, sizeof (buf),
1374c06356bSdh 	    PMCS_FIRMWARE_ILA_NAME PMCS_FIRMWARE_START_SUF);
1384c06356bSdh 	istart = ddi_modsym(modhp, buf, &errno);
1394c06356bSdh 	if (errno) {
140c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
141c3bc407cSdh 		    "%s: unable to find symbol '%s'", __func__, buf);
1424c06356bSdh 		(void) ddi_modclose(modhp);
1434c06356bSdh 		return (defret);
1444c06356bSdh 	}
1454c06356bSdh 
1464c06356bSdh 	(void) snprintf(buf, sizeof (buf),
1474c06356bSdh 	    PMCS_FIRMWARE_ILA_NAME PMCS_FIRMWARE_END_SUF);
1484c06356bSdh 	iend = ddi_modsym(modhp, buf, &errno);
1494c06356bSdh 	if (errno) {
150c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
151c3bc407cSdh 		    "%s: unable to find symbol '%s'", __func__, buf);
1524c06356bSdh 		(void) ddi_modclose(modhp);
1534c06356bSdh 		return (defret);
1544c06356bSdh 	}
1554c06356bSdh 
1564c06356bSdh 	(void) snprintf(buf, sizeof (buf),
1574c06356bSdh 	    PMCS_FIRMWARE_SPCBOOT_NAME PMCS_FIRMWARE_START_SUF);
1584c06356bSdh 	sstart = ddi_modsym(modhp, buf, &errno);
1594c06356bSdh 	if (errno) {
160c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
161c3bc407cSdh 		    "%s: unable to find symbol '%s'", __func__, buf);
1624c06356bSdh 		(void) ddi_modclose(modhp);
1634c06356bSdh 		return (defret);
1644c06356bSdh 	}
1654c06356bSdh 
1664c06356bSdh 	(void) snprintf(buf, sizeof (buf),
1674c06356bSdh 	    PMCS_FIRMWARE_SPCBOOT_NAME PMCS_FIRMWARE_END_SUF);
1684c06356bSdh 	send = ddi_modsym(modhp, buf, &errno);
1694c06356bSdh 	if (errno) {
170c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
171c3bc407cSdh 		    "%s: unable to find symbol '%s'", __func__, buf);
1724c06356bSdh 		(void) ddi_modclose(modhp);
1734c06356bSdh 		return (defret);
1744c06356bSdh 	}
1754c06356bSdh 
1762ac4abe8SDavid Hollister 	/*
1772ac4abe8SDavid Hollister 	 * Get the ILA and firmware versions from the modules themselves
1782ac4abe8SDavid Hollister 	 */
1792ac4abe8SDavid Hollister 	ila_verp = iend - PMCS_ILA_VER_OFFSET;
1802ac4abe8SDavid Hollister 	(void) ddi_strtol((const char *)ila_verp, &bufp, 16, &ila_version);
1812ac4abe8SDavid Hollister 	fw_verp = cend - PMCS_FW_VER_OFFSET;
1822ac4abe8SDavid Hollister 	(void) ddi_strtol((const char *)fw_verp, &bufp, 16, &fw_version);
1832ac4abe8SDavid Hollister 
1842ac4abe8SDavid Hollister 	/*
1852ac4abe8SDavid Hollister 	 * If force update is not set, verify that what we're loading is
1862ac4abe8SDavid Hollister 	 * what we expect.
1872ac4abe8SDavid Hollister 	 */
1882ac4abe8SDavid Hollister 	if (pwp->fw_force_update == 0) {
1892ac4abe8SDavid Hollister 		if (fw_version != PMCS_FIRMWARE_VERSION) {
1902ac4abe8SDavid Hollister 			pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
1912ac4abe8SDavid Hollister 			    "Expected fw version 0x%x, not 0x%lx: not "
1922ac4abe8SDavid Hollister 			    "updating", PMCS_FIRMWARE_VERSION, fw_version);
1932ac4abe8SDavid Hollister 			(void) ddi_modclose(modhp);
1942ac4abe8SDavid Hollister 			return (defret);
1952ac4abe8SDavid Hollister 		}
1962ac4abe8SDavid Hollister 	}
1972ac4abe8SDavid Hollister 
1982ac4abe8SDavid Hollister 	pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
1992ac4abe8SDavid Hollister 	    "Upgrading firmware on card from 0x%x to 0x%lx (ILA version 0x%lx)",
2002ac4abe8SDavid Hollister 	    pwp->fw, fw_version, ila_version);
2012ac4abe8SDavid Hollister 
2024c06356bSdh 	/*
2034c06356bSdh 	 * The SPCBoot image must be updated first, and this is written to
2044c06356bSdh 	 * SEEPROM, not flash.
2054c06356bSdh 	 */
2064c06356bSdh 	if (pmcs_set_nvmd(pwp, PMCS_NVMD_SPCBOOT, sstart,
2074c06356bSdh 	    (size_t)((size_t)send - (size_t)sstart)) == B_FALSE) {
208c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2094c06356bSdh 		    "%s: unable to flash '%s' segment",
2104c06356bSdh 		    __func__, PMCS_FIRMWARE_SPCBOOT_NAME);
2114c06356bSdh 		(void) ddi_modclose(modhp);
2124c06356bSdh 		return (-1);
2134c06356bSdh 	}
2144c06356bSdh 
21583778f44SJesse Butler repeat:
21683778f44SJesse Butler 	pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
21783778f44SJesse Butler 	    "%s: Beginning firmware update of %s image.",
21883778f44SJesse Butler 	    __func__, (first_pass ? "first" : "second"));
21983778f44SJesse Butler 
2204c06356bSdh 	if (pmcs_fw_flash(pwp, (void *)istart,
2214c06356bSdh 	    (uint32_t)((size_t)iend - (size_t)istart))) {
222c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2234c06356bSdh 		    "%s: unable to flash '%s' segment",
2244c06356bSdh 		    __func__, PMCS_FIRMWARE_ILA_NAME);
2254c06356bSdh 		(void) ddi_modclose(modhp);
2264c06356bSdh 		return (-1);
2274c06356bSdh 	}
2284c06356bSdh 
2294c06356bSdh 	if (pmcs_fw_flash(pwp, (void *)cstart,
2304c06356bSdh 	    (uint32_t)((size_t)cend - (size_t)cstart))) {
231c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2324c06356bSdh 		    "%s: unable to flash '%s' segment",
2334c06356bSdh 		    __func__, PMCS_FIRMWARE_CODE_NAME);
2344c06356bSdh 		(void) ddi_modclose(modhp);
2354c06356bSdh 		return (-1);
2364c06356bSdh 	}
2374c06356bSdh 
2384c06356bSdh 	if (pmcs_soft_reset(pwp, B_FALSE)) {
239c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2404c06356bSdh 		    "%s: soft reset after flash update failed", __func__);
24183778f44SJesse Butler 		(void) ddi_modclose(modhp);
2424c06356bSdh 		return (-1);
2434c06356bSdh 	} else {
24483778f44SJesse Butler 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
24583778f44SJesse Butler 		    "%s: %s image successfully upgraded.",
24683778f44SJesse Butler 		    __func__, (first_pass ? "First" : "Second"));
2475c45adf0SJesse Butler 		pwp->last_reset_reason = PMCS_LAST_RST_FW_UPGRADE;
2484c06356bSdh 	}
24983778f44SJesse Butler 
25083778f44SJesse Butler 	if (first_pass) {
25183778f44SJesse Butler 		first_pass = 0;
25283778f44SJesse Butler 		goto repeat;
25383778f44SJesse Butler 	}
25483778f44SJesse Butler 
25583778f44SJesse Butler 	pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
25683778f44SJesse Butler 	    "%s: Firmware successfully upgraded", __func__);
25783778f44SJesse Butler 
25883778f44SJesse Butler 	(void) ddi_modclose(modhp);
2594c06356bSdh 	return (0);
2604c06356bSdh }
2614c06356bSdh 
2624c06356bSdh /*
2634c06356bSdh  * Flash firmware support
2644c06356bSdh  * Called unlocked.
2654c06356bSdh  */
2664c06356bSdh int
pmcs_fw_flash(pmcs_hw_t * pwp,pmcs_fw_hdr_t * hdr,uint32_t length)2674c06356bSdh pmcs_fw_flash(pmcs_hw_t *pwp, pmcs_fw_hdr_t *hdr, uint32_t length)
2684c06356bSdh {
2694c06356bSdh 	pmcs_fw_hdr_t *hp;
2704c06356bSdh 	uint8_t *wrk, *base;
2714c06356bSdh 
2724c06356bSdh 	/*
2734c06356bSdh 	 * Step 1- Validate firmware chunks within passed pointer.
2744c06356bSdh 	 */
2754c06356bSdh 	hp = hdr;
2764c06356bSdh 	wrk = (uint8_t *)hdr;
2774c06356bSdh 	base = wrk;
2784c06356bSdh 	for (;;) {
279c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
2804c06356bSdh 		    "%s: partition 0x%x, Length 0x%x", __func__,
2814c06356bSdh 		    hp->destination_partition, ntohl(hp->firmware_length));
2824c06356bSdh 		if (ntohl(hp->firmware_length) == 0) {
283c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2844c06356bSdh 			    "%s: bad firmware length 0x%x",
2854c06356bSdh 			    __func__, ntohl(hp->firmware_length));
2864c06356bSdh 			return (EINVAL);
2874c06356bSdh 		}
2884c06356bSdh 		wrk += (sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length));
2894c06356bSdh 		if (wrk == base + length) {
2904c06356bSdh 			break;
2914c06356bSdh 		}
2924c06356bSdh 		if (wrk > base + length) {
293c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2944c06356bSdh 			    "%s: out of bounds firmware length", __func__);
2954c06356bSdh 			return (EINVAL);
2964c06356bSdh 		}
2974c06356bSdh 		hp = (void *)wrk;
2984c06356bSdh 	}
2994c06356bSdh 
3004c06356bSdh 	/*
3014c06356bSdh 	 * Step 2- acquire scratch
3024c06356bSdh 	 */
3034c06356bSdh 	(void) pmcs_acquire_scratch(pwp, B_TRUE);
3044c06356bSdh 
3054c06356bSdh 	/*
3064c06356bSdh 	 * Step 3- loop through firmware chunks and send each one
3074c06356bSdh 	 * down to be flashed.
3084c06356bSdh 	 */
3094c06356bSdh 	hp = hdr;
3104c06356bSdh 	wrk = (uint8_t *)hdr;
3114c06356bSdh 	base = wrk;
3124c06356bSdh 	for (;;) {
3134c06356bSdh 		if (pmcs_flash_chunk(pwp, wrk)) {
3144c06356bSdh 			pmcs_release_scratch(pwp);
3154c06356bSdh 			return (EIO);
3164c06356bSdh 		}
3174c06356bSdh 		wrk += (sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length));
3184c06356bSdh 		if (wrk == base + length) {
3194c06356bSdh 			break;
3204c06356bSdh 		}
3214c06356bSdh 		hp = (void *) wrk;
3224c06356bSdh 	}
3234c06356bSdh 	pmcs_release_scratch(pwp);
3244c06356bSdh 	return (0);
3254c06356bSdh }
3264c06356bSdh 
3274c06356bSdh static int
pmcs_flash_chunk(pmcs_hw_t * pwp,uint8_t * chunk)3284c06356bSdh pmcs_flash_chunk(pmcs_hw_t *pwp, uint8_t *chunk)
3294c06356bSdh {
3304c06356bSdh 	pmcs_fw_hdr_t *hp;
3314c06356bSdh 	pmcwork_t *pwrk;
3324c06356bSdh 	uint32_t len, seg, off, result, amt, msg[PMCS_MSG_SIZE], *ptr;
3334c06356bSdh 
3344c06356bSdh 	hp = (void *)chunk;
3354c06356bSdh 	len = sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length);
3364c06356bSdh 
3374c06356bSdh 	seg = off = 0;
3384c06356bSdh 	while (off < len) {
3394c06356bSdh 		amt = PMCS_SCRATCH_SIZE;
3404c06356bSdh 		if (off + amt > len) {
3414c06356bSdh 			amt = len - off;
3424c06356bSdh 		}
343c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
3444c06356bSdh 		    "%s: segment %d offset %u length %u",
3454c06356bSdh 		    __func__, seg, off, amt);
3464c06356bSdh 		(void) memcpy(pwp->scratch, &chunk[off], amt);
3474c06356bSdh 		pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
3484c06356bSdh 		if (pwrk == NULL) {
3494c06356bSdh 			return (ENOMEM);
3504c06356bSdh 		}
3514c06356bSdh 		pwrk->arg = msg;
3524c06356bSdh 		msg[0] = LE_32(PMCS_HIPRI(pwp,
3534c06356bSdh 		    PMCS_OQ_EVENTS, PMCIN_FW_FLASH_UPDATE));
3544c06356bSdh 		msg[1] = LE_32(pwrk->htag);
3554c06356bSdh 		msg[2] = LE_32(off);
3564c06356bSdh 		msg[3] = LE_32(amt);
3574c06356bSdh 		if (off == 0) {
3584c06356bSdh 			msg[4] = LE_32(len);
3594c06356bSdh 		} else {
3604c06356bSdh 			msg[4] = 0;
3614c06356bSdh 		}
3624c06356bSdh 		msg[5] = 0;
3634c06356bSdh 		msg[6] = 0;
3644c06356bSdh 		msg[7] = 0;
3654c06356bSdh 		msg[8] = 0;
3664c06356bSdh 		msg[9] = 0;
3674c06356bSdh 		msg[10] = 0;
3684c06356bSdh 		msg[11] = 0;
3694c06356bSdh 		msg[12] = LE_32(DWORD0(pwp->scratch_dma));
3704c06356bSdh 		msg[13] = LE_32(DWORD1(pwp->scratch_dma));
3714c06356bSdh 		msg[14] = LE_32(amt);
3724c06356bSdh 		msg[15] = 0;
3734c06356bSdh 		mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
3744c06356bSdh 		ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
3754c06356bSdh 		if (ptr == NULL) {
3764c06356bSdh 			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
3774c06356bSdh 			pmcs_pwork(pwp, pwrk);
378c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
379c3bc407cSdh 			    pmcs_nomsg, __func__);
3804c06356bSdh 			return (ENOMEM);
3814c06356bSdh 		}
3824c06356bSdh 		COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
3834c06356bSdh 		(void) memset(msg, 0xaf, sizeof (msg));
3844c06356bSdh 		pwrk->state = PMCS_WORK_STATE_ONCHIP;
3854c06356bSdh 		INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
3861f81b464SDavid Hollister 		WAIT_FOR(pwrk, PMCS_FLASH_WAIT_TIME, result);
3874c06356bSdh 		pmcs_pwork(pwp, pwrk);
3884c06356bSdh 		if (result) {
3896745c559SJesse Butler 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
390c3bc407cSdh 			    pmcs_timeo, __func__);
3914c06356bSdh 			return (EIO);
3924c06356bSdh 		}
3934c06356bSdh 		switch (LE_32(msg[2])) {
3944c06356bSdh 		case FLASH_UPDATE_COMPLETE_PENDING_REBOOT:
395c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
3964c06356bSdh 			    "%s: segment %d complete pending reboot",
3974c06356bSdh 			    __func__, seg);
3984c06356bSdh 			break;
3994c06356bSdh 		case FLASH_UPDATE_IN_PROGRESS:
400c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
4014c06356bSdh 			    "%s: segment %d downloaded", __func__, seg);
4024c06356bSdh 			break;
4034c06356bSdh 		case FLASH_UPDATE_HDR_ERR:
404c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
4054c06356bSdh 			    "%s: segment %d header error", __func__, seg);
4064c06356bSdh 			return (EIO);
4074c06356bSdh 		case FLASH_UPDATE_OFFSET_ERR:
408c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
4094c06356bSdh 			    "%s: segment %d offset error", __func__, seg);
4104c06356bSdh 			return (EIO);
4114c06356bSdh 		case FLASH_UPDATE_UPDATE_CRC_ERR:
412c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
4134c06356bSdh 			    "%s: segment %d update crc error", __func__, seg);
4144c06356bSdh 			return (EIO);
4154c06356bSdh 		case FLASH_UPDATE_LENGTH_ERR:
416c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
4174c06356bSdh 			    "%s: segment %d length error", __func__, seg);
4184c06356bSdh 			return (EIO);
4194c06356bSdh 		case FLASH_UPDATE_HW_ERR:
420c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
4214c06356bSdh 			    "%s: segment %d hw error", __func__, seg);
4224c06356bSdh 			return (EIO);
4234c06356bSdh 		case FLASH_UPDATE_DNLD_NOT_SUPPORTED:
424c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
4254c06356bSdh 			    "%s: segment %d download not supported error",
4264c06356bSdh 			    __func__, seg);
4274c06356bSdh 			return (EIO);
4284c06356bSdh 		case FLASH_UPDATE_DISABLED:
429c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
4304c06356bSdh 			    "%s: segment %d update disabled error",
4314c06356bSdh 			    __func__, seg);
4324c06356bSdh 			return (EIO);
4334c06356bSdh 		default:
434c3bc407cSdh 			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
4354c06356bSdh 			    "%s: segment %d unknown error %x",
4364c06356bSdh 			    __func__, seg, msg[2]);
4374c06356bSdh 			return (EIO);
4384c06356bSdh 		}
4394c06356bSdh 		off += amt;
4404c06356bSdh 		seg++;
4414c06356bSdh 	}
4424c06356bSdh 	return (0);
4434c06356bSdh }
4444c06356bSdh 
4454c06356bSdh /*
4464c06356bSdh  * pmcs_validate_vpd
4474c06356bSdh  *
4484c06356bSdh  * Input: softstate pointer and pointer to vpd data buffer
4494c06356bSdh  * Returns: B_TRUE if VPD data looks OK, B_FALSE otherwise
4504c06356bSdh  */
4514c06356bSdh static boolean_t
pmcs_validate_vpd(pmcs_hw_t * pwp,uint8_t * data)4524c06356bSdh pmcs_validate_vpd(pmcs_hw_t *pwp, uint8_t *data)
4534c06356bSdh {
4544c06356bSdh 	pmcs_vpd_header_t *vpd_header;
4554c06356bSdh 	uint8_t *bufp, kv_len, *chksump, chksum = 0;
4564c06356bSdh 	char tbuf[80];
4574c06356bSdh 	char prop[24];
4584c06356bSdh 	int idx, str_len;
4594c06356bSdh 	uint16_t strid_length, chksum_len;
4604c06356bSdh 	uint64_t wwid;
4614c06356bSdh 	pmcs_vpd_kv_t *vkvp;
4624c06356bSdh 
4634c06356bSdh 	vpd_header = (pmcs_vpd_header_t *)data;
4644c06356bSdh 
4654c06356bSdh 	/*
4664c06356bSdh 	 * Make sure we understand the format of this data
4674c06356bSdh 	 */
4684c06356bSdh 
469*688a63edSSrikanth Suravajhala 	/*
470*688a63edSSrikanth Suravajhala 	 * Only VPD version 1 is VALID for Thebe-INT cards and
471*688a63edSSrikanth Suravajhala 	 * Only VPD version 2 is valid for Thebe-EXT cards
472*688a63edSSrikanth Suravajhala 	 */
473*688a63edSSrikanth Suravajhala 	if ((vpd_header->eeprom_version == PMCS_EEPROM_INT_VERSION &&
474*688a63edSSrikanth Suravajhala 	    vpd_header->subsys_pid[0] == PMCS_EEPROM_INT_SSID_BYTE1 &&
475*688a63edSSrikanth Suravajhala 	    vpd_header->subsys_pid[1] == PMCS_EEPROM_INT_SSID_BYTE2) ||
476*688a63edSSrikanth Suravajhala 	    (vpd_header->eeprom_version == PMCS_EEPROM_EXT_VERSION &&
477*688a63edSSrikanth Suravajhala 	    vpd_header->subsys_pid[0] == PMCS_EEPROM_EXT_SSID_BYTE1 &&
478*688a63edSSrikanth Suravajhala 	    vpd_header->subsys_pid[1] == PMCS_EEPROM_EXT_SSID_BYTE2)) {
479*688a63edSSrikanth Suravajhala 			goto valid_version;
480*688a63edSSrikanth Suravajhala 	} else {
481*688a63edSSrikanth Suravajhala 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
482*688a63edSSrikanth Suravajhala 		    "%s: Detected Thebe card with SSID(%02x%02x)", __func__,
483*688a63edSSrikanth Suravajhala 		    vpd_header->subsys_pid[0], vpd_header->subsys_pid[1]);
484c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
485*688a63edSSrikanth Suravajhala 		    "%s: EEPROM(%d) unsupported; requires %d for INT(%02x%02x) "
486*688a63edSSrikanth Suravajhala 		    " and %d for EXT(%02x%02x) cards.", __func__,
487*688a63edSSrikanth Suravajhala 		    vpd_header->eeprom_version,
488*688a63edSSrikanth Suravajhala 		    PMCS_EEPROM_INT_VERSION, PMCS_EEPROM_INT_SSID_BYTE1,
489*688a63edSSrikanth Suravajhala 		    PMCS_EEPROM_INT_SSID_BYTE2, PMCS_EEPROM_EXT_VERSION,
490*688a63edSSrikanth Suravajhala 		    PMCS_EEPROM_EXT_SSID_BYTE1, PMCS_EEPROM_EXT_SSID_BYTE2);
4914c06356bSdh 		return (B_FALSE);
4924c06356bSdh 	}
4934c06356bSdh 
494*688a63edSSrikanth Suravajhala valid_version:
4954c06356bSdh 	/*
4964c06356bSdh 	 * Do we have a valid SAS WWID?
4974c06356bSdh 	 */
4984c06356bSdh 	if (((vpd_header->hba_sas_wwid[0] & 0xf0) >> 4) != NAA_IEEE_REG) {
499c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
5004c06356bSdh 		    "%s: SAS WWN has invalid NAA (%d)", __func__,
5014c06356bSdh 		    ((vpd_header->hba_sas_wwid[0] & 0xf0) >> 4));
5024c06356bSdh 		return (B_FALSE);
5034c06356bSdh 	}
5044c06356bSdh 	wwid = pmcs_barray2wwn(vpd_header->hba_sas_wwid);
5054c06356bSdh 	for (idx = 0; idx < PMCS_MAX_PORTS; idx++) {
5064c06356bSdh 		pwp->sas_wwns[idx] = wwid + idx;
5074c06356bSdh 	}
5084c06356bSdh 
5094c06356bSdh 	if (vpd_header->vpd_start_byte != PMCS_VPD_START) {
510c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
5114c06356bSdh 		    "%s: Didn't see VPD start byte", __func__);
5124c06356bSdh 		return (B_FALSE);
5134c06356bSdh 	}
5144c06356bSdh 
5154c06356bSdh 	/*
5164c06356bSdh 	 * We only checksum the VPD data between (and including) VPD Start byte
5174c06356bSdh 	 * and the checksum value byte. The length of this data for CRC is
5184c06356bSdh 	 * 15 less than the length indicated in vpd_length field of the header.
5194c06356bSdh 	 * 8 (SAS WWN) + 2 (subsystem ID) + 2 (subsystem vendor ID) +
5204c06356bSdh 	 * 1 (end tag) + 2 (hex byte CRC, different from this one) = 15 bytes
5214c06356bSdh 	 */
5224c06356bSdh 	/*
5234c06356bSdh 	 * VPD length (little endian format) is represented as byte-array field
5244c06356bSdh 	 * & read the following way to avoid alignment issues (in SPARC)
5254c06356bSdh 	 */
5264c06356bSdh 	chksum_len = ((vpd_header->vpd_length[1] << 8) |
5274c06356bSdh 	    (vpd_header->vpd_length[0])) - 15;
5284c06356bSdh 	/* Validate VPD data checksum */
5294c06356bSdh 	chksump = (uint8_t *)&vpd_header->vpd_start_byte;
5304c06356bSdh 	ASSERT (*chksump == PMCS_VPD_START);
5314c06356bSdh 	for (idx = 0; idx < chksum_len; idx++, chksump++) {
5324c06356bSdh 		chksum += *chksump;
5334c06356bSdh 	}
5344c06356bSdh 	ASSERT (*chksump == PMCS_VPD_END);
5354c06356bSdh 	if (chksum) {
536658280b6SDavid Hollister 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
537658280b6SDavid Hollister 		    "%s: VPD checksum failure", __func__);
538658280b6SDavid Hollister 		return (B_FALSE);
5394c06356bSdh 	}
5404c06356bSdh 
5414c06356bSdh 	/*
5424c06356bSdh 	 * Get length of string ID tag and read it.
5434c06356bSdh 	 */
5444c06356bSdh 	bufp = (uint8_t *)&vpd_header->vpd_start_byte;
5454c06356bSdh 	bufp += 3;		/* Skip the start byte and length */
5464c06356bSdh 	/*
5474c06356bSdh 	 * String ID tag length (little endian format) is represented as
5484c06356bSdh 	 * byte-array & read the following way to avoid alignment issues
5494c06356bSdh 	 * (in SPARC)
5504c06356bSdh 	 */
5514c06356bSdh 	strid_length = (vpd_header->strid_length[1] << 8) |
5524c06356bSdh 	    (vpd_header->strid_length[0]);
5534c06356bSdh 	if (strid_length > 79) {
5544c06356bSdh 		strid_length = 79;
5554c06356bSdh 	}
5564c06356bSdh 	bcopy(bufp, tbuf, strid_length);
5574c06356bSdh 	tbuf[strid_length] = 0;
5584c06356bSdh 
559c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
5604c06356bSdh 	    "%s: Product Name: '%s'", __func__, tbuf);
5614c06356bSdh 	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_MODEL_NAME, tbuf);
5624c06356bSdh 
5634c06356bSdh 	/*
5644c06356bSdh 	 * Skip VPD-R tag and length of read-only tag, then start reading
5654c06356bSdh 	 * keyword/value pairs
5664c06356bSdh 	 */
5674c06356bSdh 	bufp += strid_length;	/* Skip to VPD-R tag */
5684c06356bSdh 	bufp += 3;		/* Skip VPD-R tag and length of VPD-R data */
5694c06356bSdh 
5704c06356bSdh 	vkvp = (pmcs_vpd_kv_t *)bufp;
5714c06356bSdh 
5724c06356bSdh 	while (vkvp->keyword[0] != PMCS_VPD_END) {
5734c06356bSdh 		tbuf[0] = 0;
5744c06356bSdh 		str_len = snprintf(tbuf, 80, "VPD: %c%c = <",
5754c06356bSdh 		    vkvp->keyword[0], vkvp->keyword[1]);
5764c06356bSdh 
5774c06356bSdh 		kv_len = vkvp->value_length;
5784c06356bSdh 		for (idx = 0; idx < kv_len; idx++) {
5794c06356bSdh 			tbuf[str_len + idx] = vkvp->value[idx];
5804c06356bSdh 			prop[idx] = vkvp->value[idx];
5814c06356bSdh 		}
5824c06356bSdh 		prop[idx] = '\0';
5834c06356bSdh 		str_len += kv_len;
5844c06356bSdh 		tbuf[str_len] = '>';
5854c06356bSdh 		tbuf[str_len + 1] = 0;
586c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "%s (Len: 0x%x)",
587c3bc407cSdh 		    tbuf, kv_len);
5884c06356bSdh 
5894c06356bSdh 		/* Keyword is Manufacturer */
5904c06356bSdh 		if ((vkvp->keyword[0] == 'M') && (vkvp->keyword[1] == 'N')) {
5914c06356bSdh 			pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING,
5924c06356bSdh 			    PMCS_MANUFACTURER, prop);
5934c06356bSdh 		}
5944c06356bSdh 		/* Keyword is Serial Number */
5954c06356bSdh 		if ((vkvp->keyword[0] == 'S') && (vkvp->keyword[1] == 'N')) {
5964c06356bSdh 			pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING,
5974c06356bSdh 			    PMCS_SERIAL_NUMBER, prop);
5984c06356bSdh 		}
5994c06356bSdh 
6004c06356bSdh 		vkvp = (pmcs_vpd_kv_t *)(bufp + 3 + kv_len);
6014c06356bSdh 		bufp += kv_len + 3;
6024c06356bSdh 	}
6034c06356bSdh 
6044c06356bSdh 	return (B_TRUE);
6054c06356bSdh }
6064c06356bSdh 
6074c06356bSdh /*
6084c06356bSdh  * pmcs_get_nvmd
6094c06356bSdh  *
6104c06356bSdh  * This function will read the requested data from the non-volatile
6114c06356bSdh  * storage on the card.  This could mean SEEPROM, VPD, or other areas
6124c06356bSdh  * as defined by the PM8001 programmer's manual.
6134c06356bSdh  *
6144c06356bSdh  * nvmd_type: The data type being requested
6154c06356bSdh  * nvmd: NVM device to access (IOP/AAP1)
6164c06356bSdh  * offset: Must be 4K alignment
6174c06356bSdh  * buf: Pointer to memory region for retrieved data
6184c06356bSdh  * size_left: Total available bytes left in buf
6194c06356bSdh  *
6204c06356bSdh  * Returns: non-negative on success, -1 on failure
6214c06356bSdh  */
6224c06356bSdh 
6234c06356bSdh /*ARGSUSED*/
6244c06356bSdh 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)6254c06356bSdh pmcs_get_nvmd(pmcs_hw_t *pwp, pmcs_nvmd_type_t nvmd_type, uint8_t nvmd,
6264c06356bSdh     uint32_t offset, char *buf, uint32_t size_left)
6274c06356bSdh {
6284c06356bSdh 	pmcs_get_nvmd_cmd_t iomb;
6294c06356bSdh 	pmcwork_t *workp;
6304c06356bSdh 	uint8_t *chunkp;
6314c06356bSdh 	uint32_t *ptr, ibq, *iombp;
6324c06356bSdh 	uint32_t dlen;
6334c06356bSdh 	uint16_t status;
6344c06356bSdh 	uint8_t tdas_nvmd, ip, tda, tbn_tdps;
6354c06356bSdh 	uint8_t	doa[3];
6364c06356bSdh 	int32_t result = -1, i = 0;
6374c06356bSdh 
6384c06356bSdh 	switch (nvmd_type) {
6394c06356bSdh 	case PMCS_NVMD_VPD:
6404c06356bSdh 		tdas_nvmd = PMCIN_NVMD_TDPS_1 | PMCIN_NVMD_TWI;
6414c06356bSdh 		tda = PMCIN_TDA_PAGE(2);
6424c06356bSdh 		tbn_tdps = PMCIN_NVMD_TBN(0) | PMCIN_NVMD_TDPS_8;
6434c06356bSdh 		ip = PMCIN_NVMD_INDIRECT_PLD;
6444c06356bSdh 		dlen = LE_32(PMCS_SEEPROM_PAGE_SIZE);
6454c06356bSdh 		doa[0] = 0;
6464c06356bSdh 		doa[1] = 0;
6474c06356bSdh 		doa[2] = 0;
6484c06356bSdh 		break;
6494c06356bSdh 	case PMCS_NVMD_REG_DUMP:
6504c06356bSdh 		tdas_nvmd = nvmd;
6514c06356bSdh 		tda = 0;
6524c06356bSdh 		tbn_tdps = 0;
6534c06356bSdh 		ip = PMCIN_NVMD_INDIRECT_PLD;
6544c06356bSdh 		dlen = LE_32(PMCS_REGISTER_DUMP_BLOCK_SIZE);
6554c06356bSdh 		doa[0] = offset & 0xff;
6564c06356bSdh 		doa[1] = (offset >> 8) & 0xff;
6574c06356bSdh 		doa[2] = (offset >> 16) & 0xff;
6584c06356bSdh 		break;
6594c06356bSdh 	case PMCS_NVMD_EVENT_LOG:
6604c06356bSdh 		tdas_nvmd = nvmd;
6614c06356bSdh 		tda = 0;
6624c06356bSdh 		tbn_tdps = 0;
6634c06356bSdh 		ip = PMCIN_NVMD_INDIRECT_PLD;
6644c06356bSdh 		dlen = LE_32(PMCS_REGISTER_DUMP_BLOCK_SIZE);
6654c06356bSdh 		offset = offset + PMCS_NVMD_EVENT_LOG_OFFSET;
6664c06356bSdh 		doa[0] = offset & 0xff;
6674c06356bSdh 		doa[1] = (offset >> 8) & 0xff;
6684c06356bSdh 		doa[2] = (offset >> 16) & 0xff;
6694c06356bSdh 		break;
6704c06356bSdh 	default:
671c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6724c06356bSdh 		    "%s: Invalid nvmd type: %d", __func__, nvmd_type);
6734c06356bSdh 		return (-1);
6744c06356bSdh 	}
6754c06356bSdh 
6764c06356bSdh 	workp = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
6774c06356bSdh 	if (workp == NULL) {
678c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
6794c06356bSdh 		    "%s: Unable to get work struct", __func__);
6804c06356bSdh 		return (-1);
6814c06356bSdh 	}
6824c06356bSdh 
6834c06356bSdh 	ptr = &iomb.header;
6844c06356bSdh 	bzero(ptr, sizeof (pmcs_get_nvmd_cmd_t));
6854c06356bSdh 	*ptr = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_GENERAL, PMCIN_GET_NVMD_DATA));
6864c06356bSdh 	workp->arg = (void *)&iomb;
6874c06356bSdh 	iomb.htag = LE_32(workp->htag);
6884c06356bSdh 	iomb.ip = ip;
6894c06356bSdh 	iomb.tbn_tdps = tbn_tdps;
6904c06356bSdh 	iomb.tda = tda;
6914c06356bSdh 	iomb.tdas_nvmd = tdas_nvmd;
6924c06356bSdh 	iomb.ipbal = LE_32(DWORD0(pwp->flash_chunk_addr));
6934c06356bSdh 	iomb.ipbah = LE_32(DWORD1(pwp->flash_chunk_addr));
6944c06356bSdh 	iomb.ipdl = dlen;
6954c06356bSdh 	iomb.doa[0] = doa[0];
6964c06356bSdh 	iomb.doa[1] = doa[1];
6974c06356bSdh 	iomb.doa[2] = doa[2];
6984c06356bSdh 
6994c06356bSdh 	/*
7004c06356bSdh 	 * ptr will now point to the inbound queue message
7014c06356bSdh 	 */
7024c06356bSdh 	GET_IO_IQ_ENTRY(pwp, ptr, 0, ibq);
7034c06356bSdh 	if (ptr == NULL) {
704c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
705c3bc407cSdh 		    "!%s: Unable to get IQ entry", __func__);
7064c06356bSdh 		pmcs_pwork(pwp, workp);
7074c06356bSdh 		return (-1);
7084c06356bSdh 	}
7094c06356bSdh 
7104c06356bSdh 	bzero(ptr, PMCS_MSG_SIZE << 2);	/* PMCS_MSG_SIZE is in dwords */
7114c06356bSdh 	iombp = (uint32_t *)&iomb;
7124c06356bSdh 	COPY_MESSAGE(ptr, iombp, sizeof (pmcs_get_nvmd_cmd_t) >> 2);
7134c06356bSdh 	workp->state = PMCS_WORK_STATE_ONCHIP;
7144c06356bSdh 	INC_IQ_ENTRY(pwp, ibq);
7154c06356bSdh 
7164c06356bSdh 	WAIT_FOR(workp, 1000, result);
7174c06356bSdh 	ptr = workp->arg;
7184c06356bSdh 	if (result) {
7194c06356bSdh 		pmcs_timed_out(pwp, workp->htag, __func__);
7204c06356bSdh 		pmcs_pwork(pwp, workp);
7214c06356bSdh 		return (-1);
7224c06356bSdh 	}
7234c06356bSdh 	status = LE_32(*(ptr + 3)) & 0xffff;
7244c06356bSdh 	if (status != PMCS_NVMD_STAT_SUCCESS) {
725c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
726c3bc407cSdh 		    "%s: Error, status = 0x%04x", __func__, status);
7274c06356bSdh 		pmcs_pwork(pwp, workp);
7284c06356bSdh 		return (-1);
7294c06356bSdh 	}
7304c06356bSdh 
7314c06356bSdh 	pmcs_pwork(pwp, workp);
7324c06356bSdh 
7334c06356bSdh 	if (ddi_dma_sync(pwp->cip_handles, 0, 0,
7344c06356bSdh 	    DDI_DMA_SYNC_FORKERNEL) != DDI_SUCCESS) {
735c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
736c3bc407cSdh 		    "Condition check failed at %s():%d", __func__, __LINE__);
7374c06356bSdh 	}
7384c06356bSdh 	chunkp = (uint8_t *)pwp->flash_chunkp;
7394c06356bSdh 
7404c06356bSdh 	switch (nvmd) {
7414c06356bSdh 	case PMCIN_NVMD_VPD:
7424c06356bSdh 		if (pmcs_validate_vpd(pwp, chunkp)) {
7434c06356bSdh 			result = 0;
7444c06356bSdh 		} else {
7454c06356bSdh 			result = -1;
7464c06356bSdh 		}
7474c06356bSdh 		break;
7484c06356bSdh 	case PMCIN_NVMD_AAP1:
7494c06356bSdh 	case PMCIN_NVMD_IOP:
7504c06356bSdh 		ASSERT(buf);
7514c06356bSdh 		i = 0;
7524c06356bSdh 		if (nvmd_type == PMCS_NVMD_REG_DUMP) {
7534c06356bSdh 			while ((i < PMCS_FLASH_CHUNK_SIZE) &&
7544c06356bSdh 			    (chunkp[i] != 0xff) && (chunkp[i] != '\0')) {
7554c06356bSdh 				(void) snprintf(&buf[i], (size_left - i),
7564c06356bSdh 				    "%c", chunkp[i]);
7574c06356bSdh 				i++;
7584c06356bSdh 			}
7594c06356bSdh 		} else if (nvmd_type == PMCS_NVMD_EVENT_LOG) {
7604c06356bSdh 			i = pmcs_dump_binary(pwp, pwp->flash_chunkp, 0,
7614c06356bSdh 			    (PMCS_FLASH_CHUNK_SIZE >> 2), buf, size_left);
7624c06356bSdh 		}
7634c06356bSdh 		result = i;
7644c06356bSdh 		break;
7654c06356bSdh 	default:
766c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
767c3bc407cSdh 		    "UNKNOWN NVMD DEVICE");
7684c06356bSdh 		return (-1);
7694c06356bSdh 	}
7704c06356bSdh 
7714c06356bSdh 	return (result);
7724c06356bSdh }
7734c06356bSdh 
7744c06356bSdh /*
7754c06356bSdh  * pmcs_set_nvmd
7764c06356bSdh  *
7774c06356bSdh  * This function will write the requested data to non-volatile storage
7784c06356bSdh  * on the HBA.  This could mean SEEPROM, VPD, or other areas as defined by
7794c06356bSdh  * the PM8001 programmer's manual.
7804c06356bSdh  *
7814c06356bSdh  * nvmd_type: The data type to be written
7824c06356bSdh  * buf: Pointer to memory region for data to write
7834c06356bSdh  * len: Length of the data buffer
7844c06356bSdh  *
7854c06356bSdh  * Returns: B_TRUE on success, B_FALSE on failure
7864c06356bSdh  */
7874c06356bSdh 
7884c06356bSdh boolean_t
pmcs_set_nvmd(pmcs_hw_t * pwp,pmcs_nvmd_type_t nvmd_type,uint8_t * buf,size_t len)7894c06356bSdh pmcs_set_nvmd(pmcs_hw_t *pwp, pmcs_nvmd_type_t nvmd_type, uint8_t *buf,
7904c06356bSdh     size_t len)
7914c06356bSdh {
7924c06356bSdh 	pmcs_set_nvmd_cmd_t iomb;
7934c06356bSdh 	pmcwork_t *workp;
7944c06356bSdh 	uint32_t *ptr, ibq, *iombp;
7954c06356bSdh 	uint32_t dlen;
7964c06356bSdh 	uint16_t status;
7974c06356bSdh 	uint8_t tdas_nvmd, ip;
7984c06356bSdh 	int result;
7994c06356bSdh 
8004c06356bSdh 	switch (nvmd_type) {
8014c06356bSdh 	case PMCS_NVMD_SPCBOOT:
8024c06356bSdh 		tdas_nvmd = PMCIN_NVMD_SEEPROM;
8034c06356bSdh 		ip = PMCIN_NVMD_INDIRECT_PLD;
8044c06356bSdh 		ASSERT((len >= PMCS_SPCBOOT_MIN_SIZE) &&
8054c06356bSdh 		    (len <= PMCS_SPCBOOT_MAX_SIZE));
8064c06356bSdh 		dlen = LE_32(len);
8074c06356bSdh 		break;
8084c06356bSdh 	default:
809c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8104c06356bSdh 		    "%s: Invalid nvmd type: %d", __func__, nvmd_type);
8114c06356bSdh 		return (B_FALSE);
8124c06356bSdh 	}
8134c06356bSdh 
814c3bc407cSdh 	pmcs_prt(pwp, PMCS_PRT_DEBUG_DEVEL, NULL, NULL,
815c3bc407cSdh 	    "%s: Request for nvmd type: %d", __func__, nvmd_type);
8164c06356bSdh 
8174c06356bSdh 	workp = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
8184c06356bSdh 	if (workp == NULL) {
819c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
8204c06356bSdh 		    "%s: Unable to get work struct", __func__);
8214c06356bSdh 		return (B_FALSE);
8224c06356bSdh 	}
8234c06356bSdh 
8244c06356bSdh 	ptr = &iomb.header;
8254c06356bSdh 	bzero(ptr, sizeof (pmcs_set_nvmd_cmd_t));
8264c06356bSdh 	*ptr = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_GENERAL, PMCIN_SET_NVMD_DATA));
8274c06356bSdh 	workp->arg = (void *)&iomb;
8284c06356bSdh 	iomb.htag = LE_32(workp->htag);
8294c06356bSdh 	iomb.ip = ip;
8304c06356bSdh 	iomb.tdas_nvmd = tdas_nvmd;
8314c06356bSdh 	iomb.signature = LE_32(PMCS_SEEPROM_SIGNATURE);
8324c06356bSdh 	iomb.ipbal = LE_32(DWORD0(pwp->flash_chunk_addr));
8334c06356bSdh 	iomb.ipbah = LE_32(DWORD1(pwp->flash_chunk_addr));
8344c06356bSdh 	iomb.ipdl = dlen;
8354c06356bSdh 
8364c06356bSdh 	pmcs_print_entry(pwp, PMCS_PRT_DEBUG_DEVEL,
8374c06356bSdh 	    "PMCIN_SET_NVMD_DATA iomb", (void *)&iomb);
8384c06356bSdh 
8394c06356bSdh 	bcopy(buf, pwp->flash_chunkp, len);
8404c06356bSdh 	if (ddi_dma_sync(pwp->cip_handles, 0, 0,
8414c06356bSdh 	    DDI_DMA_SYNC_FORDEV) != DDI_SUCCESS) {
842c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
843c3bc407cSdh 		    "Condition check failed at %s():%d", __func__, __LINE__);
8444c06356bSdh 	}
8454c06356bSdh 
8464c06356bSdh 	/*
8474c06356bSdh 	 * ptr will now point to the inbound queue message
8484c06356bSdh 	 */
8494c06356bSdh 	GET_IO_IQ_ENTRY(pwp, ptr, 0, ibq);
8504c06356bSdh 	if (ptr == NULL) {
851c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
852c3bc407cSdh 		    "!%s: Unable to get IQ entry", __func__);
8534c06356bSdh 		pmcs_pwork(pwp, workp);
8544c06356bSdh 		return (B_FALSE);
8554c06356bSdh 	}
8564c06356bSdh 
8574c06356bSdh 	bzero(ptr, PMCS_MSG_SIZE << 2);	/* PMCS_MSG_SIZE is in dwords */
8584c06356bSdh 	iombp = (uint32_t *)&iomb;
8594c06356bSdh 	COPY_MESSAGE(ptr, iombp, sizeof (pmcs_set_nvmd_cmd_t) >> 2);
8604c06356bSdh 	workp->state = PMCS_WORK_STATE_ONCHIP;
8614c06356bSdh 	INC_IQ_ENTRY(pwp, ibq);
8624c06356bSdh 
8634c06356bSdh 	WAIT_FOR(workp, 2000, result);
8644c06356bSdh 
8654c06356bSdh 	if (result) {
8664c06356bSdh 		pmcs_timed_out(pwp, workp->htag, __func__);
8674c06356bSdh 		pmcs_pwork(pwp, workp);
8684c06356bSdh 		return (B_FALSE);
8694c06356bSdh 	}
8704c06356bSdh 
8714c06356bSdh 	pmcs_pwork(pwp, workp);
8724c06356bSdh 
8734c06356bSdh 	status = LE_32(*(ptr + 3)) & 0xffff;
8744c06356bSdh 	if (status != PMCS_NVMD_STAT_SUCCESS) {
875c3bc407cSdh 		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
876c3bc407cSdh 		    "%s: Error, status = 0x%04x", __func__, status);
8774c06356bSdh 		return (B_FALSE);
8784c06356bSdh 	}
8794c06356bSdh 
8804c06356bSdh 	return (B_TRUE);
8814c06356bSdh }
882