Home | History | Annotate | Download | only in npi
      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
     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
     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
     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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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 
    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
    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
    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