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