1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  *
21  *
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * This file contains various support routines.
28  */
29 
30 #include <sys/scsi/adapters/pmcs/pmcs.h>
31 
32 /*
33  * SAS Topology Configuration
34  */
35 static int pmcs_flash_chunk(pmcs_hw_t *, uint8_t *);
36 
37 /*
38  * Check current firmware version for correctness
39  * and try to flash the correct firmware if what is
40  * running isn't correct.
41  *
42  * Must be called after setup and MPI setup and
43  * interrupts are enabled.
44  */
45 
46 int
47 pmcs_firmware_update(pmcs_hw_t *pwp)
48 {
49 	ddi_modhandle_t modhp;
50 	char buf[64];
51 	int errno;
52 	uint8_t *cstart, *cend;		/* Firmware image file */
53 	uint8_t *istart, *iend; 	/* ila */
54 	uint8_t *sstart, *send;		/* SPCBoot */
55 	uint32_t *fwvp;
56 	int defret = 0;
57 
58 	/*
59 	 * If updating is disabled, we're done.
60 	 */
61 	if (pwp->fw_disable_update) {
62 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
63 		    "Firmware update disabled by conf file");
64 		return (0);
65 	}
66 
67 	/*
68 	 * If we're already running the right firmware, we're done.
69 	 */
70 	if (pwp->fw == PMCS_FIRMWARE_VERSION) {
71 		if (pwp->fw_force_update == 0) {
72 			return (0);
73 		}
74 
75 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
76 		    "Firmware version matches, but still forcing update");
77 	} else {
78 		pmcs_prt(pwp, PMCS_PRT_WARN,
79 		    "Upgrading firmware on card from 0x%x to 0x%x",
80 		    pwp->fw, PMCS_FIRMWARE_VERSION);
81 	}
82 
83 	modhp = ddi_modopen(PMCS_FIRMWARE_FILENAME, KRTLD_MODE_FIRST, &errno);
84 	if (errno) {
85 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
86 		    "%s: Firmware module not available; will not upgrade",
87 		    __func__);
88 		return (defret);
89 	}
90 
91 	fwvp = ddi_modsym(modhp, PMCS_FIRMWARE_VERSION_NAME, &errno);
92 	if (errno) {
93 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: unable to find symbol '%s'",
94 		    __func__, PMCS_FIRMWARE_VERSION_NAME);
95 		(void) ddi_modclose(modhp);
96 		return (defret);
97 	}
98 
99 	/*
100 	 * If the firmware version from the module isn't what we expect,
101 	 * and force updating is disabled, return the default (for this
102 	 * mode of operation) value.
103 	 */
104 	if (*fwvp != PMCS_FIRMWARE_VERSION) {
105 		if (pwp->fw_force_update == 0) {
106 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
107 			    "%s: firmware module version wrong (0x%x)",
108 			    __func__, *fwvp);
109 			(void) ddi_modclose(modhp);
110 			return (defret);
111 		}
112 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
113 		    "%s: firmware module version wrong (0x%x) - update forced",
114 		    __func__, *fwvp);
115 	}
116 
117 	(void) snprintf(buf, sizeof (buf),
118 	    PMCS_FIRMWARE_CODE_NAME PMCS_FIRMWARE_START_SUF);
119 	cstart = ddi_modsym(modhp, buf, &errno);
120 	if (errno) {
121 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: unable to find symbol '%s'",
122 		    __func__, buf);
123 		(void) ddi_modclose(modhp);
124 		return (defret);
125 	}
126 
127 	(void) snprintf(buf, sizeof (buf),
128 	    PMCS_FIRMWARE_CODE_NAME PMCS_FIRMWARE_END_SUF);
129 	cend = ddi_modsym(modhp, buf, &errno);
130 	if (errno) {
131 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: unable to find symbol '%s'",
132 		    __func__, buf);
133 		(void) ddi_modclose(modhp);
134 		return (defret);
135 	}
136 
137 	(void) snprintf(buf, sizeof (buf),
138 	    PMCS_FIRMWARE_ILA_NAME PMCS_FIRMWARE_START_SUF);
139 	istart = ddi_modsym(modhp, buf, &errno);
140 	if (errno) {
141 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: unable to find symbol '%s'",
142 		    __func__, buf);
143 		(void) ddi_modclose(modhp);
144 		return (defret);
145 	}
146 
147 	(void) snprintf(buf, sizeof (buf),
148 	    PMCS_FIRMWARE_ILA_NAME PMCS_FIRMWARE_END_SUF);
149 	iend = ddi_modsym(modhp, buf, &errno);
150 	if (errno) {
151 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: unable to find symbol '%s'",
152 		    __func__, buf);
153 		(void) ddi_modclose(modhp);
154 		return (defret);
155 	}
156 
157 	(void) snprintf(buf, sizeof (buf),
158 	    PMCS_FIRMWARE_SPCBOOT_NAME PMCS_FIRMWARE_START_SUF);
159 	sstart = ddi_modsym(modhp, buf, &errno);
160 	if (errno) {
161 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: unable to find symbol '%s'",
162 		    __func__, buf);
163 		(void) ddi_modclose(modhp);
164 		return (defret);
165 	}
166 
167 	(void) snprintf(buf, sizeof (buf),
168 	    PMCS_FIRMWARE_SPCBOOT_NAME PMCS_FIRMWARE_END_SUF);
169 	send = ddi_modsym(modhp, buf, &errno);
170 	if (errno) {
171 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: unable to find symbol '%s'",
172 		    __func__, buf);
173 		(void) ddi_modclose(modhp);
174 		return (defret);
175 	}
176 
177 	/*
178 	 * The SPCBoot image must be updated first, and this is written to
179 	 * SEEPROM, not flash.
180 	 */
181 	if (pmcs_set_nvmd(pwp, PMCS_NVMD_SPCBOOT, sstart,
182 	    (size_t)((size_t)send - (size_t)sstart)) == B_FALSE) {
183 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
184 		    "%s: unable to flash '%s' segment",
185 		    __func__, PMCS_FIRMWARE_SPCBOOT_NAME);
186 		(void) ddi_modclose(modhp);
187 		return (-1);
188 	}
189 
190 	if (pmcs_fw_flash(pwp, (void *)istart,
191 	    (uint32_t)((size_t)iend - (size_t)istart))) {
192 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
193 		    "%s: unable to flash '%s' segment",
194 		    __func__, PMCS_FIRMWARE_ILA_NAME);
195 		(void) ddi_modclose(modhp);
196 		return (-1);
197 	}
198 
199 	if (pmcs_fw_flash(pwp, (void *)cstart,
200 	    (uint32_t)((size_t)cend - (size_t)cstart))) {
201 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
202 		    "%s: unable to flash '%s' segment",
203 		    __func__, PMCS_FIRMWARE_CODE_NAME);
204 		(void) ddi_modclose(modhp);
205 		return (-1);
206 	}
207 
208 	(void) ddi_modclose(modhp);
209 
210 	if (pmcs_soft_reset(pwp, B_FALSE)) {
211 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
212 		    "%s: soft reset after flash update failed", __func__);
213 		return (-1);
214 	} else {
215 		pmcs_prt(pwp, PMCS_PRT_WARN,
216 		    "%s: Firmware successfully upgraded.", __func__);
217 	}
218 	return (0);
219 }
220 
221 /*
222  * Flash firmware support
223  * Called unlocked.
224  */
225 int
226 pmcs_fw_flash(pmcs_hw_t *pwp, pmcs_fw_hdr_t *hdr, uint32_t length)
227 {
228 	pmcs_fw_hdr_t *hp;
229 	uint8_t *wrk, *base;
230 
231 	/*
232 	 * Step 1- Validate firmware chunks within passed pointer.
233 	 */
234 	hp = hdr;
235 	wrk = (uint8_t *)hdr;
236 	base = wrk;
237 	for (;;) {
238 		pmcs_prt(pwp, PMCS_PRT_DEBUG1,
239 		    "%s: partition 0x%x, Length 0x%x", __func__,
240 		    hp->destination_partition, ntohl(hp->firmware_length));
241 		if (ntohl(hp->firmware_length) == 0) {
242 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
243 			    "%s: bad firmware length 0x%x",
244 			    __func__, ntohl(hp->firmware_length));
245 			return (EINVAL);
246 		}
247 		wrk += (sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length));
248 		if (wrk == base + length) {
249 			break;
250 		}
251 		if (wrk > base + length) {
252 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
253 			    "%s: out of bounds firmware length", __func__);
254 			return (EINVAL);
255 		}
256 		hp = (void *)wrk;
257 	}
258 
259 	/*
260 	 * Step 2- acquire scratch
261 	 */
262 	(void) pmcs_acquire_scratch(pwp, B_TRUE);
263 
264 	/*
265 	 * Step 3- loop through firmware chunks and send each one
266 	 * down to be flashed.
267 	 */
268 	hp = hdr;
269 	wrk = (uint8_t *)hdr;
270 	base = wrk;
271 	for (;;) {
272 		if (pmcs_flash_chunk(pwp, wrk)) {
273 			pmcs_release_scratch(pwp);
274 			return (EIO);
275 		}
276 		wrk += (sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length));
277 		if (wrk == base + length) {
278 			break;
279 		}
280 		hp = (void *) wrk;
281 	}
282 	pmcs_release_scratch(pwp);
283 	return (0);
284 }
285 
286 static int
287 pmcs_flash_chunk(pmcs_hw_t *pwp, uint8_t *chunk)
288 {
289 	pmcs_fw_hdr_t *hp;
290 	pmcwork_t *pwrk;
291 	uint32_t len, seg, off, result, amt, msg[PMCS_MSG_SIZE], *ptr;
292 
293 	hp = (void *)chunk;
294 	len = sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length);
295 
296 	seg = off = 0;
297 	while (off < len) {
298 		amt = PMCS_SCRATCH_SIZE;
299 		if (off + amt > len) {
300 			amt = len - off;
301 		}
302 		pmcs_prt(pwp, PMCS_PRT_DEBUG1,
303 		    "%s: segment %d offset %u length %u",
304 		    __func__, seg, off, amt);
305 		(void) memcpy(pwp->scratch, &chunk[off], amt);
306 		pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
307 		if (pwrk == NULL) {
308 			return (ENOMEM);
309 		}
310 		pwrk->arg = msg;
311 		msg[0] = LE_32(PMCS_HIPRI(pwp,
312 		    PMCS_OQ_EVENTS, PMCIN_FW_FLASH_UPDATE));
313 		msg[1] = LE_32(pwrk->htag);
314 		msg[2] = LE_32(off);
315 		msg[3] = LE_32(amt);
316 		if (off == 0) {
317 			msg[4] = LE_32(len);
318 		} else {
319 			msg[4] = 0;
320 		}
321 		msg[5] = 0;
322 		msg[6] = 0;
323 		msg[7] = 0;
324 		msg[8] = 0;
325 		msg[9] = 0;
326 		msg[10] = 0;
327 		msg[11] = 0;
328 		msg[12] = LE_32(DWORD0(pwp->scratch_dma));
329 		msg[13] = LE_32(DWORD1(pwp->scratch_dma));
330 		msg[14] = LE_32(amt);
331 		msg[15] = 0;
332 		mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
333 		ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
334 		if (ptr == NULL) {
335 			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
336 			pmcs_pwork(pwp, pwrk);
337 			pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
338 			return (ENOMEM);
339 		}
340 		COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
341 		(void) memset(msg, 0xaf, sizeof (msg));
342 		pwrk->state = PMCS_WORK_STATE_ONCHIP;
343 		INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
344 		WAIT_FOR(pwrk, 5000, result);
345 		pmcs_pwork(pwp, pwrk);
346 		if (result) {
347 			pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_timeo, __func__);
348 			return (EIO);
349 		}
350 		switch (LE_32(msg[2])) {
351 		case FLASH_UPDATE_COMPLETE_PENDING_REBOOT:
352 			pmcs_prt(pwp, PMCS_PRT_DEBUG1,
353 			    "%s: segment %d complete pending reboot",
354 			    __func__, seg);
355 			break;
356 		case FLASH_UPDATE_IN_PROGRESS:
357 			pmcs_prt(pwp, PMCS_PRT_DEBUG1,
358 			    "%s: segment %d downloaded", __func__, seg);
359 			break;
360 		case FLASH_UPDATE_HDR_ERR:
361 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
362 			    "%s: segment %d header error", __func__, seg);
363 			return (EIO);
364 		case FLASH_UPDATE_OFFSET_ERR:
365 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
366 			    "%s: segment %d offset error", __func__, seg);
367 			return (EIO);
368 		case FLASH_UPDATE_UPDATE_CRC_ERR:
369 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
370 			    "%s: segment %d update crc error", __func__, seg);
371 			return (EIO);
372 		case FLASH_UPDATE_LENGTH_ERR:
373 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
374 			    "%s: segment %d length error", __func__, seg);
375 			return (EIO);
376 		case FLASH_UPDATE_HW_ERR:
377 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
378 			    "%s: segment %d hw error", __func__, seg);
379 			return (EIO);
380 		case FLASH_UPDATE_DNLD_NOT_SUPPORTED:
381 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
382 			    "%s: segment %d download not supported error",
383 			    __func__, seg);
384 			return (EIO);
385 		case FLASH_UPDATE_DISABLED:
386 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
387 			    "%s: segment %d update disabled error",
388 			    __func__, seg);
389 			return (EIO);
390 		default:
391 			pmcs_prt(pwp, PMCS_PRT_DEBUG,
392 			    "%s: segment %d unknown error %x",
393 			    __func__, seg, msg[2]);
394 			return (EIO);
395 		}
396 		off += amt;
397 		seg++;
398 	}
399 	return (0);
400 }
401 
402 /*
403  * pmcs_validate_vpd
404  *
405  * Input: softstate pointer and pointer to vpd data buffer
406  * Returns: B_TRUE if VPD data looks OK, B_FALSE otherwise
407  */
408 static boolean_t
409 pmcs_validate_vpd(pmcs_hw_t *pwp, uint8_t *data)
410 {
411 	pmcs_vpd_header_t *vpd_header;
412 	uint8_t *bufp, kv_len, *chksump, chksum = 0;
413 	char tbuf[80];
414 	char prop[24];
415 	int idx, str_len;
416 	uint16_t strid_length, chksum_len;
417 	uint64_t wwid;
418 	pmcs_vpd_kv_t *vkvp;
419 
420 	vpd_header = (pmcs_vpd_header_t *)data;
421 
422 	/*
423 	 * Make sure we understand the format of this data
424 	 */
425 
426 	if (vpd_header->eeprom_version < PMCS_VPD_VERSION) {
427 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
428 		    "%s: VPD version(%d) out-of-date; (%d) required."
429 		    " Thebe card needs to be flashed.",
430 		    __func__, vpd_header->eeprom_version, PMCS_VPD_VERSION);
431 	}
432 	if ((vpd_header->eeprom_version != PMCS_VPD_VERSION) &&
433 	    (vpd_header->eeprom_version != (PMCS_VPD_VERSION - 1))) {
434 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
435 		    "%s: VPD version mismatch (%d != %d)",
436 		    __func__, vpd_header->eeprom_version, PMCS_VPD_VERSION);
437 		return (B_FALSE);
438 	}
439 
440 	/*
441 	 * Do we have a valid SAS WWID?
442 	 */
443 	if (((vpd_header->hba_sas_wwid[0] & 0xf0) >> 4) != NAA_IEEE_REG) {
444 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
445 		    "%s: SAS WWN has invalid NAA (%d)", __func__,
446 		    ((vpd_header->hba_sas_wwid[0] & 0xf0) >> 4));
447 		return (B_FALSE);
448 	}
449 	wwid = pmcs_barray2wwn(vpd_header->hba_sas_wwid);
450 	for (idx = 0; idx < PMCS_MAX_PORTS; idx++) {
451 		pwp->sas_wwns[idx] = wwid + idx;
452 	}
453 
454 	if (vpd_header->vpd_start_byte != PMCS_VPD_START) {
455 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
456 		    "%s: Didn't see VPD start byte", __func__);
457 		return (B_FALSE);
458 	}
459 
460 	/*
461 	 * We only checksum the VPD data between (and including) VPD Start byte
462 	 * and the checksum value byte. The length of this data for CRC is
463 	 * 15 less than the length indicated in vpd_length field of the header.
464 	 * 8 (SAS WWN) + 2 (subsystem ID) + 2 (subsystem vendor ID) +
465 	 * 1 (end tag) + 2 (hex byte CRC, different from this one) = 15 bytes
466 	 */
467 	/*
468 	 * VPD length (little endian format) is represented as byte-array field
469 	 * & read the following way to avoid alignment issues (in SPARC)
470 	 */
471 	chksum_len = ((vpd_header->vpd_length[1] << 8) |
472 	    (vpd_header->vpd_length[0])) - 15;
473 	/* Validate VPD data checksum */
474 	chksump = (uint8_t *)&vpd_header->vpd_start_byte;
475 	ASSERT (*chksump == PMCS_VPD_START);
476 	for (idx = 0; idx < chksum_len; idx++, chksump++) {
477 		chksum += *chksump;
478 	}
479 	ASSERT (*chksump == PMCS_VPD_END);
480 	if (chksum) {
481 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: VPD checksum(%d) non-zero."
482 		    " Checksum validation failed.", __func__, chksum);
483 	}
484 
485 	/*
486 	 * Get length of string ID tag and read it.
487 	 */
488 	bufp = (uint8_t *)&vpd_header->vpd_start_byte;
489 	bufp += 3;		/* Skip the start byte and length */
490 	/*
491 	 * String ID tag length (little endian format) is represented as
492 	 * byte-array & read the following way to avoid alignment issues
493 	 * (in SPARC)
494 	 */
495 	strid_length = (vpd_header->strid_length[1] << 8) |
496 	    (vpd_header->strid_length[0]);
497 	if (strid_length > 79) {
498 		strid_length = 79;
499 	}
500 	bcopy(bufp, tbuf, strid_length);
501 	tbuf[strid_length] = 0;
502 
503 	pmcs_prt(pwp, PMCS_PRT_DEBUG2,
504 	    "%s: Product Name: '%s'", __func__, tbuf);
505 	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_MODEL_NAME, tbuf);
506 
507 	/*
508 	 * Skip VPD-R tag and length of read-only tag, then start reading
509 	 * keyword/value pairs
510 	 */
511 	bufp += strid_length;	/* Skip to VPD-R tag */
512 	bufp += 3;		/* Skip VPD-R tag and length of VPD-R data */
513 
514 	vkvp = (pmcs_vpd_kv_t *)bufp;
515 
516 	while (vkvp->keyword[0] != PMCS_VPD_END) {
517 		tbuf[0] = 0;
518 		str_len = snprintf(tbuf, 80, "VPD: %c%c = <",
519 		    vkvp->keyword[0], vkvp->keyword[1]);
520 
521 		kv_len = vkvp->value_length;
522 		for (idx = 0; idx < kv_len; idx++) {
523 			tbuf[str_len + idx] = vkvp->value[idx];
524 			prop[idx] = vkvp->value[idx];
525 		}
526 		prop[idx] = '\0';
527 		str_len += kv_len;
528 		tbuf[str_len] = '>';
529 		tbuf[str_len + 1] = 0;
530 		pmcs_prt(pwp, PMCS_PRT_DEBUG2, "%s (Len: 0x%x)", tbuf, kv_len);
531 
532 		/* Keyword is Manufacturer */
533 		if ((vkvp->keyword[0] == 'M') && (vkvp->keyword[1] == 'N')) {
534 			pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING,
535 			    PMCS_MANUFACTURER, prop);
536 		}
537 		/* Keyword is Serial Number */
538 		if ((vkvp->keyword[0] == 'S') && (vkvp->keyword[1] == 'N')) {
539 			pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING,
540 			    PMCS_SERIAL_NUMBER, prop);
541 		}
542 
543 		vkvp = (pmcs_vpd_kv_t *)(bufp + 3 + kv_len);
544 		bufp += kv_len + 3;
545 	}
546 
547 	return (B_TRUE);
548 }
549 
550 /*
551  * pmcs_get_nvmd
552  *
553  * This function will read the requested data from the non-volatile
554  * storage on the card.  This could mean SEEPROM, VPD, or other areas
555  * as defined by the PM8001 programmer's manual.
556  *
557  * nvmd_type: The data type being requested
558  * nvmd: NVM device to access (IOP/AAP1)
559  * offset: Must be 4K alignment
560  * buf: Pointer to memory region for retrieved data
561  * size_left: Total available bytes left in buf
562  *
563  * Returns: non-negative on success, -1 on failure
564  */
565 
566 /*ARGSUSED*/
567 int
568 pmcs_get_nvmd(pmcs_hw_t *pwp, pmcs_nvmd_type_t nvmd_type, uint8_t nvmd,
569     uint32_t offset, char *buf, uint32_t size_left)
570 {
571 	pmcs_get_nvmd_cmd_t iomb;
572 	pmcwork_t *workp;
573 	uint8_t *chunkp;
574 	uint32_t *ptr, ibq, *iombp;
575 	uint32_t dlen;
576 	uint16_t status;
577 	uint8_t tdas_nvmd, ip, tda, tbn_tdps;
578 	uint8_t	doa[3];
579 	int32_t result = -1, i = 0;
580 
581 	switch (nvmd_type) {
582 	case PMCS_NVMD_VPD:
583 		tdas_nvmd = PMCIN_NVMD_TDPS_1 | PMCIN_NVMD_TWI;
584 		tda = PMCIN_TDA_PAGE(2);
585 		tbn_tdps = PMCIN_NVMD_TBN(0) | PMCIN_NVMD_TDPS_8;
586 		ip = PMCIN_NVMD_INDIRECT_PLD;
587 		dlen = LE_32(PMCS_SEEPROM_PAGE_SIZE);
588 		doa[0] = 0;
589 		doa[1] = 0;
590 		doa[2] = 0;
591 		break;
592 	case PMCS_NVMD_REG_DUMP:
593 		tdas_nvmd = nvmd;
594 		tda = 0;
595 		tbn_tdps = 0;
596 		ip = PMCIN_NVMD_INDIRECT_PLD;
597 		dlen = LE_32(PMCS_REGISTER_DUMP_BLOCK_SIZE);
598 		doa[0] = offset & 0xff;
599 		doa[1] = (offset >> 8) & 0xff;
600 		doa[2] = (offset >> 16) & 0xff;
601 		break;
602 	case PMCS_NVMD_EVENT_LOG:
603 		tdas_nvmd = nvmd;
604 		tda = 0;
605 		tbn_tdps = 0;
606 		ip = PMCIN_NVMD_INDIRECT_PLD;
607 		dlen = LE_32(PMCS_REGISTER_DUMP_BLOCK_SIZE);
608 		offset = offset + PMCS_NVMD_EVENT_LOG_OFFSET;
609 		doa[0] = offset & 0xff;
610 		doa[1] = (offset >> 8) & 0xff;
611 		doa[2] = (offset >> 16) & 0xff;
612 		break;
613 	default:
614 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
615 		    "%s: Invalid nvmd type: %d", __func__, nvmd_type);
616 		return (-1);
617 	}
618 
619 	workp = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
620 	if (workp == NULL) {
621 		pmcs_prt(pwp, PMCS_PRT_WARN,
622 		    "%s: Unable to get work struct", __func__);
623 		return (-1);
624 	}
625 
626 	ptr = &iomb.header;
627 	bzero(ptr, sizeof (pmcs_get_nvmd_cmd_t));
628 	*ptr = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_GENERAL, PMCIN_GET_NVMD_DATA));
629 	workp->arg = (void *)&iomb;
630 	iomb.htag = LE_32(workp->htag);
631 	iomb.ip = ip;
632 	iomb.tbn_tdps = tbn_tdps;
633 	iomb.tda = tda;
634 	iomb.tdas_nvmd = tdas_nvmd;
635 	iomb.ipbal = LE_32(DWORD0(pwp->flash_chunk_addr));
636 	iomb.ipbah = LE_32(DWORD1(pwp->flash_chunk_addr));
637 	iomb.ipdl = dlen;
638 	iomb.doa[0] = doa[0];
639 	iomb.doa[1] = doa[1];
640 	iomb.doa[2] = doa[2];
641 
642 	/*
643 	 * ptr will now point to the inbound queue message
644 	 */
645 	GET_IO_IQ_ENTRY(pwp, ptr, 0, ibq);
646 	if (ptr == NULL) {
647 		pmcs_prt(pwp, PMCS_PRT_ERR, "!%s: Unable to get IQ entry",
648 		    __func__);
649 		pmcs_pwork(pwp, workp);
650 		return (-1);
651 	}
652 
653 	bzero(ptr, PMCS_MSG_SIZE << 2);	/* PMCS_MSG_SIZE is in dwords */
654 	iombp = (uint32_t *)&iomb;
655 	COPY_MESSAGE(ptr, iombp, sizeof (pmcs_get_nvmd_cmd_t) >> 2);
656 	workp->state = PMCS_WORK_STATE_ONCHIP;
657 	INC_IQ_ENTRY(pwp, ibq);
658 
659 	WAIT_FOR(workp, 1000, result);
660 	ptr = workp->arg;
661 	if (result) {
662 		pmcs_timed_out(pwp, workp->htag, __func__);
663 		pmcs_pwork(pwp, workp);
664 		return (-1);
665 	}
666 	status = LE_32(*(ptr + 3)) & 0xffff;
667 	if (status != PMCS_NVMD_STAT_SUCCESS) {
668 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Error, status = 0x%04x",
669 		    __func__, status);
670 		pmcs_pwork(pwp, workp);
671 		return (-1);
672 	}
673 
674 	pmcs_pwork(pwp, workp);
675 
676 	if (ddi_dma_sync(pwp->cip_handles, 0, 0,
677 	    DDI_DMA_SYNC_FORKERNEL) != DDI_SUCCESS) {
678 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "Condition check failed at "
679 		    "%s():%d", __func__, __LINE__);
680 	}
681 	chunkp = (uint8_t *)pwp->flash_chunkp;
682 
683 	switch (nvmd) {
684 	case PMCIN_NVMD_VPD:
685 		if (pmcs_validate_vpd(pwp, chunkp)) {
686 			result = 0;
687 		} else {
688 			result = -1;
689 		}
690 		break;
691 	case PMCIN_NVMD_AAP1:
692 	case PMCIN_NVMD_IOP:
693 		ASSERT(buf);
694 		i = 0;
695 		if (nvmd_type == PMCS_NVMD_REG_DUMP) {
696 			while ((i < PMCS_FLASH_CHUNK_SIZE) &&
697 			    (chunkp[i] != 0xff) && (chunkp[i] != '\0')) {
698 				(void) snprintf(&buf[i], (size_left - i),
699 				    "%c", chunkp[i]);
700 				i++;
701 			}
702 		} else if (nvmd_type == PMCS_NVMD_EVENT_LOG) {
703 			i = pmcs_dump_binary(pwp, pwp->flash_chunkp, 0,
704 			    (PMCS_FLASH_CHUNK_SIZE >> 2), buf, size_left);
705 		}
706 		result = i;
707 		break;
708 	default:
709 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "UNKNOWN NVMD DEVICE");
710 		return (-1);
711 	}
712 
713 	return (result);
714 }
715 
716 /*
717  * pmcs_set_nvmd
718  *
719  * This function will write the requested data to non-volatile storage
720  * on the HBA.  This could mean SEEPROM, VPD, or other areas as defined by
721  * the PM8001 programmer's manual.
722  *
723  * nvmd_type: The data type to be written
724  * buf: Pointer to memory region for data to write
725  * len: Length of the data buffer
726  *
727  * Returns: B_TRUE on success, B_FALSE on failure
728  */
729 
730 boolean_t
731 pmcs_set_nvmd(pmcs_hw_t *pwp, pmcs_nvmd_type_t nvmd_type, uint8_t *buf,
732     size_t len)
733 {
734 	pmcs_set_nvmd_cmd_t iomb;
735 	pmcwork_t *workp;
736 	uint32_t *ptr, ibq, *iombp;
737 	uint32_t dlen;
738 	uint16_t status;
739 	uint8_t tdas_nvmd, ip;
740 	int result;
741 
742 	switch (nvmd_type) {
743 	case PMCS_NVMD_SPCBOOT:
744 		tdas_nvmd = PMCIN_NVMD_SEEPROM;
745 		ip = PMCIN_NVMD_INDIRECT_PLD;
746 		ASSERT((len >= PMCS_SPCBOOT_MIN_SIZE) &&
747 		    (len <= PMCS_SPCBOOT_MAX_SIZE));
748 		dlen = LE_32(len);
749 		break;
750 	default:
751 		pmcs_prt(pwp, PMCS_PRT_DEBUG,
752 		    "%s: Invalid nvmd type: %d", __func__, nvmd_type);
753 		return (B_FALSE);
754 	}
755 
756 	pmcs_prt(pwp, PMCS_PRT_DEBUG_DEVEL, "%s: Request for nvmd type: %d",
757 	    __func__, nvmd_type);
758 
759 	workp = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
760 	if (workp == NULL) {
761 		pmcs_prt(pwp, PMCS_PRT_WARN,
762 		    "%s: Unable to get work struct", __func__);
763 		return (B_FALSE);
764 	}
765 
766 	ptr = &iomb.header;
767 	bzero(ptr, sizeof (pmcs_set_nvmd_cmd_t));
768 	*ptr = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_GENERAL, PMCIN_SET_NVMD_DATA));
769 	workp->arg = (void *)&iomb;
770 	iomb.htag = LE_32(workp->htag);
771 	iomb.ip = ip;
772 	iomb.tdas_nvmd = tdas_nvmd;
773 	iomb.signature = LE_32(PMCS_SEEPROM_SIGNATURE);
774 	iomb.ipbal = LE_32(DWORD0(pwp->flash_chunk_addr));
775 	iomb.ipbah = LE_32(DWORD1(pwp->flash_chunk_addr));
776 	iomb.ipdl = dlen;
777 
778 	pmcs_print_entry(pwp, PMCS_PRT_DEBUG_DEVEL,
779 	    "PMCIN_SET_NVMD_DATA iomb", (void *)&iomb);
780 
781 	bcopy(buf, pwp->flash_chunkp, len);
782 	if (ddi_dma_sync(pwp->cip_handles, 0, 0,
783 	    DDI_DMA_SYNC_FORDEV) != DDI_SUCCESS) {
784 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "Condition check failed at "
785 		    "%s():%d", __func__, __LINE__);
786 	}
787 
788 	/*
789 	 * ptr will now point to the inbound queue message
790 	 */
791 	GET_IO_IQ_ENTRY(pwp, ptr, 0, ibq);
792 	if (ptr == NULL) {
793 		pmcs_prt(pwp, PMCS_PRT_ERR, "!%s: Unable to get IQ entry",
794 		    __func__);
795 		pmcs_pwork(pwp, workp);
796 		return (B_FALSE);
797 	}
798 
799 	bzero(ptr, PMCS_MSG_SIZE << 2);	/* PMCS_MSG_SIZE is in dwords */
800 	iombp = (uint32_t *)&iomb;
801 	COPY_MESSAGE(ptr, iombp, sizeof (pmcs_set_nvmd_cmd_t) >> 2);
802 	workp->state = PMCS_WORK_STATE_ONCHIP;
803 	INC_IQ_ENTRY(pwp, ibq);
804 
805 	WAIT_FOR(workp, 2000, result);
806 
807 	if (result) {
808 		pmcs_timed_out(pwp, workp->htag, __func__);
809 		pmcs_pwork(pwp, workp);
810 		return (B_FALSE);
811 	}
812 
813 	pmcs_pwork(pwp, workp);
814 
815 	status = LE_32(*(ptr + 3)) & 0xffff;
816 	if (status != PMCS_NVMD_STAT_SUCCESS) {
817 		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Error, status = 0x%04x",
818 		    __func__, status);
819 		return (B_FALSE);
820 	}
821 
822 	return (B_TRUE);
823 }
824