110696SDavid.Hollister@Sun.COM /*
210696SDavid.Hollister@Sun.COM * CDDL HEADER START
310696SDavid.Hollister@Sun.COM *
410696SDavid.Hollister@Sun.COM * The contents of this file are subject to the terms of the
510696SDavid.Hollister@Sun.COM * Common Development and Distribution License (the "License").
610696SDavid.Hollister@Sun.COM * You may not use this file except in compliance with the License.
710696SDavid.Hollister@Sun.COM *
810696SDavid.Hollister@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910696SDavid.Hollister@Sun.COM * or http://www.opensolaris.org/os/licensing.
1010696SDavid.Hollister@Sun.COM * See the License for the specific language governing permissions
1110696SDavid.Hollister@Sun.COM * and limitations under the License.
1210696SDavid.Hollister@Sun.COM *
1310696SDavid.Hollister@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each
1410696SDavid.Hollister@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510696SDavid.Hollister@Sun.COM * If applicable, add the following below this CDDL HEADER, with the
1610696SDavid.Hollister@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying
1710696SDavid.Hollister@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner]
1810696SDavid.Hollister@Sun.COM *
1910696SDavid.Hollister@Sun.COM * CDDL HEADER END
2012060SDavid.Hollister@Sun.COM */
2112060SDavid.Hollister@Sun.COM /*
2212060SDavid.Hollister@Sun.COM * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2310696SDavid.Hollister@Sun.COM */
2410696SDavid.Hollister@Sun.COM
2510696SDavid.Hollister@Sun.COM /*
2610696SDavid.Hollister@Sun.COM * This file contains various support routines.
2710696SDavid.Hollister@Sun.COM */
2810696SDavid.Hollister@Sun.COM
2910696SDavid.Hollister@Sun.COM #include <sys/scsi/adapters/pmcs/pmcs.h>
3010696SDavid.Hollister@Sun.COM
3110696SDavid.Hollister@Sun.COM /*
3210696SDavid.Hollister@Sun.COM * SAS Topology Configuration
3310696SDavid.Hollister@Sun.COM */
3410696SDavid.Hollister@Sun.COM static int pmcs_flash_chunk(pmcs_hw_t *, uint8_t *);
3510696SDavid.Hollister@Sun.COM
3610696SDavid.Hollister@Sun.COM /*
3710696SDavid.Hollister@Sun.COM * Check current firmware version for correctness
3810696SDavid.Hollister@Sun.COM * and try to flash the correct firmware if what is
3910696SDavid.Hollister@Sun.COM * running isn't correct.
4010696SDavid.Hollister@Sun.COM *
4110696SDavid.Hollister@Sun.COM * Must be called after setup and MPI setup and
4210696SDavid.Hollister@Sun.COM * interrupts are enabled.
4310696SDavid.Hollister@Sun.COM */
4410696SDavid.Hollister@Sun.COM
4510696SDavid.Hollister@Sun.COM int
pmcs_firmware_update(pmcs_hw_t * pwp)4610696SDavid.Hollister@Sun.COM pmcs_firmware_update(pmcs_hw_t *pwp)
4710696SDavid.Hollister@Sun.COM {
4810696SDavid.Hollister@Sun.COM ddi_modhandle_t modhp;
4911980SDavid.Hollister@Sun.COM char buf[64], *bufp;
5010696SDavid.Hollister@Sun.COM int errno;
5110696SDavid.Hollister@Sun.COM uint8_t *cstart, *cend; /* Firmware image file */
5210696SDavid.Hollister@Sun.COM uint8_t *istart, *iend; /* ila */
5310696SDavid.Hollister@Sun.COM uint8_t *sstart, *send; /* SPCBoot */
5410696SDavid.Hollister@Sun.COM uint32_t *fwvp;
5510696SDavid.Hollister@Sun.COM int defret = 0;
5612119SJesse.Butler@Sun.COM int first_pass = 1;
5711980SDavid.Hollister@Sun.COM long fw_version, ila_version;
5811980SDavid.Hollister@Sun.COM uint8_t *fw_verp, *ila_verp;
5910696SDavid.Hollister@Sun.COM
6010696SDavid.Hollister@Sun.COM /*
6110696SDavid.Hollister@Sun.COM * If updating is disabled, we're done.
6210696SDavid.Hollister@Sun.COM */
6310696SDavid.Hollister@Sun.COM if (pwp->fw_disable_update) {
6411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
6510696SDavid.Hollister@Sun.COM "Firmware update disabled by conf file");
6610696SDavid.Hollister@Sun.COM return (0);
6710696SDavid.Hollister@Sun.COM }
6810696SDavid.Hollister@Sun.COM
6910696SDavid.Hollister@Sun.COM /*
7010696SDavid.Hollister@Sun.COM * If we're already running the right firmware, we're done.
7110696SDavid.Hollister@Sun.COM */
7210696SDavid.Hollister@Sun.COM if (pwp->fw == PMCS_FIRMWARE_VERSION) {
7310696SDavid.Hollister@Sun.COM if (pwp->fw_force_update == 0) {
7410696SDavid.Hollister@Sun.COM return (0);
7510696SDavid.Hollister@Sun.COM }
7610696SDavid.Hollister@Sun.COM
7711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
7810696SDavid.Hollister@Sun.COM "Firmware version matches, but still forcing update");
7910696SDavid.Hollister@Sun.COM }
8010696SDavid.Hollister@Sun.COM
8110696SDavid.Hollister@Sun.COM modhp = ddi_modopen(PMCS_FIRMWARE_FILENAME, KRTLD_MODE_FIRST, &errno);
8210696SDavid.Hollister@Sun.COM if (errno) {
8311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8410696SDavid.Hollister@Sun.COM "%s: Firmware module not available; will not upgrade",
8510696SDavid.Hollister@Sun.COM __func__);
8610696SDavid.Hollister@Sun.COM return (defret);
8710696SDavid.Hollister@Sun.COM }
8810696SDavid.Hollister@Sun.COM
8910696SDavid.Hollister@Sun.COM fwvp = ddi_modsym(modhp, PMCS_FIRMWARE_VERSION_NAME, &errno);
9010696SDavid.Hollister@Sun.COM if (errno) {
9111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
9211048SDavid.Hollister@Sun.COM "%s: unable to find symbol '%s'",
9310696SDavid.Hollister@Sun.COM __func__, PMCS_FIRMWARE_VERSION_NAME);
9410696SDavid.Hollister@Sun.COM (void) ddi_modclose(modhp);
9510696SDavid.Hollister@Sun.COM return (defret);
9610696SDavid.Hollister@Sun.COM }
9710696SDavid.Hollister@Sun.COM
9810696SDavid.Hollister@Sun.COM /*
9910696SDavid.Hollister@Sun.COM * If the firmware version from the module isn't what we expect,
10010696SDavid.Hollister@Sun.COM * and force updating is disabled, return the default (for this
10110696SDavid.Hollister@Sun.COM * mode of operation) value.
10210696SDavid.Hollister@Sun.COM */
10310696SDavid.Hollister@Sun.COM if (*fwvp != PMCS_FIRMWARE_VERSION) {
10410696SDavid.Hollister@Sun.COM if (pwp->fw_force_update == 0) {
10511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
10610696SDavid.Hollister@Sun.COM "%s: firmware module version wrong (0x%x)",
10710696SDavid.Hollister@Sun.COM __func__, *fwvp);
10810696SDavid.Hollister@Sun.COM (void) ddi_modclose(modhp);
10910696SDavid.Hollister@Sun.COM return (defret);
11010696SDavid.Hollister@Sun.COM }
11111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
11210696SDavid.Hollister@Sun.COM "%s: firmware module version wrong (0x%x) - update forced",
11310696SDavid.Hollister@Sun.COM __func__, *fwvp);
11410696SDavid.Hollister@Sun.COM }
11510696SDavid.Hollister@Sun.COM
11610696SDavid.Hollister@Sun.COM (void) snprintf(buf, sizeof (buf),
11710696SDavid.Hollister@Sun.COM PMCS_FIRMWARE_CODE_NAME PMCS_FIRMWARE_START_SUF);
11810696SDavid.Hollister@Sun.COM cstart = ddi_modsym(modhp, buf, &errno);
11910696SDavid.Hollister@Sun.COM if (errno) {
12011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
12111048SDavid.Hollister@Sun.COM "%s: unable to find symbol '%s'", __func__, buf);
12210696SDavid.Hollister@Sun.COM (void) ddi_modclose(modhp);
12310696SDavid.Hollister@Sun.COM return (defret);
12410696SDavid.Hollister@Sun.COM }
12510696SDavid.Hollister@Sun.COM
12610696SDavid.Hollister@Sun.COM (void) snprintf(buf, sizeof (buf),
12710696SDavid.Hollister@Sun.COM PMCS_FIRMWARE_CODE_NAME PMCS_FIRMWARE_END_SUF);
12810696SDavid.Hollister@Sun.COM cend = ddi_modsym(modhp, buf, &errno);
12910696SDavid.Hollister@Sun.COM if (errno) {
13011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
13111048SDavid.Hollister@Sun.COM "%s: unable to find symbol '%s'", __func__, buf);
13210696SDavid.Hollister@Sun.COM (void) ddi_modclose(modhp);
13310696SDavid.Hollister@Sun.COM return (defret);
13410696SDavid.Hollister@Sun.COM }
13510696SDavid.Hollister@Sun.COM
13610696SDavid.Hollister@Sun.COM (void) snprintf(buf, sizeof (buf),
13710696SDavid.Hollister@Sun.COM PMCS_FIRMWARE_ILA_NAME PMCS_FIRMWARE_START_SUF);
13810696SDavid.Hollister@Sun.COM istart = ddi_modsym(modhp, buf, &errno);
13910696SDavid.Hollister@Sun.COM if (errno) {
14011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
14111048SDavid.Hollister@Sun.COM "%s: unable to find symbol '%s'", __func__, buf);
14210696SDavid.Hollister@Sun.COM (void) ddi_modclose(modhp);
14310696SDavid.Hollister@Sun.COM return (defret);
14410696SDavid.Hollister@Sun.COM }
14510696SDavid.Hollister@Sun.COM
14610696SDavid.Hollister@Sun.COM (void) snprintf(buf, sizeof (buf),
14710696SDavid.Hollister@Sun.COM PMCS_FIRMWARE_ILA_NAME PMCS_FIRMWARE_END_SUF);
14810696SDavid.Hollister@Sun.COM iend = ddi_modsym(modhp, buf, &errno);
14910696SDavid.Hollister@Sun.COM if (errno) {
15011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
15111048SDavid.Hollister@Sun.COM "%s: unable to find symbol '%s'", __func__, buf);
15210696SDavid.Hollister@Sun.COM (void) ddi_modclose(modhp);
15310696SDavid.Hollister@Sun.COM return (defret);
15410696SDavid.Hollister@Sun.COM }
15510696SDavid.Hollister@Sun.COM
15610696SDavid.Hollister@Sun.COM (void) snprintf(buf, sizeof (buf),
15710696SDavid.Hollister@Sun.COM PMCS_FIRMWARE_SPCBOOT_NAME PMCS_FIRMWARE_START_SUF);
15810696SDavid.Hollister@Sun.COM sstart = ddi_modsym(modhp, buf, &errno);
15910696SDavid.Hollister@Sun.COM if (errno) {
16011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
16111048SDavid.Hollister@Sun.COM "%s: unable to find symbol '%s'", __func__, buf);
16210696SDavid.Hollister@Sun.COM (void) ddi_modclose(modhp);
16310696SDavid.Hollister@Sun.COM return (defret);
16410696SDavid.Hollister@Sun.COM }
16510696SDavid.Hollister@Sun.COM
16610696SDavid.Hollister@Sun.COM (void) snprintf(buf, sizeof (buf),
16710696SDavid.Hollister@Sun.COM PMCS_FIRMWARE_SPCBOOT_NAME PMCS_FIRMWARE_END_SUF);
16810696SDavid.Hollister@Sun.COM send = ddi_modsym(modhp, buf, &errno);
16910696SDavid.Hollister@Sun.COM if (errno) {
17011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
17111048SDavid.Hollister@Sun.COM "%s: unable to find symbol '%s'", __func__, buf);
17210696SDavid.Hollister@Sun.COM (void) ddi_modclose(modhp);
17310696SDavid.Hollister@Sun.COM return (defret);
17410696SDavid.Hollister@Sun.COM }
17510696SDavid.Hollister@Sun.COM
17610696SDavid.Hollister@Sun.COM /*
17711980SDavid.Hollister@Sun.COM * Get the ILA and firmware versions from the modules themselves
17811980SDavid.Hollister@Sun.COM */
17911980SDavid.Hollister@Sun.COM ila_verp = iend - PMCS_ILA_VER_OFFSET;
18011980SDavid.Hollister@Sun.COM (void) ddi_strtol((const char *)ila_verp, &bufp, 16, &ila_version);
18111980SDavid.Hollister@Sun.COM fw_verp = cend - PMCS_FW_VER_OFFSET;
18211980SDavid.Hollister@Sun.COM (void) ddi_strtol((const char *)fw_verp, &bufp, 16, &fw_version);
18311980SDavid.Hollister@Sun.COM
18411980SDavid.Hollister@Sun.COM /*
18511980SDavid.Hollister@Sun.COM * If force update is not set, verify that what we're loading is
18611980SDavid.Hollister@Sun.COM * what we expect.
18711980SDavid.Hollister@Sun.COM */
18811980SDavid.Hollister@Sun.COM if (pwp->fw_force_update == 0) {
18911980SDavid.Hollister@Sun.COM if (fw_version != PMCS_FIRMWARE_VERSION) {
19011980SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
19111980SDavid.Hollister@Sun.COM "Expected fw version 0x%x, not 0x%lx: not "
19211980SDavid.Hollister@Sun.COM "updating", PMCS_FIRMWARE_VERSION, fw_version);
19311980SDavid.Hollister@Sun.COM (void) ddi_modclose(modhp);
19411980SDavid.Hollister@Sun.COM return (defret);
19511980SDavid.Hollister@Sun.COM }
19611980SDavid.Hollister@Sun.COM }
19711980SDavid.Hollister@Sun.COM
19811980SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
19911980SDavid.Hollister@Sun.COM "Upgrading firmware on card from 0x%x to 0x%lx (ILA version 0x%lx)",
20011980SDavid.Hollister@Sun.COM pwp->fw, fw_version, ila_version);
20111980SDavid.Hollister@Sun.COM
20211980SDavid.Hollister@Sun.COM /*
20310696SDavid.Hollister@Sun.COM * The SPCBoot image must be updated first, and this is written to
20410696SDavid.Hollister@Sun.COM * SEEPROM, not flash.
20510696SDavid.Hollister@Sun.COM */
20610696SDavid.Hollister@Sun.COM if (pmcs_set_nvmd(pwp, PMCS_NVMD_SPCBOOT, sstart,
20710696SDavid.Hollister@Sun.COM (size_t)((size_t)send - (size_t)sstart)) == B_FALSE) {
20811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
20910696SDavid.Hollister@Sun.COM "%s: unable to flash '%s' segment",
21010696SDavid.Hollister@Sun.COM __func__, PMCS_FIRMWARE_SPCBOOT_NAME);
21110696SDavid.Hollister@Sun.COM (void) ddi_modclose(modhp);
21210696SDavid.Hollister@Sun.COM return (-1);
21310696SDavid.Hollister@Sun.COM }
21410696SDavid.Hollister@Sun.COM
21512119SJesse.Butler@Sun.COM repeat:
21612119SJesse.Butler@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
21712119SJesse.Butler@Sun.COM "%s: Beginning firmware update of %s image.",
21812119SJesse.Butler@Sun.COM __func__, (first_pass ? "first" : "second"));
21912119SJesse.Butler@Sun.COM
22010696SDavid.Hollister@Sun.COM if (pmcs_fw_flash(pwp, (void *)istart,
22110696SDavid.Hollister@Sun.COM (uint32_t)((size_t)iend - (size_t)istart))) {
22211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
22310696SDavid.Hollister@Sun.COM "%s: unable to flash '%s' segment",
22410696SDavid.Hollister@Sun.COM __func__, PMCS_FIRMWARE_ILA_NAME);
22510696SDavid.Hollister@Sun.COM (void) ddi_modclose(modhp);
22610696SDavid.Hollister@Sun.COM return (-1);
22710696SDavid.Hollister@Sun.COM }
22810696SDavid.Hollister@Sun.COM
22910696SDavid.Hollister@Sun.COM if (pmcs_fw_flash(pwp, (void *)cstart,
23010696SDavid.Hollister@Sun.COM (uint32_t)((size_t)cend - (size_t)cstart))) {
23111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
23210696SDavid.Hollister@Sun.COM "%s: unable to flash '%s' segment",
23310696SDavid.Hollister@Sun.COM __func__, PMCS_FIRMWARE_CODE_NAME);
23410696SDavid.Hollister@Sun.COM (void) ddi_modclose(modhp);
23510696SDavid.Hollister@Sun.COM return (-1);
23610696SDavid.Hollister@Sun.COM }
23710696SDavid.Hollister@Sun.COM
23810696SDavid.Hollister@Sun.COM if (pmcs_soft_reset(pwp, B_FALSE)) {
23911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
24010696SDavid.Hollister@Sun.COM "%s: soft reset after flash update failed", __func__);
24112119SJesse.Butler@Sun.COM (void) ddi_modclose(modhp);
24210696SDavid.Hollister@Sun.COM return (-1);
24310696SDavid.Hollister@Sun.COM } else {
24412119SJesse.Butler@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
24512119SJesse.Butler@Sun.COM "%s: %s image successfully upgraded.",
24612119SJesse.Butler@Sun.COM __func__, (first_pass ? "First" : "Second"));
24711692SJesse.Butler@Sun.COM pwp->last_reset_reason = PMCS_LAST_RST_FW_UPGRADE;
24810696SDavid.Hollister@Sun.COM }
24912119SJesse.Butler@Sun.COM
25012119SJesse.Butler@Sun.COM if (first_pass) {
25112119SJesse.Butler@Sun.COM first_pass = 0;
25212119SJesse.Butler@Sun.COM goto repeat;
25312119SJesse.Butler@Sun.COM }
25412119SJesse.Butler@Sun.COM
25512119SJesse.Butler@Sun.COM pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
25612119SJesse.Butler@Sun.COM "%s: Firmware successfully upgraded", __func__);
25712119SJesse.Butler@Sun.COM
25812119SJesse.Butler@Sun.COM (void) ddi_modclose(modhp);
25910696SDavid.Hollister@Sun.COM return (0);
26010696SDavid.Hollister@Sun.COM }
26110696SDavid.Hollister@Sun.COM
26210696SDavid.Hollister@Sun.COM /*
26310696SDavid.Hollister@Sun.COM * Flash firmware support
26410696SDavid.Hollister@Sun.COM * Called unlocked.
26510696SDavid.Hollister@Sun.COM */
26610696SDavid.Hollister@Sun.COM int
pmcs_fw_flash(pmcs_hw_t * pwp,pmcs_fw_hdr_t * hdr,uint32_t length)26710696SDavid.Hollister@Sun.COM pmcs_fw_flash(pmcs_hw_t *pwp, pmcs_fw_hdr_t *hdr, uint32_t length)
26810696SDavid.Hollister@Sun.COM {
26910696SDavid.Hollister@Sun.COM pmcs_fw_hdr_t *hp;
27010696SDavid.Hollister@Sun.COM uint8_t *wrk, *base;
27110696SDavid.Hollister@Sun.COM
27210696SDavid.Hollister@Sun.COM /*
27310696SDavid.Hollister@Sun.COM * Step 1- Validate firmware chunks within passed pointer.
27410696SDavid.Hollister@Sun.COM */
27510696SDavid.Hollister@Sun.COM hp = hdr;
27610696SDavid.Hollister@Sun.COM wrk = (uint8_t *)hdr;
27710696SDavid.Hollister@Sun.COM base = wrk;
27810696SDavid.Hollister@Sun.COM for (;;) {
27911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
28010696SDavid.Hollister@Sun.COM "%s: partition 0x%x, Length 0x%x", __func__,
28110696SDavid.Hollister@Sun.COM hp->destination_partition, ntohl(hp->firmware_length));
28210696SDavid.Hollister@Sun.COM if (ntohl(hp->firmware_length) == 0) {
28311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
28410696SDavid.Hollister@Sun.COM "%s: bad firmware length 0x%x",
28510696SDavid.Hollister@Sun.COM __func__, ntohl(hp->firmware_length));
28610696SDavid.Hollister@Sun.COM return (EINVAL);
28710696SDavid.Hollister@Sun.COM }
28810696SDavid.Hollister@Sun.COM wrk += (sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length));
28910696SDavid.Hollister@Sun.COM if (wrk == base + length) {
29010696SDavid.Hollister@Sun.COM break;
29110696SDavid.Hollister@Sun.COM }
29210696SDavid.Hollister@Sun.COM if (wrk > base + length) {
29311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
29410696SDavid.Hollister@Sun.COM "%s: out of bounds firmware length", __func__);
29510696SDavid.Hollister@Sun.COM return (EINVAL);
29610696SDavid.Hollister@Sun.COM }
29710696SDavid.Hollister@Sun.COM hp = (void *)wrk;
29810696SDavid.Hollister@Sun.COM }
29910696SDavid.Hollister@Sun.COM
30010696SDavid.Hollister@Sun.COM /*
30110696SDavid.Hollister@Sun.COM * Step 2- acquire scratch
30210696SDavid.Hollister@Sun.COM */
30310696SDavid.Hollister@Sun.COM (void) pmcs_acquire_scratch(pwp, B_TRUE);
30410696SDavid.Hollister@Sun.COM
30510696SDavid.Hollister@Sun.COM /*
30610696SDavid.Hollister@Sun.COM * Step 3- loop through firmware chunks and send each one
30710696SDavid.Hollister@Sun.COM * down to be flashed.
30810696SDavid.Hollister@Sun.COM */
30910696SDavid.Hollister@Sun.COM hp = hdr;
31010696SDavid.Hollister@Sun.COM wrk = (uint8_t *)hdr;
31110696SDavid.Hollister@Sun.COM base = wrk;
31210696SDavid.Hollister@Sun.COM for (;;) {
31310696SDavid.Hollister@Sun.COM if (pmcs_flash_chunk(pwp, wrk)) {
31410696SDavid.Hollister@Sun.COM pmcs_release_scratch(pwp);
31510696SDavid.Hollister@Sun.COM return (EIO);
31610696SDavid.Hollister@Sun.COM }
31710696SDavid.Hollister@Sun.COM wrk += (sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length));
31810696SDavid.Hollister@Sun.COM if (wrk == base + length) {
31910696SDavid.Hollister@Sun.COM break;
32010696SDavid.Hollister@Sun.COM }
32110696SDavid.Hollister@Sun.COM hp = (void *) wrk;
32210696SDavid.Hollister@Sun.COM }
32310696SDavid.Hollister@Sun.COM pmcs_release_scratch(pwp);
32410696SDavid.Hollister@Sun.COM return (0);
32510696SDavid.Hollister@Sun.COM }
32610696SDavid.Hollister@Sun.COM
32710696SDavid.Hollister@Sun.COM static int
pmcs_flash_chunk(pmcs_hw_t * pwp,uint8_t * chunk)32810696SDavid.Hollister@Sun.COM pmcs_flash_chunk(pmcs_hw_t *pwp, uint8_t *chunk)
32910696SDavid.Hollister@Sun.COM {
33010696SDavid.Hollister@Sun.COM pmcs_fw_hdr_t *hp;
33110696SDavid.Hollister@Sun.COM pmcwork_t *pwrk;
33210696SDavid.Hollister@Sun.COM uint32_t len, seg, off, result, amt, msg[PMCS_MSG_SIZE], *ptr;
33310696SDavid.Hollister@Sun.COM
33410696SDavid.Hollister@Sun.COM hp = (void *)chunk;
33510696SDavid.Hollister@Sun.COM len = sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length);
33610696SDavid.Hollister@Sun.COM
33710696SDavid.Hollister@Sun.COM seg = off = 0;
33810696SDavid.Hollister@Sun.COM while (off < len) {
33910696SDavid.Hollister@Sun.COM amt = PMCS_SCRATCH_SIZE;
34010696SDavid.Hollister@Sun.COM if (off + amt > len) {
34110696SDavid.Hollister@Sun.COM amt = len - off;
34210696SDavid.Hollister@Sun.COM }
34311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
34410696SDavid.Hollister@Sun.COM "%s: segment %d offset %u length %u",
34510696SDavid.Hollister@Sun.COM __func__, seg, off, amt);
34610696SDavid.Hollister@Sun.COM (void) memcpy(pwp->scratch, &chunk[off], amt);
34710696SDavid.Hollister@Sun.COM pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
34810696SDavid.Hollister@Sun.COM if (pwrk == NULL) {
34910696SDavid.Hollister@Sun.COM return (ENOMEM);
35010696SDavid.Hollister@Sun.COM }
35110696SDavid.Hollister@Sun.COM pwrk->arg = msg;
35210696SDavid.Hollister@Sun.COM msg[0] = LE_32(PMCS_HIPRI(pwp,
35310696SDavid.Hollister@Sun.COM PMCS_OQ_EVENTS, PMCIN_FW_FLASH_UPDATE));
35410696SDavid.Hollister@Sun.COM msg[1] = LE_32(pwrk->htag);
35510696SDavid.Hollister@Sun.COM msg[2] = LE_32(off);
35610696SDavid.Hollister@Sun.COM msg[3] = LE_32(amt);
35710696SDavid.Hollister@Sun.COM if (off == 0) {
35810696SDavid.Hollister@Sun.COM msg[4] = LE_32(len);
35910696SDavid.Hollister@Sun.COM } else {
36010696SDavid.Hollister@Sun.COM msg[4] = 0;
36110696SDavid.Hollister@Sun.COM }
36210696SDavid.Hollister@Sun.COM msg[5] = 0;
36310696SDavid.Hollister@Sun.COM msg[6] = 0;
36410696SDavid.Hollister@Sun.COM msg[7] = 0;
36510696SDavid.Hollister@Sun.COM msg[8] = 0;
36610696SDavid.Hollister@Sun.COM msg[9] = 0;
36710696SDavid.Hollister@Sun.COM msg[10] = 0;
36810696SDavid.Hollister@Sun.COM msg[11] = 0;
36910696SDavid.Hollister@Sun.COM msg[12] = LE_32(DWORD0(pwp->scratch_dma));
37010696SDavid.Hollister@Sun.COM msg[13] = LE_32(DWORD1(pwp->scratch_dma));
37110696SDavid.Hollister@Sun.COM msg[14] = LE_32(amt);
37210696SDavid.Hollister@Sun.COM msg[15] = 0;
37310696SDavid.Hollister@Sun.COM mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
37410696SDavid.Hollister@Sun.COM ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
37510696SDavid.Hollister@Sun.COM if (ptr == NULL) {
37610696SDavid.Hollister@Sun.COM mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
37710696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
37811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
37911048SDavid.Hollister@Sun.COM pmcs_nomsg, __func__);
38010696SDavid.Hollister@Sun.COM return (ENOMEM);
38110696SDavid.Hollister@Sun.COM }
38210696SDavid.Hollister@Sun.COM COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
38310696SDavid.Hollister@Sun.COM (void) memset(msg, 0xaf, sizeof (msg));
38410696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_ONCHIP;
38510696SDavid.Hollister@Sun.COM INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
38612120SDavid.Hollister@Sun.COM WAIT_FOR(pwrk, PMCS_FLASH_WAIT_TIME, result);
38710696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
38810696SDavid.Hollister@Sun.COM if (result) {
38911267SJesse.Butler@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
39011048SDavid.Hollister@Sun.COM pmcs_timeo, __func__);
39110696SDavid.Hollister@Sun.COM return (EIO);
39210696SDavid.Hollister@Sun.COM }
39310696SDavid.Hollister@Sun.COM switch (LE_32(msg[2])) {
39410696SDavid.Hollister@Sun.COM case FLASH_UPDATE_COMPLETE_PENDING_REBOOT:
39511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
39610696SDavid.Hollister@Sun.COM "%s: segment %d complete pending reboot",
39710696SDavid.Hollister@Sun.COM __func__, seg);
39810696SDavid.Hollister@Sun.COM break;
39910696SDavid.Hollister@Sun.COM case FLASH_UPDATE_IN_PROGRESS:
40011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
40110696SDavid.Hollister@Sun.COM "%s: segment %d downloaded", __func__, seg);
40210696SDavid.Hollister@Sun.COM break;
40310696SDavid.Hollister@Sun.COM case FLASH_UPDATE_HDR_ERR:
40411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
40510696SDavid.Hollister@Sun.COM "%s: segment %d header error", __func__, seg);
40610696SDavid.Hollister@Sun.COM return (EIO);
40710696SDavid.Hollister@Sun.COM case FLASH_UPDATE_OFFSET_ERR:
40811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
40910696SDavid.Hollister@Sun.COM "%s: segment %d offset error", __func__, seg);
41010696SDavid.Hollister@Sun.COM return (EIO);
41110696SDavid.Hollister@Sun.COM case FLASH_UPDATE_UPDATE_CRC_ERR:
41211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
41310696SDavid.Hollister@Sun.COM "%s: segment %d update crc error", __func__, seg);
41410696SDavid.Hollister@Sun.COM return (EIO);
41510696SDavid.Hollister@Sun.COM case FLASH_UPDATE_LENGTH_ERR:
41611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
41710696SDavid.Hollister@Sun.COM "%s: segment %d length error", __func__, seg);
41810696SDavid.Hollister@Sun.COM return (EIO);
41910696SDavid.Hollister@Sun.COM case FLASH_UPDATE_HW_ERR:
42011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
42110696SDavid.Hollister@Sun.COM "%s: segment %d hw error", __func__, seg);
42210696SDavid.Hollister@Sun.COM return (EIO);
42310696SDavid.Hollister@Sun.COM case FLASH_UPDATE_DNLD_NOT_SUPPORTED:
42411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
42510696SDavid.Hollister@Sun.COM "%s: segment %d download not supported error",
42610696SDavid.Hollister@Sun.COM __func__, seg);
42710696SDavid.Hollister@Sun.COM return (EIO);
42810696SDavid.Hollister@Sun.COM case FLASH_UPDATE_DISABLED:
42911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
43010696SDavid.Hollister@Sun.COM "%s: segment %d update disabled error",
43110696SDavid.Hollister@Sun.COM __func__, seg);
43210696SDavid.Hollister@Sun.COM return (EIO);
43310696SDavid.Hollister@Sun.COM default:
43411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
43510696SDavid.Hollister@Sun.COM "%s: segment %d unknown error %x",
43610696SDavid.Hollister@Sun.COM __func__, seg, msg[2]);
43710696SDavid.Hollister@Sun.COM return (EIO);
43810696SDavid.Hollister@Sun.COM }
43910696SDavid.Hollister@Sun.COM off += amt;
44010696SDavid.Hollister@Sun.COM seg++;
44110696SDavid.Hollister@Sun.COM }
44210696SDavid.Hollister@Sun.COM return (0);
44310696SDavid.Hollister@Sun.COM }
44410696SDavid.Hollister@Sun.COM
44510696SDavid.Hollister@Sun.COM /*
44610696SDavid.Hollister@Sun.COM * pmcs_validate_vpd
44710696SDavid.Hollister@Sun.COM *
44810696SDavid.Hollister@Sun.COM * Input: softstate pointer and pointer to vpd data buffer
44910696SDavid.Hollister@Sun.COM * Returns: B_TRUE if VPD data looks OK, B_FALSE otherwise
45010696SDavid.Hollister@Sun.COM */
45110696SDavid.Hollister@Sun.COM static boolean_t
pmcs_validate_vpd(pmcs_hw_t * pwp,uint8_t * data)45210696SDavid.Hollister@Sun.COM pmcs_validate_vpd(pmcs_hw_t *pwp, uint8_t *data)
45310696SDavid.Hollister@Sun.COM {
45410696SDavid.Hollister@Sun.COM pmcs_vpd_header_t *vpd_header;
45510696SDavid.Hollister@Sun.COM uint8_t *bufp, kv_len, *chksump, chksum = 0;
45610696SDavid.Hollister@Sun.COM char tbuf[80];
45710696SDavid.Hollister@Sun.COM char prop[24];
45810696SDavid.Hollister@Sun.COM int idx, str_len;
45910696SDavid.Hollister@Sun.COM uint16_t strid_length, chksum_len;
46010696SDavid.Hollister@Sun.COM uint64_t wwid;
46110696SDavid.Hollister@Sun.COM pmcs_vpd_kv_t *vkvp;
46210696SDavid.Hollister@Sun.COM
46310696SDavid.Hollister@Sun.COM vpd_header = (pmcs_vpd_header_t *)data;
46410696SDavid.Hollister@Sun.COM
46510696SDavid.Hollister@Sun.COM /*
46610696SDavid.Hollister@Sun.COM * Make sure we understand the format of this data
46710696SDavid.Hollister@Sun.COM */
46810696SDavid.Hollister@Sun.COM
469*12698Ssrikanth.suravajhala@oracle.com /*
470*12698Ssrikanth.suravajhala@oracle.com * Only VPD version 1 is VALID for Thebe-INT cards and
471*12698Ssrikanth.suravajhala@oracle.com * Only VPD version 2 is valid for Thebe-EXT cards
472*12698Ssrikanth.suravajhala@oracle.com */
473*12698Ssrikanth.suravajhala@oracle.com if ((vpd_header->eeprom_version == PMCS_EEPROM_INT_VERSION &&
474*12698Ssrikanth.suravajhala@oracle.com vpd_header->subsys_pid[0] == PMCS_EEPROM_INT_SSID_BYTE1 &&
475*12698Ssrikanth.suravajhala@oracle.com vpd_header->subsys_pid[1] == PMCS_EEPROM_INT_SSID_BYTE2) ||
476*12698Ssrikanth.suravajhala@oracle.com (vpd_header->eeprom_version == PMCS_EEPROM_EXT_VERSION &&
477*12698Ssrikanth.suravajhala@oracle.com vpd_header->subsys_pid[0] == PMCS_EEPROM_EXT_SSID_BYTE1 &&
478*12698Ssrikanth.suravajhala@oracle.com vpd_header->subsys_pid[1] == PMCS_EEPROM_EXT_SSID_BYTE2)) {
479*12698Ssrikanth.suravajhala@oracle.com goto valid_version;
480*12698Ssrikanth.suravajhala@oracle.com } else {
48111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
482*12698Ssrikanth.suravajhala@oracle.com "%s: Detected Thebe card with SSID(%02x%02x)", __func__,
483*12698Ssrikanth.suravajhala@oracle.com vpd_header->subsys_pid[0], vpd_header->subsys_pid[1]);
484*12698Ssrikanth.suravajhala@oracle.com pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
485*12698Ssrikanth.suravajhala@oracle.com "%s: EEPROM(%d) unsupported; requires %d for INT(%02x%02x) "
486*12698Ssrikanth.suravajhala@oracle.com " and %d for EXT(%02x%02x) cards.", __func__,
487*12698Ssrikanth.suravajhala@oracle.com vpd_header->eeprom_version,
488*12698Ssrikanth.suravajhala@oracle.com PMCS_EEPROM_INT_VERSION, PMCS_EEPROM_INT_SSID_BYTE1,
489*12698Ssrikanth.suravajhala@oracle.com PMCS_EEPROM_INT_SSID_BYTE2, PMCS_EEPROM_EXT_VERSION,
490*12698Ssrikanth.suravajhala@oracle.com PMCS_EEPROM_EXT_SSID_BYTE1, PMCS_EEPROM_EXT_SSID_BYTE2);
49110696SDavid.Hollister@Sun.COM return (B_FALSE);
49210696SDavid.Hollister@Sun.COM }
49310696SDavid.Hollister@Sun.COM
494*12698Ssrikanth.suravajhala@oracle.com valid_version:
49510696SDavid.Hollister@Sun.COM /*
49610696SDavid.Hollister@Sun.COM * Do we have a valid SAS WWID?
49710696SDavid.Hollister@Sun.COM */
49810696SDavid.Hollister@Sun.COM if (((vpd_header->hba_sas_wwid[0] & 0xf0) >> 4) != NAA_IEEE_REG) {
49911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
50010696SDavid.Hollister@Sun.COM "%s: SAS WWN has invalid NAA (%d)", __func__,
50110696SDavid.Hollister@Sun.COM ((vpd_header->hba_sas_wwid[0] & 0xf0) >> 4));
50210696SDavid.Hollister@Sun.COM return (B_FALSE);
50310696SDavid.Hollister@Sun.COM }
50410696SDavid.Hollister@Sun.COM wwid = pmcs_barray2wwn(vpd_header->hba_sas_wwid);
50510696SDavid.Hollister@Sun.COM for (idx = 0; idx < PMCS_MAX_PORTS; idx++) {
50610696SDavid.Hollister@Sun.COM pwp->sas_wwns[idx] = wwid + idx;
50710696SDavid.Hollister@Sun.COM }
50810696SDavid.Hollister@Sun.COM
50910696SDavid.Hollister@Sun.COM if (vpd_header->vpd_start_byte != PMCS_VPD_START) {
51011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
51110696SDavid.Hollister@Sun.COM "%s: Didn't see VPD start byte", __func__);
51210696SDavid.Hollister@Sun.COM return (B_FALSE);
51310696SDavid.Hollister@Sun.COM }
51410696SDavid.Hollister@Sun.COM
51510696SDavid.Hollister@Sun.COM /*
51610696SDavid.Hollister@Sun.COM * We only checksum the VPD data between (and including) VPD Start byte
51710696SDavid.Hollister@Sun.COM * and the checksum value byte. The length of this data for CRC is
51810696SDavid.Hollister@Sun.COM * 15 less than the length indicated in vpd_length field of the header.
51910696SDavid.Hollister@Sun.COM * 8 (SAS WWN) + 2 (subsystem ID) + 2 (subsystem vendor ID) +
52010696SDavid.Hollister@Sun.COM * 1 (end tag) + 2 (hex byte CRC, different from this one) = 15 bytes
52110696SDavid.Hollister@Sun.COM */
52210696SDavid.Hollister@Sun.COM /*
52310696SDavid.Hollister@Sun.COM * VPD length (little endian format) is represented as byte-array field
52410696SDavid.Hollister@Sun.COM * & read the following way to avoid alignment issues (in SPARC)
52510696SDavid.Hollister@Sun.COM */
52610696SDavid.Hollister@Sun.COM chksum_len = ((vpd_header->vpd_length[1] << 8) |
52710696SDavid.Hollister@Sun.COM (vpd_header->vpd_length[0])) - 15;
52810696SDavid.Hollister@Sun.COM /* Validate VPD data checksum */
52910696SDavid.Hollister@Sun.COM chksump = (uint8_t *)&vpd_header->vpd_start_byte;
53010696SDavid.Hollister@Sun.COM ASSERT (*chksump == PMCS_VPD_START);
53110696SDavid.Hollister@Sun.COM for (idx = 0; idx < chksum_len; idx++, chksump++) {
53210696SDavid.Hollister@Sun.COM chksum += *chksump;
53310696SDavid.Hollister@Sun.COM }
53410696SDavid.Hollister@Sun.COM ASSERT (*chksump == PMCS_VPD_END);
53510696SDavid.Hollister@Sun.COM if (chksum) {
53612060SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
53712060SDavid.Hollister@Sun.COM "%s: VPD checksum failure", __func__);
53812060SDavid.Hollister@Sun.COM return (B_FALSE);
53910696SDavid.Hollister@Sun.COM }
54010696SDavid.Hollister@Sun.COM
54110696SDavid.Hollister@Sun.COM /*
54210696SDavid.Hollister@Sun.COM * Get length of string ID tag and read it.
54310696SDavid.Hollister@Sun.COM */
54410696SDavid.Hollister@Sun.COM bufp = (uint8_t *)&vpd_header->vpd_start_byte;
54510696SDavid.Hollister@Sun.COM bufp += 3; /* Skip the start byte and length */
54610696SDavid.Hollister@Sun.COM /*
54710696SDavid.Hollister@Sun.COM * String ID tag length (little endian format) is represented as
54810696SDavid.Hollister@Sun.COM * byte-array & read the following way to avoid alignment issues
54910696SDavid.Hollister@Sun.COM * (in SPARC)
55010696SDavid.Hollister@Sun.COM */
55110696SDavid.Hollister@Sun.COM strid_length = (vpd_header->strid_length[1] << 8) |
55210696SDavid.Hollister@Sun.COM (vpd_header->strid_length[0]);
55310696SDavid.Hollister@Sun.COM if (strid_length > 79) {
55410696SDavid.Hollister@Sun.COM strid_length = 79;
55510696SDavid.Hollister@Sun.COM }
55610696SDavid.Hollister@Sun.COM bcopy(bufp, tbuf, strid_length);
55710696SDavid.Hollister@Sun.COM tbuf[strid_length] = 0;
55810696SDavid.Hollister@Sun.COM
55911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
56010696SDavid.Hollister@Sun.COM "%s: Product Name: '%s'", __func__, tbuf);
56110696SDavid.Hollister@Sun.COM pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_MODEL_NAME, tbuf);
56210696SDavid.Hollister@Sun.COM
56310696SDavid.Hollister@Sun.COM /*
56410696SDavid.Hollister@Sun.COM * Skip VPD-R tag and length of read-only tag, then start reading
56510696SDavid.Hollister@Sun.COM * keyword/value pairs
56610696SDavid.Hollister@Sun.COM */
56710696SDavid.Hollister@Sun.COM bufp += strid_length; /* Skip to VPD-R tag */
56810696SDavid.Hollister@Sun.COM bufp += 3; /* Skip VPD-R tag and length of VPD-R data */
56910696SDavid.Hollister@Sun.COM
57010696SDavid.Hollister@Sun.COM vkvp = (pmcs_vpd_kv_t *)bufp;
57110696SDavid.Hollister@Sun.COM
57210696SDavid.Hollister@Sun.COM while (vkvp->keyword[0] != PMCS_VPD_END) {
57310696SDavid.Hollister@Sun.COM tbuf[0] = 0;
57410696SDavid.Hollister@Sun.COM str_len = snprintf(tbuf, 80, "VPD: %c%c = <",
57510696SDavid.Hollister@Sun.COM vkvp->keyword[0], vkvp->keyword[1]);
57610696SDavid.Hollister@Sun.COM
57710696SDavid.Hollister@Sun.COM kv_len = vkvp->value_length;
57810696SDavid.Hollister@Sun.COM for (idx = 0; idx < kv_len; idx++) {
57910696SDavid.Hollister@Sun.COM tbuf[str_len + idx] = vkvp->value[idx];
58010696SDavid.Hollister@Sun.COM prop[idx] = vkvp->value[idx];
58110696SDavid.Hollister@Sun.COM }
58210696SDavid.Hollister@Sun.COM prop[idx] = '\0';
58310696SDavid.Hollister@Sun.COM str_len += kv_len;
58410696SDavid.Hollister@Sun.COM tbuf[str_len] = '>';
58510696SDavid.Hollister@Sun.COM tbuf[str_len + 1] = 0;
58611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "%s (Len: 0x%x)",
58711048SDavid.Hollister@Sun.COM tbuf, kv_len);
58810696SDavid.Hollister@Sun.COM
58910696SDavid.Hollister@Sun.COM /* Keyword is Manufacturer */
59010696SDavid.Hollister@Sun.COM if ((vkvp->keyword[0] == 'M') && (vkvp->keyword[1] == 'N')) {
59110696SDavid.Hollister@Sun.COM pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING,
59210696SDavid.Hollister@Sun.COM PMCS_MANUFACTURER, prop);
59310696SDavid.Hollister@Sun.COM }
59410696SDavid.Hollister@Sun.COM /* Keyword is Serial Number */
59510696SDavid.Hollister@Sun.COM if ((vkvp->keyword[0] == 'S') && (vkvp->keyword[1] == 'N')) {
59610696SDavid.Hollister@Sun.COM pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING,
59710696SDavid.Hollister@Sun.COM PMCS_SERIAL_NUMBER, prop);
59810696SDavid.Hollister@Sun.COM }
59910696SDavid.Hollister@Sun.COM
60010696SDavid.Hollister@Sun.COM vkvp = (pmcs_vpd_kv_t *)(bufp + 3 + kv_len);
60110696SDavid.Hollister@Sun.COM bufp += kv_len + 3;
60210696SDavid.Hollister@Sun.COM }
60310696SDavid.Hollister@Sun.COM
60410696SDavid.Hollister@Sun.COM return (B_TRUE);
60510696SDavid.Hollister@Sun.COM }
60610696SDavid.Hollister@Sun.COM
60710696SDavid.Hollister@Sun.COM /*
60810696SDavid.Hollister@Sun.COM * pmcs_get_nvmd
60910696SDavid.Hollister@Sun.COM *
61010696SDavid.Hollister@Sun.COM * This function will read the requested data from the non-volatile
61110696SDavid.Hollister@Sun.COM * storage on the card. This could mean SEEPROM, VPD, or other areas
61210696SDavid.Hollister@Sun.COM * as defined by the PM8001 programmer's manual.
61310696SDavid.Hollister@Sun.COM *
61410696SDavid.Hollister@Sun.COM * nvmd_type: The data type being requested
61510696SDavid.Hollister@Sun.COM * nvmd: NVM device to access (IOP/AAP1)
61610696SDavid.Hollister@Sun.COM * offset: Must be 4K alignment
61710696SDavid.Hollister@Sun.COM * buf: Pointer to memory region for retrieved data
61810696SDavid.Hollister@Sun.COM * size_left: Total available bytes left in buf
61910696SDavid.Hollister@Sun.COM *
62010696SDavid.Hollister@Sun.COM * Returns: non-negative on success, -1 on failure
62110696SDavid.Hollister@Sun.COM */
62210696SDavid.Hollister@Sun.COM
62310696SDavid.Hollister@Sun.COM /*ARGSUSED*/
62410696SDavid.Hollister@Sun.COM int
pmcs_get_nvmd(pmcs_hw_t * pwp,pmcs_nvmd_type_t nvmd_type,uint8_t nvmd,uint32_t offset,char * buf,uint32_t size_left)62510696SDavid.Hollister@Sun.COM pmcs_get_nvmd(pmcs_hw_t *pwp, pmcs_nvmd_type_t nvmd_type, uint8_t nvmd,
62610696SDavid.Hollister@Sun.COM uint32_t offset, char *buf, uint32_t size_left)
62710696SDavid.Hollister@Sun.COM {
62810696SDavid.Hollister@Sun.COM pmcs_get_nvmd_cmd_t iomb;
62910696SDavid.Hollister@Sun.COM pmcwork_t *workp;
63010696SDavid.Hollister@Sun.COM uint8_t *chunkp;
63110696SDavid.Hollister@Sun.COM uint32_t *ptr, ibq, *iombp;
63210696SDavid.Hollister@Sun.COM uint32_t dlen;
63310696SDavid.Hollister@Sun.COM uint16_t status;
63410696SDavid.Hollister@Sun.COM uint8_t tdas_nvmd, ip, tda, tbn_tdps;
63510696SDavid.Hollister@Sun.COM uint8_t doa[3];
63610696SDavid.Hollister@Sun.COM int32_t result = -1, i = 0;
63710696SDavid.Hollister@Sun.COM
63810696SDavid.Hollister@Sun.COM switch (nvmd_type) {
63910696SDavid.Hollister@Sun.COM case PMCS_NVMD_VPD:
64010696SDavid.Hollister@Sun.COM tdas_nvmd = PMCIN_NVMD_TDPS_1 | PMCIN_NVMD_TWI;
64110696SDavid.Hollister@Sun.COM tda = PMCIN_TDA_PAGE(2);
64210696SDavid.Hollister@Sun.COM tbn_tdps = PMCIN_NVMD_TBN(0) | PMCIN_NVMD_TDPS_8;
64310696SDavid.Hollister@Sun.COM ip = PMCIN_NVMD_INDIRECT_PLD;
64410696SDavid.Hollister@Sun.COM dlen = LE_32(PMCS_SEEPROM_PAGE_SIZE);
64510696SDavid.Hollister@Sun.COM doa[0] = 0;
64610696SDavid.Hollister@Sun.COM doa[1] = 0;
64710696SDavid.Hollister@Sun.COM doa[2] = 0;
64810696SDavid.Hollister@Sun.COM break;
64910696SDavid.Hollister@Sun.COM case PMCS_NVMD_REG_DUMP:
65010696SDavid.Hollister@Sun.COM tdas_nvmd = nvmd;
65110696SDavid.Hollister@Sun.COM tda = 0;
65210696SDavid.Hollister@Sun.COM tbn_tdps = 0;
65310696SDavid.Hollister@Sun.COM ip = PMCIN_NVMD_INDIRECT_PLD;
65410696SDavid.Hollister@Sun.COM dlen = LE_32(PMCS_REGISTER_DUMP_BLOCK_SIZE);
65510696SDavid.Hollister@Sun.COM doa[0] = offset & 0xff;
65610696SDavid.Hollister@Sun.COM doa[1] = (offset >> 8) & 0xff;
65710696SDavid.Hollister@Sun.COM doa[2] = (offset >> 16) & 0xff;
65810696SDavid.Hollister@Sun.COM break;
65910696SDavid.Hollister@Sun.COM case PMCS_NVMD_EVENT_LOG:
66010696SDavid.Hollister@Sun.COM tdas_nvmd = nvmd;
66110696SDavid.Hollister@Sun.COM tda = 0;
66210696SDavid.Hollister@Sun.COM tbn_tdps = 0;
66310696SDavid.Hollister@Sun.COM ip = PMCIN_NVMD_INDIRECT_PLD;
66410696SDavid.Hollister@Sun.COM dlen = LE_32(PMCS_REGISTER_DUMP_BLOCK_SIZE);
66510696SDavid.Hollister@Sun.COM offset = offset + PMCS_NVMD_EVENT_LOG_OFFSET;
66610696SDavid.Hollister@Sun.COM doa[0] = offset & 0xff;
66710696SDavid.Hollister@Sun.COM doa[1] = (offset >> 8) & 0xff;
66810696SDavid.Hollister@Sun.COM doa[2] = (offset >> 16) & 0xff;
66910696SDavid.Hollister@Sun.COM break;
67010696SDavid.Hollister@Sun.COM default:
67111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
67210696SDavid.Hollister@Sun.COM "%s: Invalid nvmd type: %d", __func__, nvmd_type);
67310696SDavid.Hollister@Sun.COM return (-1);
67410696SDavid.Hollister@Sun.COM }
67510696SDavid.Hollister@Sun.COM
67610696SDavid.Hollister@Sun.COM workp = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
67710696SDavid.Hollister@Sun.COM if (workp == NULL) {
67811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
67910696SDavid.Hollister@Sun.COM "%s: Unable to get work struct", __func__);
68010696SDavid.Hollister@Sun.COM return (-1);
68110696SDavid.Hollister@Sun.COM }
68210696SDavid.Hollister@Sun.COM
68310696SDavid.Hollister@Sun.COM ptr = &iomb.header;
68410696SDavid.Hollister@Sun.COM bzero(ptr, sizeof (pmcs_get_nvmd_cmd_t));
68510696SDavid.Hollister@Sun.COM *ptr = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_GENERAL, PMCIN_GET_NVMD_DATA));
68610696SDavid.Hollister@Sun.COM workp->arg = (void *)&iomb;
68710696SDavid.Hollister@Sun.COM iomb.htag = LE_32(workp->htag);
68810696SDavid.Hollister@Sun.COM iomb.ip = ip;
68910696SDavid.Hollister@Sun.COM iomb.tbn_tdps = tbn_tdps;
69010696SDavid.Hollister@Sun.COM iomb.tda = tda;
69110696SDavid.Hollister@Sun.COM iomb.tdas_nvmd = tdas_nvmd;
69210696SDavid.Hollister@Sun.COM iomb.ipbal = LE_32(DWORD0(pwp->flash_chunk_addr));
69310696SDavid.Hollister@Sun.COM iomb.ipbah = LE_32(DWORD1(pwp->flash_chunk_addr));
69410696SDavid.Hollister@Sun.COM iomb.ipdl = dlen;
69510696SDavid.Hollister@Sun.COM iomb.doa[0] = doa[0];
69610696SDavid.Hollister@Sun.COM iomb.doa[1] = doa[1];
69710696SDavid.Hollister@Sun.COM iomb.doa[2] = doa[2];
69810696SDavid.Hollister@Sun.COM
69910696SDavid.Hollister@Sun.COM /*
70010696SDavid.Hollister@Sun.COM * ptr will now point to the inbound queue message
70110696SDavid.Hollister@Sun.COM */
70210696SDavid.Hollister@Sun.COM GET_IO_IQ_ENTRY(pwp, ptr, 0, ibq);
70310696SDavid.Hollister@Sun.COM if (ptr == NULL) {
70411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
70511048SDavid.Hollister@Sun.COM "!%s: Unable to get IQ entry", __func__);
70610696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, workp);
70710696SDavid.Hollister@Sun.COM return (-1);
70810696SDavid.Hollister@Sun.COM }
70910696SDavid.Hollister@Sun.COM
71010696SDavid.Hollister@Sun.COM bzero(ptr, PMCS_MSG_SIZE << 2); /* PMCS_MSG_SIZE is in dwords */
71110696SDavid.Hollister@Sun.COM iombp = (uint32_t *)&iomb;
71210696SDavid.Hollister@Sun.COM COPY_MESSAGE(ptr, iombp, sizeof (pmcs_get_nvmd_cmd_t) >> 2);
71310696SDavid.Hollister@Sun.COM workp->state = PMCS_WORK_STATE_ONCHIP;
71410696SDavid.Hollister@Sun.COM INC_IQ_ENTRY(pwp, ibq);
71510696SDavid.Hollister@Sun.COM
71610696SDavid.Hollister@Sun.COM WAIT_FOR(workp, 1000, result);
71710696SDavid.Hollister@Sun.COM ptr = workp->arg;
71810696SDavid.Hollister@Sun.COM if (result) {
71910696SDavid.Hollister@Sun.COM pmcs_timed_out(pwp, workp->htag, __func__);
72010696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, workp);
72110696SDavid.Hollister@Sun.COM return (-1);
72210696SDavid.Hollister@Sun.COM }
72310696SDavid.Hollister@Sun.COM status = LE_32(*(ptr + 3)) & 0xffff;
72410696SDavid.Hollister@Sun.COM if (status != PMCS_NVMD_STAT_SUCCESS) {
72511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
72611048SDavid.Hollister@Sun.COM "%s: Error, status = 0x%04x", __func__, status);
72710696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, workp);
72810696SDavid.Hollister@Sun.COM return (-1);
72910696SDavid.Hollister@Sun.COM }
73010696SDavid.Hollister@Sun.COM
73110696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, workp);
73210696SDavid.Hollister@Sun.COM
73310696SDavid.Hollister@Sun.COM if (ddi_dma_sync(pwp->cip_handles, 0, 0,
73410696SDavid.Hollister@Sun.COM DDI_DMA_SYNC_FORKERNEL) != DDI_SUCCESS) {
73511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
73611048SDavid.Hollister@Sun.COM "Condition check failed at %s():%d", __func__, __LINE__);
73710696SDavid.Hollister@Sun.COM }
73810696SDavid.Hollister@Sun.COM chunkp = (uint8_t *)pwp->flash_chunkp;
73910696SDavid.Hollister@Sun.COM
74010696SDavid.Hollister@Sun.COM switch (nvmd) {
74110696SDavid.Hollister@Sun.COM case PMCIN_NVMD_VPD:
74210696SDavid.Hollister@Sun.COM if (pmcs_validate_vpd(pwp, chunkp)) {
74310696SDavid.Hollister@Sun.COM result = 0;
74410696SDavid.Hollister@Sun.COM } else {
74510696SDavid.Hollister@Sun.COM result = -1;
74610696SDavid.Hollister@Sun.COM }
74710696SDavid.Hollister@Sun.COM break;
74810696SDavid.Hollister@Sun.COM case PMCIN_NVMD_AAP1:
74910696SDavid.Hollister@Sun.COM case PMCIN_NVMD_IOP:
75010696SDavid.Hollister@Sun.COM ASSERT(buf);
75110696SDavid.Hollister@Sun.COM i = 0;
75210696SDavid.Hollister@Sun.COM if (nvmd_type == PMCS_NVMD_REG_DUMP) {
75310696SDavid.Hollister@Sun.COM while ((i < PMCS_FLASH_CHUNK_SIZE) &&
75410696SDavid.Hollister@Sun.COM (chunkp[i] != 0xff) && (chunkp[i] != '\0')) {
75510696SDavid.Hollister@Sun.COM (void) snprintf(&buf[i], (size_left - i),
75610696SDavid.Hollister@Sun.COM "%c", chunkp[i]);
75710696SDavid.Hollister@Sun.COM i++;
75810696SDavid.Hollister@Sun.COM }
75910696SDavid.Hollister@Sun.COM } else if (nvmd_type == PMCS_NVMD_EVENT_LOG) {
76010696SDavid.Hollister@Sun.COM i = pmcs_dump_binary(pwp, pwp->flash_chunkp, 0,
76110696SDavid.Hollister@Sun.COM (PMCS_FLASH_CHUNK_SIZE >> 2), buf, size_left);
76210696SDavid.Hollister@Sun.COM }
76310696SDavid.Hollister@Sun.COM result = i;
76410696SDavid.Hollister@Sun.COM break;
76510696SDavid.Hollister@Sun.COM default:
76611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
76711048SDavid.Hollister@Sun.COM "UNKNOWN NVMD DEVICE");
76810696SDavid.Hollister@Sun.COM return (-1);
76910696SDavid.Hollister@Sun.COM }
77010696SDavid.Hollister@Sun.COM
77110696SDavid.Hollister@Sun.COM return (result);
77210696SDavid.Hollister@Sun.COM }
77310696SDavid.Hollister@Sun.COM
77410696SDavid.Hollister@Sun.COM /*
77510696SDavid.Hollister@Sun.COM * pmcs_set_nvmd
77610696SDavid.Hollister@Sun.COM *
77710696SDavid.Hollister@Sun.COM * This function will write the requested data to non-volatile storage
77810696SDavid.Hollister@Sun.COM * on the HBA. This could mean SEEPROM, VPD, or other areas as defined by
77910696SDavid.Hollister@Sun.COM * the PM8001 programmer's manual.
78010696SDavid.Hollister@Sun.COM *
78110696SDavid.Hollister@Sun.COM * nvmd_type: The data type to be written
78210696SDavid.Hollister@Sun.COM * buf: Pointer to memory region for data to write
78310696SDavid.Hollister@Sun.COM * len: Length of the data buffer
78410696SDavid.Hollister@Sun.COM *
78510696SDavid.Hollister@Sun.COM * Returns: B_TRUE on success, B_FALSE on failure
78610696SDavid.Hollister@Sun.COM */
78710696SDavid.Hollister@Sun.COM
78810696SDavid.Hollister@Sun.COM boolean_t
pmcs_set_nvmd(pmcs_hw_t * pwp,pmcs_nvmd_type_t nvmd_type,uint8_t * buf,size_t len)78910696SDavid.Hollister@Sun.COM pmcs_set_nvmd(pmcs_hw_t *pwp, pmcs_nvmd_type_t nvmd_type, uint8_t *buf,
79010696SDavid.Hollister@Sun.COM size_t len)
79110696SDavid.Hollister@Sun.COM {
79210696SDavid.Hollister@Sun.COM pmcs_set_nvmd_cmd_t iomb;
79310696SDavid.Hollister@Sun.COM pmcwork_t *workp;
79410696SDavid.Hollister@Sun.COM uint32_t *ptr, ibq, *iombp;
79510696SDavid.Hollister@Sun.COM uint32_t dlen;
79610696SDavid.Hollister@Sun.COM uint16_t status;
79710696SDavid.Hollister@Sun.COM uint8_t tdas_nvmd, ip;
79810696SDavid.Hollister@Sun.COM int result;
79910696SDavid.Hollister@Sun.COM
80010696SDavid.Hollister@Sun.COM switch (nvmd_type) {
80110696SDavid.Hollister@Sun.COM case PMCS_NVMD_SPCBOOT:
80210696SDavid.Hollister@Sun.COM tdas_nvmd = PMCIN_NVMD_SEEPROM;
80310696SDavid.Hollister@Sun.COM ip = PMCIN_NVMD_INDIRECT_PLD;
80410696SDavid.Hollister@Sun.COM ASSERT((len >= PMCS_SPCBOOT_MIN_SIZE) &&
80510696SDavid.Hollister@Sun.COM (len <= PMCS_SPCBOOT_MAX_SIZE));
80610696SDavid.Hollister@Sun.COM dlen = LE_32(len);
80710696SDavid.Hollister@Sun.COM break;
80810696SDavid.Hollister@Sun.COM default:
80911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
81010696SDavid.Hollister@Sun.COM "%s: Invalid nvmd type: %d", __func__, nvmd_type);
81110696SDavid.Hollister@Sun.COM return (B_FALSE);
81210696SDavid.Hollister@Sun.COM }
81310696SDavid.Hollister@Sun.COM
81411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_DEVEL, NULL, NULL,
81511048SDavid.Hollister@Sun.COM "%s: Request for nvmd type: %d", __func__, nvmd_type);
81610696SDavid.Hollister@Sun.COM
81710696SDavid.Hollister@Sun.COM workp = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
81810696SDavid.Hollister@Sun.COM if (workp == NULL) {
81911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
82010696SDavid.Hollister@Sun.COM "%s: Unable to get work struct", __func__);
82110696SDavid.Hollister@Sun.COM return (B_FALSE);
82210696SDavid.Hollister@Sun.COM }
82310696SDavid.Hollister@Sun.COM
82410696SDavid.Hollister@Sun.COM ptr = &iomb.header;
82510696SDavid.Hollister@Sun.COM bzero(ptr, sizeof (pmcs_set_nvmd_cmd_t));
82610696SDavid.Hollister@Sun.COM *ptr = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_GENERAL, PMCIN_SET_NVMD_DATA));
82710696SDavid.Hollister@Sun.COM workp->arg = (void *)&iomb;
82810696SDavid.Hollister@Sun.COM iomb.htag = LE_32(workp->htag);
82910696SDavid.Hollister@Sun.COM iomb.ip = ip;
83010696SDavid.Hollister@Sun.COM iomb.tdas_nvmd = tdas_nvmd;
83110696SDavid.Hollister@Sun.COM iomb.signature = LE_32(PMCS_SEEPROM_SIGNATURE);
83210696SDavid.Hollister@Sun.COM iomb.ipbal = LE_32(DWORD0(pwp->flash_chunk_addr));
83310696SDavid.Hollister@Sun.COM iomb.ipbah = LE_32(DWORD1(pwp->flash_chunk_addr));
83410696SDavid.Hollister@Sun.COM iomb.ipdl = dlen;
83510696SDavid.Hollister@Sun.COM
83610696SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG_DEVEL,
83710696SDavid.Hollister@Sun.COM "PMCIN_SET_NVMD_DATA iomb", (void *)&iomb);
83810696SDavid.Hollister@Sun.COM
83910696SDavid.Hollister@Sun.COM bcopy(buf, pwp->flash_chunkp, len);
84010696SDavid.Hollister@Sun.COM if (ddi_dma_sync(pwp->cip_handles, 0, 0,
84110696SDavid.Hollister@Sun.COM DDI_DMA_SYNC_FORDEV) != DDI_SUCCESS) {
84211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
84311048SDavid.Hollister@Sun.COM "Condition check failed at %s():%d", __func__, __LINE__);
84410696SDavid.Hollister@Sun.COM }
84510696SDavid.Hollister@Sun.COM
84610696SDavid.Hollister@Sun.COM /*
84710696SDavid.Hollister@Sun.COM * ptr will now point to the inbound queue message
84810696SDavid.Hollister@Sun.COM */
84910696SDavid.Hollister@Sun.COM GET_IO_IQ_ENTRY(pwp, ptr, 0, ibq);
85010696SDavid.Hollister@Sun.COM if (ptr == NULL) {
85111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
85211048SDavid.Hollister@Sun.COM "!%s: Unable to get IQ entry", __func__);
85310696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, workp);
85410696SDavid.Hollister@Sun.COM return (B_FALSE);
85510696SDavid.Hollister@Sun.COM }
85610696SDavid.Hollister@Sun.COM
85710696SDavid.Hollister@Sun.COM bzero(ptr, PMCS_MSG_SIZE << 2); /* PMCS_MSG_SIZE is in dwords */
85810696SDavid.Hollister@Sun.COM iombp = (uint32_t *)&iomb;
85910696SDavid.Hollister@Sun.COM COPY_MESSAGE(ptr, iombp, sizeof (pmcs_set_nvmd_cmd_t) >> 2);
86010696SDavid.Hollister@Sun.COM workp->state = PMCS_WORK_STATE_ONCHIP;
86110696SDavid.Hollister@Sun.COM INC_IQ_ENTRY(pwp, ibq);
86210696SDavid.Hollister@Sun.COM
86310696SDavid.Hollister@Sun.COM WAIT_FOR(workp, 2000, result);
86410696SDavid.Hollister@Sun.COM
86510696SDavid.Hollister@Sun.COM if (result) {
86610696SDavid.Hollister@Sun.COM pmcs_timed_out(pwp, workp->htag, __func__);
86710696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, workp);
86810696SDavid.Hollister@Sun.COM return (B_FALSE);
86910696SDavid.Hollister@Sun.COM }
87010696SDavid.Hollister@Sun.COM
87110696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, workp);
87210696SDavid.Hollister@Sun.COM
87310696SDavid.Hollister@Sun.COM status = LE_32(*(ptr + 3)) & 0xffff;
87410696SDavid.Hollister@Sun.COM if (status != PMCS_NVMD_STAT_SUCCESS) {
87511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
87611048SDavid.Hollister@Sun.COM "%s: Error, status = 0x%04x", __func__, status);
87710696SDavid.Hollister@Sun.COM return (B_FALSE);
87810696SDavid.Hollister@Sun.COM }
87910696SDavid.Hollister@Sun.COM
88010696SDavid.Hollister@Sun.COM return (B_TRUE);
88110696SDavid.Hollister@Sun.COM }
882