4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * CDDL HEADER START
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * The contents of this file are subject to the terms of the
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * Common Development and Distribution License (the "License").
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * You may not use this file except in compliance with the License.
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * See the License for the specific language governing permissions
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * and limitations under the License.
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * When distributing Covered Code, include this CDDL HEADER in each
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * If applicable, add the following below this CDDL HEADER, with the
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * fields enclosed by brackets "[]" replaced with your own identifying
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * information: Portions Copyright [yyyy] [name of copyright owner]
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * CDDL HEADER END
658280b6253b61dbb155f43d0e3cbcffa85ccb90David Hollister * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * This file contains various support routines.
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * SAS Topology Configuration
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * Check current firmware version for correctness
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * and try to flash the correct firmware if what is
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * running isn't correct.
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * Must be called after setup and MPI setup and
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * interrupts are enabled.
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * If updating is disabled, we're done.
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh "Firmware update disabled by conf file");
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh return (0);
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * If we're already running the right firmware, we're done.
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh return (0);
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh "Firmware version matches, but still forcing update");
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh modhp = ddi_modopen(PMCS_FIRMWARE_FILENAME, KRTLD_MODE_FIRST, &errno);
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh "%s: Firmware module not available; will not upgrade",
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh fwvp = ddi_modsym(modhp, PMCS_FIRMWARE_VERSION_NAME, &errno);
c3bc407cfbd238a18e4728ad5f36f39cecdb062fdh "%s: unable to find symbol '%s'",
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * If the firmware version from the module isn't what we expect,
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * and force updating is disabled, return the default (for this
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * mode of operation) value.
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh "%s: firmware module version wrong (0x%x)",
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh "%s: firmware module version wrong (0x%x) - update forced",
2ac4abe882db38ef90020f7c5ca28586e3d57258David Hollister * Get the ILA and firmware versions from the modules themselves
2ac4abe882db38ef90020f7c5ca28586e3d57258David Hollister (void) ddi_strtol((const char *)ila_verp, &bufp, 16, &ila_version);
2ac4abe882db38ef90020f7c5ca28586e3d57258David Hollister (void) ddi_strtol((const char *)fw_verp, &bufp, 16, &fw_version);
2ac4abe882db38ef90020f7c5ca28586e3d57258David Hollister * If force update is not set, verify that what we're loading is
2ac4abe882db38ef90020f7c5ca28586e3d57258David Hollister * what we expect.
2ac4abe882db38ef90020f7c5ca28586e3d57258David Hollister "Expected fw version 0x%x, not 0x%lx: not "
2ac4abe882db38ef90020f7c5ca28586e3d57258David Hollister "updating", PMCS_FIRMWARE_VERSION, fw_version);
2ac4abe882db38ef90020f7c5ca28586e3d57258David Hollister "Upgrading firmware on card from 0x%x to 0x%lx (ILA version 0x%lx)",
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * The SPCBoot image must be updated first, and this is written to
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * SEEPROM, not flash.
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh "%s: unable to flash '%s' segment",
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh return (-1);
83778f4415cf351d3daec8ab6c5c567a0da4b414Jesse Butler "%s: Beginning firmware update of %s image.",
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh "%s: unable to flash '%s' segment",
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh return (-1);
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh "%s: unable to flash '%s' segment",
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh return (-1);
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh return (-1);
83778f4415cf351d3daec8ab6c5c567a0da4b414Jesse Butler "%s: %s image successfully upgraded.",
5c45adf04db8ffdcb5dd969bb5203ff9b17677dbJesse Butler pwp->last_reset_reason = PMCS_LAST_RST_FW_UPGRADE;
83778f4415cf351d3daec8ab6c5c567a0da4b414Jesse Butler "%s: Firmware successfully upgraded", __func__);
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh return (0);
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * Flash firmware support
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * Called unlocked.
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dhpmcs_fw_flash(pmcs_hw_t *pwp, pmcs_fw_hdr_t *hdr, uint32_t length)
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * Step 1- Validate firmware chunks within passed pointer.
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh for (;;) {
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh "%s: bad firmware length 0x%x",
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh wrk += (sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length));
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * Step 2- acquire scratch
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * Step 3- loop through firmware chunks and send each one
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * down to be flashed.
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh for (;;) {
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh wrk += (sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length));
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh return (0);
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh uint32_t len, seg, off, result, amt, msg[PMCS_MSG_SIZE], *ptr;
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh len = sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length);
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh "%s: segment %d offset %u length %u",
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh if (off == 0) {
1f81b46471e38fdeb9ab74c25510b2f903f8af12David Hollister WAIT_FOR(pwrk, PMCS_FLASH_WAIT_TIME, result);
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh "%s: segment %d complete pending reboot",
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh "%s: segment %d download not supported error",
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh "%s: segment %d update disabled error",
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh "%s: segment %d unknown error %x",
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh return (0);
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * pmcs_validate_vpd
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * Input: softstate pointer and pointer to vpd data buffer
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * Returns: B_TRUE if VPD data looks OK, B_FALSE otherwise
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * Make sure we understand the format of this data
688a63ed70eca66e1dc82c8f096cb66f8bd94013Srikanth Suravajhala * Only VPD version 1 is VALID for Thebe-INT cards and
688a63ed70eca66e1dc82c8f096cb66f8bd94013Srikanth Suravajhala * Only VPD version 2 is valid for Thebe-EXT cards
688a63ed70eca66e1dc82c8f096cb66f8bd94013Srikanth Suravajhala if ((vpd_header->eeprom_version == PMCS_EEPROM_INT_VERSION &&
688a63ed70eca66e1dc82c8f096cb66f8bd94013Srikanth Suravajhala vpd_header->subsys_pid[0] == PMCS_EEPROM_INT_SSID_BYTE1 &&
688a63ed70eca66e1dc82c8f096cb66f8bd94013Srikanth Suravajhala vpd_header->subsys_pid[1] == PMCS_EEPROM_INT_SSID_BYTE2) ||
688a63ed70eca66e1dc82c8f096cb66f8bd94013Srikanth Suravajhala (vpd_header->eeprom_version == PMCS_EEPROM_EXT_VERSION &&
688a63ed70eca66e1dc82c8f096cb66f8bd94013Srikanth Suravajhala vpd_header->subsys_pid[0] == PMCS_EEPROM_EXT_SSID_BYTE1 &&
688a63ed70eca66e1dc82c8f096cb66f8bd94013Srikanth Suravajhala vpd_header->subsys_pid[1] == PMCS_EEPROM_EXT_SSID_BYTE2)) {
688a63ed70eca66e1dc82c8f096cb66f8bd94013Srikanth Suravajhala pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
688a63ed70eca66e1dc82c8f096cb66f8bd94013Srikanth Suravajhala "%s: Detected Thebe card with SSID(%02x%02x)", __func__,
688a63ed70eca66e1dc82c8f096cb66f8bd94013Srikanth Suravajhala vpd_header->subsys_pid[0], vpd_header->subsys_pid[1]);
688a63ed70eca66e1dc82c8f096cb66f8bd94013Srikanth Suravajhala "%s: EEPROM(%d) unsupported; requires %d for INT(%02x%02x) "
688a63ed70eca66e1dc82c8f096cb66f8bd94013Srikanth Suravajhala " and %d for EXT(%02x%02x) cards.", __func__,
688a63ed70eca66e1dc82c8f096cb66f8bd94013Srikanth Suravajhala PMCS_EEPROM_INT_VERSION, PMCS_EEPROM_INT_SSID_BYTE1,
688a63ed70eca66e1dc82c8f096cb66f8bd94013Srikanth Suravajhala PMCS_EEPROM_INT_SSID_BYTE2, PMCS_EEPROM_EXT_VERSION,
688a63ed70eca66e1dc82c8f096cb66f8bd94013Srikanth Suravajhala PMCS_EEPROM_EXT_SSID_BYTE1, PMCS_EEPROM_EXT_SSID_BYTE2);
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * Do we have a valid SAS WWID?
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh if (((vpd_header->hba_sas_wwid[0] & 0xf0) >> 4) != NAA_IEEE_REG) {
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * We only checksum the VPD data between (and including) VPD Start byte
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * and the checksum value byte. The length of this data for CRC is
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * 15 less than the length indicated in vpd_length field of the header.
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * 8 (SAS WWN) + 2 (subsystem ID) + 2 (subsystem vendor ID) +
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * 1 (end tag) + 2 (hex byte CRC, different from this one) = 15 bytes
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * VPD length (little endian format) is represented as byte-array field
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * & read the following way to avoid alignment issues (in SPARC)
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh /* Validate VPD data checksum */
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * Get length of string ID tag and read it.
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * String ID tag length (little endian format) is represented as
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * byte-array & read the following way to avoid alignment issues
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * (in SPARC)
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_MODEL_NAME, tbuf);
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * Skip VPD-R tag and length of read-only tag, then start reading
c3bc407cfbd238a18e4728ad5f36f39cecdb062fdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "%s (Len: 0x%x)",
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh /* Keyword is Manufacturer */
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh if ((vkvp->keyword[0] == 'M') && (vkvp->keyword[1] == 'N')) {
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh /* Keyword is Serial Number */
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh if ((vkvp->keyword[0] == 'S') && (vkvp->keyword[1] == 'N')) {
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * pmcs_get_nvmd
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * This function will read the requested data from the non-volatile
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * storage on the card. This could mean SEEPROM, VPD, or other areas
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * as defined by the PM8001 programmer's manual.
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * nvmd_type: The data type being requested
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * nvmd: NVM device to access (IOP/AAP1)
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * offset: Must be 4K alignment
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * buf: Pointer to memory region for retrieved data
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * size_left: Total available bytes left in buf
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * Returns: non-negative on success, -1 on failure
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh/*ARGSUSED*/
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dhpmcs_get_nvmd(pmcs_hw_t *pwp, pmcs_nvmd_type_t nvmd_type, uint8_t nvmd,
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh return (-1);
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh return (-1);
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh *ptr = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_GENERAL, PMCIN_GET_NVMD_DATA));
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * ptr will now point to the inbound queue message
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh return (-1);
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh bzero(ptr, PMCS_MSG_SIZE << 2); /* PMCS_MSG_SIZE is in dwords */
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh COPY_MESSAGE(ptr, iombp, sizeof (pmcs_get_nvmd_cmd_t) >> 2);
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh return (-1);
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh return (-1);
c3bc407cfbd238a18e4728ad5f36f39cecdb062fdh "Condition check failed at %s():%d", __func__, __LINE__);
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh switch (nvmd) {
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh while ((i < PMCS_FLASH_CHUNK_SIZE) &&
c3bc407cfbd238a18e4728ad5f36f39cecdb062fdh "UNKNOWN NVMD DEVICE");
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh return (-1);
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * pmcs_set_nvmd
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * This function will write the requested data to non-volatile storage
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * on the HBA. This could mean SEEPROM, VPD, or other areas as defined by
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * the PM8001 programmer's manual.
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * nvmd_type: The data type to be written
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * buf: Pointer to memory region for data to write
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * len: Length of the data buffer
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * Returns: B_TRUE on success, B_FALSE on failure
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dhpmcs_set_nvmd(pmcs_hw_t *pwp, pmcs_nvmd_type_t nvmd_type, uint8_t *buf,
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh *ptr = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_GENERAL, PMCIN_SET_NVMD_DATA));
c3bc407cfbd238a18e4728ad5f36f39cecdb062fdh "Condition check failed at %s():%d", __func__, __LINE__);
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh * ptr will now point to the inbound queue message
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh bzero(ptr, PMCS_MSG_SIZE << 2); /* PMCS_MSG_SIZE is in dwords */
4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6dh COPY_MESSAGE(ptr, iombp, sizeof (pmcs_set_nvmd_cmd_t) >> 2);