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