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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <npi_espc.h>
29 #include <nxge_espc.h>
30 
31 static int npi_vpd_read_prop(npi_handle_t handle, uint32_t ep,
32 		const char *prop, int len, char *val);
33 
34 npi_status_t
npi_espc_pio_enable(npi_handle_t handle)35 npi_espc_pio_enable(npi_handle_t handle)
36 {
37 	NXGE_REG_WR64(handle, ESPC_REG_ADDR(ESPC_PIO_EN_REG), 0x1);
38 	return (NPI_SUCCESS);
39 }
40 
41 npi_status_t
npi_espc_pio_disable(npi_handle_t handle)42 npi_espc_pio_disable(npi_handle_t handle)
43 {
44 	NXGE_REG_WR64(handle, ESPC_REG_ADDR(ESPC_PIO_EN_REG), 0);
45 	return (NPI_SUCCESS);
46 }
47 
48 npi_status_t
npi_espc_eeprom_entry(npi_handle_t handle,io_op_t op,uint32_t addr,uint8_t * data)49 npi_espc_eeprom_entry(npi_handle_t handle, io_op_t op, uint32_t addr,
50 			uint8_t *data)
51 {
52 	uint64_t val = 0;
53 
54 	if ((addr & ~EPC_EEPROM_ADDR_BITS) != 0) {
55 		NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
56 		    " npi_espc_eerprom_entry"
57 		    " Invalid input addr <0x%x>\n",
58 		    addr));
59 		return (NPI_FAILURE | NPI_ESPC_EEPROM_ADDR_INVALID);
60 	}
61 
62 	switch (op) {
63 	case OP_SET:
64 		val = EPC_WRITE_INITIATE | (addr << EPC_EEPROM_ADDR_SHIFT) |
65 		    *data;
66 		NXGE_REG_WR64(handle, ESPC_REG_ADDR(ESPC_PIO_STATUS_REG), val);
67 		EPC_WAIT_RW_COMP(handle, &val, EPC_WRITE_COMPLETE);
68 		if ((val & EPC_WRITE_COMPLETE) == 0) {
69 			NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
70 			    " npi_espc_eeprom_entry"
71 			    " HW Error: EEPROM_WR <0x%x>\n",
72 			    val));
73 			return (NPI_FAILURE | NPI_ESPC_EEPROM_WRITE_FAILED);
74 		}
75 		break;
76 	case OP_GET:
77 		val = EPC_READ_INITIATE | (addr << EPC_EEPROM_ADDR_SHIFT);
78 		NXGE_REG_WR64(handle, ESPC_REG_ADDR(ESPC_PIO_STATUS_REG), val);
79 		EPC_WAIT_RW_COMP(handle, &val, EPC_READ_COMPLETE);
80 		if ((val & EPC_READ_COMPLETE) == 0) {
81 			NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
82 			    " npi_espc_eeprom_entry"
83 			    " HW Error: EEPROM_RD <0x%x>",
84 			    val));
85 			return (NPI_FAILURE | NPI_ESPC_EEPROM_READ_FAILED);
86 		}
87 		NXGE_REG_RD64(handle, ESPC_REG_ADDR(ESPC_PIO_STATUS_REG), &val);
88 		/*
89 		 * Workaround for synchronization issues - do a second PIO
90 		 */
91 		val = EPC_READ_INITIATE | (addr << EPC_EEPROM_ADDR_SHIFT);
92 		NXGE_REG_WR64(handle, ESPC_REG_ADDR(ESPC_PIO_STATUS_REG), val);
93 		EPC_WAIT_RW_COMP(handle, &val, EPC_READ_COMPLETE);
94 		if ((val & EPC_READ_COMPLETE) == 0) {
95 			NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
96 			    " npi_espc_eeprom_entry HW Error: "
97 			    "EEPROM_RD <0x%x>", val));
98 			return (NPI_FAILURE | NPI_ESPC_EEPROM_READ_FAILED);
99 		}
100 		NXGE_REG_RD64(handle, ESPC_REG_ADDR(ESPC_PIO_STATUS_REG), &val);
101 		*data = val & EPC_EEPROM_DATA_MASK;
102 		break;
103 	default:
104 		NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
105 		    " npi_espc_eeprom_entry"
106 		    " Invalid Input addr <0x%x>\n", addr));
107 		return (NPI_FAILURE | NPI_ESPC_OPCODE_INVALID);
108 	}
109 
110 	return (NPI_SUCCESS);
111 }
112 
113 npi_status_t
npi_espc_mac_addr_get(npi_handle_t handle,uint8_t * data)114 npi_espc_mac_addr_get(npi_handle_t handle, uint8_t *data)
115 {
116 	mac_addr_0_t mac0;
117 	mac_addr_1_t mac1;
118 
119 	NXGE_REG_RD64(handle, ESPC_MAC_ADDR_0, &mac0.value);
120 	data[0] = mac0.bits.w0.byte0;
121 	data[1] = mac0.bits.w0.byte1;
122 	data[2] = mac0.bits.w0.byte2;
123 	data[3] = mac0.bits.w0.byte3;
124 
125 	NXGE_REG_RD64(handle, ESPC_MAC_ADDR_1, &mac1.value);
126 	data[4] = mac1.bits.w0.byte4;
127 	data[5] = mac1.bits.w0.byte5;
128 
129 	return (NPI_SUCCESS);
130 }
131 
132 npi_status_t
npi_espc_num_ports_get(npi_handle_t handle,uint8_t * data)133 npi_espc_num_ports_get(npi_handle_t handle, uint8_t *data)
134 {
135 	uint64_t val = 0;
136 
137 	NXGE_REG_RD64(handle, ESPC_NUM_PORTS_MACS, &val);
138 	val &= NUM_PORTS_MASK;
139 	*data = (uint8_t)val;
140 
141 	return (NPI_SUCCESS);
142 }
143 
144 npi_status_t
npi_espc_num_macs_get(npi_handle_t handle,uint8_t * data)145 npi_espc_num_macs_get(npi_handle_t handle, uint8_t *data)
146 {
147 	uint64_t val = 0;
148 
149 	NXGE_REG_RD64(handle, ESPC_NUM_PORTS_MACS, &val);
150 	val &= NUM_MAC_ADDRS_MASK;
151 	val = (val >> NUM_MAC_ADDRS_SHIFT);
152 	*data = (uint8_t)val;
153 
154 	return (NPI_SUCCESS);
155 }
156 
157 npi_status_t
npi_espc_model_str_get(npi_handle_t handle,char * data)158 npi_espc_model_str_get(npi_handle_t handle, char *data)
159 {
160 	uint64_t val = 0;
161 	uint16_t str_len;
162 	int i, j;
163 
164 	NXGE_REG_RD64(handle, ESPC_MOD_STR_LEN, &val);
165 	val &= MOD_STR_LEN_MASK;
166 	str_len = (uint8_t)val;
167 
168 	if (str_len > MAX_MOD_STR_LEN) {
169 		NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
170 		    " npi_espc_model_str_get"
171 		    " Model string length %d exceeds max %d\n",
172 		    str_len, MAX_MOD_STR_LEN));
173 		return (NPI_FAILURE | NPI_ESPC_STR_LEN_INVALID);
174 	}
175 
176 	/*
177 	 * Might have to reverse the order depending on how the string
178 	 * is written.
179 	 */
180 	for (i = 0, j = 0; i < str_len; j++) {
181 		NXGE_REG_RD64(handle, ESPC_MOD_STR(j), &val);
182 		data[i++] = ((char *)&val)[3];
183 		data[i++] = ((char *)&val)[2];
184 		data[i++] = ((char *)&val)[1];
185 		data[i++] = ((char *)&val)[0];
186 	}
187 
188 	data[str_len] = '\0';
189 
190 	return (NPI_SUCCESS);
191 }
192 
193 npi_status_t
npi_espc_bd_model_str_get(npi_handle_t handle,char * data)194 npi_espc_bd_model_str_get(npi_handle_t handle, char *data)
195 {
196 	uint64_t val = 0;
197 	uint16_t str_len;
198 	int i, j;
199 
200 	NXGE_REG_RD64(handle, ESPC_BD_MOD_STR_LEN, &val);
201 	val &= BD_MOD_STR_LEN_MASK;
202 	str_len = (uint8_t)val;
203 
204 	if (str_len > MAX_BD_MOD_STR_LEN) {
205 		NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
206 		    " npi_espc_model_str_get"
207 		    " Board Model string length %d "
208 		    "exceeds max %d\n",
209 		    str_len, MAX_BD_MOD_STR_LEN));
210 		return (NPI_FAILURE | NPI_ESPC_STR_LEN_INVALID);
211 	}
212 
213 	/*
214 	 * Might have to reverse the order depending on how the string
215 	 * is written.
216 	 */
217 	for (i = 0, j = 0; i < str_len; j++) {
218 		NXGE_REG_RD64(handle, ESPC_BD_MOD_STR(j), &val);
219 		data[i++] = ((char *)&val)[3];
220 		data[i++] = ((char *)&val)[2];
221 		data[i++] = ((char *)&val)[1];
222 		data[i++] = ((char *)&val)[0];
223 	}
224 
225 	data[str_len] = '\0';
226 
227 	return (NPI_SUCCESS);
228 }
229 
230 npi_status_t
npi_espc_phy_type_get(npi_handle_t handle,uint8_t * data)231 npi_espc_phy_type_get(npi_handle_t handle, uint8_t *data)
232 {
233 	phy_type_t	phy;
234 
235 	NXGE_REG_RD64(handle, ESPC_PHY_TYPE, &phy.value);
236 	data[0] = phy.bits.w0.pt0_phy_type;
237 	data[1] = phy.bits.w0.pt1_phy_type;
238 	data[2] = phy.bits.w0.pt2_phy_type;
239 	data[3] = phy.bits.w0.pt3_phy_type;
240 
241 	return (NPI_SUCCESS);
242 }
243 
244 npi_status_t
npi_espc_port_phy_type_get(npi_handle_t handle,uint8_t * data,uint8_t portn)245 npi_espc_port_phy_type_get(npi_handle_t handle, uint8_t *data, uint8_t portn)
246 {
247 	phy_type_t	phy;
248 
249 	ASSERT(IS_PORT_NUM_VALID(portn));
250 
251 	NXGE_REG_RD64(handle, ESPC_PHY_TYPE, &phy.value);
252 	switch (portn) {
253 	case 0:
254 		*data = phy.bits.w0.pt0_phy_type;
255 		break;
256 	case 1:
257 		*data = phy.bits.w0.pt1_phy_type;
258 		break;
259 	case 2:
260 		*data = phy.bits.w0.pt2_phy_type;
261 		break;
262 	case 3:
263 		*data = phy.bits.w0.pt3_phy_type;
264 		break;
265 	default:
266 		NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
267 		    " npi_espc_port_phy_type_get"
268 		    " Invalid Input: portn <%d>",
269 		    portn));
270 		return (NPI_FAILURE | NPI_ESPC_PORT_INVALID);
271 	}
272 
273 	return (NPI_SUCCESS);
274 }
275 
276 npi_status_t
npi_espc_max_frame_get(npi_handle_t handle,uint16_t * data)277 npi_espc_max_frame_get(npi_handle_t handle, uint16_t *data)
278 {
279 	uint64_t val = 0;
280 
281 	NXGE_REG_RD64(handle, ESPC_MAX_FM_SZ, &val);
282 	val &= MAX_FM_SZ_MASK;
283 	*data = (uint8_t)val;
284 
285 	return (NPI_SUCCESS);
286 }
287 
288 npi_status_t
npi_espc_version_get(npi_handle_t handle,uint16_t * data)289 npi_espc_version_get(npi_handle_t handle, uint16_t *data)
290 {
291 	uint64_t val = 0;
292 
293 	NXGE_REG_RD64(handle, ESPC_VER_IMGSZ, &val);
294 	val &= VER_NUM_MASK;
295 	*data = (uint8_t)val;
296 
297 	return (NPI_SUCCESS);
298 }
299 
300 npi_status_t
npi_espc_img_sz_get(npi_handle_t handle,uint16_t * data)301 npi_espc_img_sz_get(npi_handle_t handle, uint16_t *data)
302 {
303 	uint64_t val = 0;
304 
305 	NXGE_REG_RD64(handle, ESPC_VER_IMGSZ, &val);
306 	val &= IMG_SZ_MASK;
307 	val = val >> IMG_SZ_SHIFT;
308 	*data = (uint8_t)val;
309 
310 	return (NPI_SUCCESS);
311 }
312 
313 npi_status_t
npi_espc_chksum_get(npi_handle_t handle,uint8_t * data)314 npi_espc_chksum_get(npi_handle_t handle, uint8_t *data)
315 {
316 	uint64_t val = 0;
317 
318 	NXGE_REG_RD64(handle, ESPC_CHKSUM, &val);
319 	val &= CHKSUM_MASK;
320 	*data = (uint8_t)val;
321 
322 	return (NPI_SUCCESS);
323 }
324 
325 npi_status_t
npi_espc_intr_num_get(npi_handle_t handle,uint8_t * data)326 npi_espc_intr_num_get(npi_handle_t handle, uint8_t *data)
327 {
328 	intr_num_t	intr;
329 
330 	NXGE_REG_RD64(handle, ESPC_INTR_NUM, &intr.value);
331 	data[0] = intr.bits.w0.pt0_intr_num;
332 	data[1] = intr.bits.w0.pt1_intr_num;
333 	data[2] = intr.bits.w0.pt2_intr_num;
334 	data[3] = intr.bits.w0.pt3_intr_num;
335 
336 	return (NPI_SUCCESS);
337 }
338 
339 void
npi_espc_dump(npi_handle_t handle)340 npi_espc_dump(npi_handle_t handle)
341 {
342 	int i;
343 	uint64_t val = 0;
344 
345 	NPI_REG_DUMP_MSG((handle.function, NPI_REG_CTL,
346 	    "Dumping SEEPROM registers directly:\n\n"));
347 
348 	for (i = 0; i < 23; i++) {
349 		NXGE_REG_RD64(handle, ESPC_NCR_REGN(i), &val);
350 		NPI_REG_DUMP_MSG((handle.function, NPI_REG_CTL,
351 		    "reg[%d]      0x%llx\n",
352 		    i, val & 0xffffffff));
353 	}
354 
355 	NPI_REG_DUMP_MSG((handle.function, NPI_REG_CTL, "\n\n"));
356 }
357 
358 uint32_t
npi_espc_reg_get(npi_handle_t handle,int reg_idx)359 npi_espc_reg_get(npi_handle_t handle, int reg_idx)
360 {
361 	uint64_t val = 0;
362 	uint32_t reg_val = 0;
363 
364 	NXGE_REG_RD64(handle, ESPC_NCR_REGN(reg_idx), &val);
365 	reg_val = val & 0xffffffff;
366 
367 	return (reg_val);
368 }
369 
vpd_rd(npi_handle_t handle,uint32_t addr)370 static inline uint8_t vpd_rd(npi_handle_t handle, uint32_t addr)
371 {
372 	uint8_t data = 0;
373 
374 	if (npi_espc_eeprom_entry(handle, OP_GET, addr, &data) != NPI_SUCCESS)
375 		data = 0;
376 	return (data);
377 }
378 
379 npi_status_t
npi_espc_vpd_info_get(npi_handle_t handle,p_npi_vpd_info_t vpdp,uint32_t rom_len)380 npi_espc_vpd_info_get(npi_handle_t handle, p_npi_vpd_info_t vpdp,
381 		uint32_t rom_len)
382 {
383 	int		i, len;
384 	uint32_t	base = 0, kstart = 0, ep, end;
385 	uint8_t		fd_flags = 0;
386 
387 	/* Fill the vpd_info struct with invalid vals */
388 	(void) strcpy(vpdp->model, "\0");
389 	(void) strcpy(vpdp->bd_model, "\0");
390 	(void) strcpy(vpdp->phy_type, "\0");
391 	(void) strcpy(vpdp->ver, "\0");
392 	vpdp->num_macs = 0;
393 	for (i = 0; i < ETHERADDRL; i++) {
394 		vpdp->mac_addr[i] = 0;
395 	}
396 
397 	ep = 0;
398 	end = ep + rom_len;
399 
400 	/* go through the images till OBP image type is found */
401 	while (ep < end) {
402 		base = ep;
403 		/* check for expansion rom header signature */
404 		if (vpd_rd(handle, ep) != 0x55 ||
405 		    vpd_rd(handle, ep + 1) != 0xaa) {
406 			NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
407 			    "npi_espc_vpd_info_get: expansion rom image "
408 			    "not found, 0x%x [0x%x 0x%x]", ep,
409 			    vpd_rd(handle, ep), vpd_rd(handle, ep + 1)));
410 			goto vpd_info_err;
411 		}
412 		/* go to the beginning of the PCI data struct of this image */
413 		ep = ep + 23;
414 		ep = base + ((vpd_rd(handle, ep) << 8) |
415 		    (vpd_rd(handle, ep + 1)));
416 		/* check for PCI data struct signature "PCIR" */
417 		if ((vpd_rd(handle, ep) != 0x50) ||
418 		    (vpd_rd(handle, ep + 1) != 0x43) ||
419 		    (vpd_rd(handle, ep + 2) != 0x49) ||
420 		    (vpd_rd(handle, ep + 3) != 0x52)) {
421 			NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
422 			    "npi_espc_vpd_info_get: PCIR sig not found"));
423 			goto vpd_info_err;
424 		}
425 		/* check for image type OBP */
426 		if (vpd_rd(handle, ep + 20) != 0x01) {
427 			/* go to the next image */
428 			ep = base + ((vpd_rd(handle, base + 2)) * 512);
429 			continue;
430 		}
431 		/* find the beginning of the VPD data */
432 		base = base + (vpd_rd(handle, ep + 8) |
433 		    (vpd_rd(handle, ep + 9) << 8));
434 		break;
435 	}
436 
437 	/* check first byte of identifier string tag */
438 	if (!base || (vpd_rd(handle, base + 0) != 0x82)) {
439 		NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
440 		    "npi_espc_vpd_info_get: Could not find VPD!!"));
441 		goto vpd_info_err;
442 	}
443 
444 	/*
445 	 * skip over the ID string descriptor to go to the read-only VPD
446 	 * keywords list.
447 	 */
448 	i = (vpd_rd(handle, base + 1) |
449 	    (vpd_rd(handle, base + 2) << 8)) + 3;
450 
451 	while (i < EXPANSION_ROM_SIZE) {
452 		if (vpd_rd(handle, base + i) != 0x90) { /* no vpd found */
453 			NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
454 			    "nxge_get_vpd_info: Could not find "
455 			    "VPD ReadOnly list!! [0x%x] %d",
456 			    vpd_rd(handle, base + i), i));
457 			goto vpd_info_err;
458 		}
459 
460 		/* found a vpd read-only list, get its length */
461 		len = vpd_rd(handle, base + i + 1) |
462 		    (vpd_rd(handle, base + i + 2) << 8);
463 
464 		/* extract keywords */
465 		kstart = base + i + 3;
466 		ep = kstart;
467 		/*
468 		 * Each keyword field is as follows:
469 		 * 2 bytes keyword in the form of "Zx" where x = 0,1,2....
470 		 * 1 byte keyword data field length - klen
471 		 * Now the actual keyword data field:
472 		 * 	1 byte VPD property instance, 'M' / 'I'
473 		 * 	2 bytes
474 		 * 	1 byte VPD property data type, 'B' / 'S'
475 		 * 	1 byte VPD property value length - n
476 		 * 	Actual property string, length (klen - n - 5) bytes
477 		 * 	Actual property value, length n bytes
478 		 */
479 		while ((ep - kstart) < len) {
480 			int klen = vpd_rd(handle, ep + 2);
481 			int dlen;
482 			char type;
483 
484 			ep += 3;
485 
486 			/*
487 			 * Look for the following properties:
488 			 *
489 			 * local-mac-address:
490 			 * -- VPD Instance 'I'
491 			 * -- VPD Type String 'B'
492 			 * -- property string == local-mac-address
493 			 *
494 			 * model:
495 			 * -- VPD Instance 'M'
496 			 * -- VPD Type String 'S'
497 			 * -- property string == model
498 			 *
499 			 * board-model:
500 			 * -- VPD Instance 'M'
501 			 * -- VPD Type String 'S'
502 			 * -- property string == board-model
503 			 *
504 			 * num-mac-addresses:
505 			 * -- VPD Instance 'I'
506 			 * -- VPD Type String 'B'
507 			 * -- property string == num-mac-addresses
508 			 *
509 			 * phy-type:
510 			 * -- VPD Instance 'I'
511 			 * -- VPD Type String 'S'
512 			 * -- property string == phy-type
513 			 *
514 			 * version:
515 			 * -- VPD Instance 'M'
516 			 * -- VPD Type String 'S'
517 			 * -- property string == version
518 			 */
519 			if (vpd_rd(handle, ep) == 'M') {
520 				type = vpd_rd(handle, ep + 3);
521 				if (type == 'S') {
522 					dlen = vpd_rd(handle, ep + 4);
523 					if (npi_vpd_read_prop(handle, ep + 5,
524 					    "model", dlen, vpdp->model)) {
525 						fd_flags |= FD_MODEL;
526 						goto next;
527 					}
528 					if (npi_vpd_read_prop(handle, ep + 5,
529 					    "board-model", dlen,
530 					    vpdp->bd_model)) {
531 						fd_flags |= FD_BD_MODEL;
532 						goto next;
533 					}
534 					if (npi_vpd_read_prop(handle, ep + 5,
535 					    "version", dlen, vpdp->ver)) {
536 						fd_flags |= FD_FW_VERSION;
537 						goto next;
538 					}
539 				}
540 				goto next;
541 			} else if (vpd_rd(handle, ep) == 'I') {
542 				type = vpd_rd(handle, ep + 3);
543 				if (type == 'B') {
544 					dlen = vpd_rd(handle, ep + 4);
545 					if (npi_vpd_read_prop(handle, ep + 5,
546 					    "local-mac-address", dlen,
547 					    (char *)(vpdp->mac_addr))) {
548 						fd_flags |= FD_MAC_ADDR;
549 						goto next;
550 					}
551 					if (npi_vpd_read_prop(handle, ep + 5,
552 					    "num-mac-addresses", dlen,
553 					    (char *)&(vpdp->num_macs))) {
554 						fd_flags |= FD_NUM_MACS;
555 					}
556 				} else if (type == 'S') {
557 					dlen = vpd_rd(handle, ep + 4);
558 					if (npi_vpd_read_prop(handle, ep + 5,
559 					    "phy-type", dlen,
560 					    vpdp->phy_type)) {
561 						fd_flags |= FD_PHY_TYPE;
562 					}
563 				}
564 				goto next;
565 			} else {
566 				goto vpd_info_err;
567 			}
568 
569 next:
570 			if ((fd_flags & FD_ALL) == FD_ALL)
571 				goto vpd_success;
572 			ep += klen;
573 		}
574 		i += len + 3;
575 	}
576 
577 vpd_success:
578 	return (NPI_SUCCESS);
579 
580 vpd_info_err:
581 	return (NPI_FAILURE);
582 }
583 
584 static int
npi_vpd_read_prop(npi_handle_t handle,uint32_t ep,const char * prop,int len,char * val)585 npi_vpd_read_prop(npi_handle_t handle, uint32_t ep, const char *prop, int len,
586 		char *val)
587 {
588 	int prop_len =  strlen(prop) + 1;
589 	int i;
590 
591 	for (i = 0; i < prop_len; i++) {
592 		if (vpd_rd(handle, ep + i) != prop[i])
593 			return (0);
594 	}
595 
596 	ep += prop_len;
597 
598 	for (i = 0; i < len; i++)
599 		val[i] = vpd_rd(handle, ep + i);
600 	return (1);
601 }
602