pmcs_subr.c revision 601c90f161ff0319c1b4a2c3362b466043a65d8d
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * CDDL HEADER START
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * The contents of this file are subject to the terms of the
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Common Development and Distribution License (the "License").
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * You may not use this file except in compliance with the License.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * or http://www.opensolaris.org/os/licensing.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * See the License for the specific language governing permissions
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * and limitations under the License.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * When distributing Covered Code, include this CDDL HEADER in each
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If applicable, add the following below this CDDL HEADER, with the
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * fields enclosed by brackets "[]" replaced with your own identifying
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * information: Portions Copyright [yyyy] [name of copyright owner]
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * CDDL HEADER END
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Use is subject to license terms.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * This file contains various support routines.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster#include <sys/scsi/adapters/pmcs/pmcs.h>
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Local static data
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic int tgtmap_usec = MICROSEC;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * SAS Topology Configuration
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic void pmcs_new_tport(pmcs_hw_t *, pmcs_phy_t *);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic void pmcs_configure_expander(pmcs_hw_t *, pmcs_phy_t *, pmcs_iport_t *);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic boolean_t pmcs_check_expanders(pmcs_hw_t *, pmcs_phy_t *);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic void pmcs_check_expander(pmcs_hw_t *, pmcs_phy_t *);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic void pmcs_clear_expander(pmcs_hw_t *, pmcs_phy_t *, int);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic int pmcs_expander_get_nphy(pmcs_hw_t *, pmcs_phy_t *);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic int pmcs_expander_content_discover(pmcs_hw_t *, pmcs_phy_t *,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_phy_t *);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic int pmcs_smp_function_result(pmcs_hw_t *, smp_response_frame_t *);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic boolean_t pmcs_validate_devid(pmcs_phy_t *, pmcs_phy_t *, uint32_t);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic void pmcs_clear_phys(pmcs_hw_t *, pmcs_phy_t *);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic int pmcs_configure_new_devices(pmcs_hw_t *, pmcs_phy_t *);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic void pmcs_begin_observations(pmcs_hw_t *);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic boolean_t pmcs_report_observations(pmcs_hw_t *);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic boolean_t pmcs_report_iport_observations(pmcs_hw_t *, pmcs_iport_t *,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_phy_t *);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic pmcs_phy_t *pmcs_find_phy_needing_work(pmcs_hw_t *, pmcs_phy_t *);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic int pmcs_kill_devices(pmcs_hw_t *, pmcs_phy_t *);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic void pmcs_lock_phy_impl(pmcs_phy_t *, int);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic void pmcs_unlock_phy_impl(pmcs_phy_t *, int);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic pmcs_phy_t *pmcs_clone_phy(pmcs_phy_t *);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic boolean_t pmcs_configure_phy(pmcs_hw_t *, pmcs_phy_t *);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic void pmcs_reap_dead_phy(pmcs_phy_t *);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic pmcs_iport_t *pmcs_get_iport_by_ua(pmcs_hw_t *, char *);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic boolean_t pmcs_phy_target_match(pmcs_phy_t *);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Often used strings
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterconst char pmcs_nowrk[] = "%s: unable to get work structure";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterconst char pmcs_nomsg[] = "%s: unable to get Inbound Message entry";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterconst char pmcs_timeo[] = "%s: command timed out";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterextern const ddi_dma_attr_t pmcs_dattr;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Some Initial setup steps.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterint
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_setup(pmcs_hw_t *pwp)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t barval = pwp->mpibar;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t i, scratch, regbar, regoff, barbar, baroff;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t new_ioq_depth, ferr = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Check current state. If we're not at READY state,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * we can't go further.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster scratch = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((scratch & PMCS_MSGU_AAP_STATE_MASK) == PMCS_MSGU_AAP_STATE_ERROR) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: AAP Error State (0x%x)",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PMCS_MSGU_AAP_ERROR_MASK);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_fm_ereport(pwp, DDI_FM_DEVICE_INVAL_STATE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((scratch & PMCS_MSGU_AAP_STATE_MASK) != PMCS_MSGU_AAP_STATE_READY) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: AAP unit not ready (state 0x%x)",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, scratch & PMCS_MSGU_AAP_STATE_MASK);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_fm_ereport(pwp, DDI_FM_DEVICE_INVAL_STATE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Read the offset from the Message Unit scratchpad 0 register.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * This allows us to read the MPI Configuration table.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Check its signature for validity.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster baroff = barval;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster barbar = barval >> PMCS_MSGU_MPI_BAR_SHIFT;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster baroff &= PMCS_MSGU_MPI_OFFSET_MASK;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster regoff = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster regbar = regoff >> PMCS_MSGU_MPI_BAR_SHIFT;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster regoff &= PMCS_MSGU_MPI_OFFSET_MASK;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (regoff > baroff) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: bad MPI Table Length (register offset=0x%08x, "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "passed offset=0x%08x)", __func__, regoff, baroff);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (regbar != barbar) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: bad MPI BAR (register BAROFF=0x%08x, "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "passed BAROFF=0x%08x)", __func__, regbar, barbar);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->mpi_offset = regoff;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_AS) != PMCS_SIGNATURE) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: Bad MPI Configuration Table Signature 0x%x", __func__,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_rd_mpi_tbl(pwp, PMCS_MPI_AS));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IR) != PMCS_MPI_REVISION1) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: Bad MPI Configuration Revision 0x%x", __func__,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IR));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Generate offsets for the General System, Inbound Queue Configuration
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * and Outbound Queue configuration tables. This way the macros to
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * access those tables will work correctly.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->mpi_gst_offset =
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_GSTO);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->mpi_iqc_offset =
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IQCTO);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->mpi_oqc_offset =
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_OQCTO);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->fw = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_FW);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->max_cmd = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_MOIO);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->max_dev = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO0) >> 16;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->max_iq = PMCS_MNIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->max_oq = PMCS_MNOQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->nphy = PMCS_NPHY(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->max_iq <= PMCS_NIQ) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: not enough Inbound Queues supported "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "(need %d, max_oq=%d)", __func__, pwp->max_iq, PMCS_NIQ);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->max_oq <= PMCS_NOQ) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: not enough Outbound Queues supported "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "(need %d, max_oq=%d)", __func__, pwp->max_oq, PMCS_NOQ);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->nphy == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: zero phys reported", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (PMCS_HPIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1))) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->hipri_queue = (1 << PMCS_IQ_OTHER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < pwp->nphy; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PMCS_MPI_EVQSET(pwp, PMCS_OQ_EVENTS, i);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PMCS_MPI_NCQSET(pwp, PMCS_OQ_EVENTS, i);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_mpi_tbl(pwp, PMCS_MPI_INFO2,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (PMCS_OQ_EVENTS << GENERAL_EVENT_OQ_SHIFT) |
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (PMCS_OQ_EVENTS << DEVICE_HANDLE_REMOVED_SHIFT));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Verify that ioq_depth is valid (> 0 and not so high that it
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * would cause us to overrun the chip with commands).
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->ioq_depth == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: I/O queue depth set to 0. Setting to %d",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, PMCS_NQENTRY);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->ioq_depth = PMCS_NQENTRY;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->ioq_depth < PMCS_MIN_NQENTRY) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: I/O queue depth set too low (%d). Setting to %d",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, pwp->ioq_depth, PMCS_MIN_NQENTRY);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->ioq_depth = PMCS_MIN_NQENTRY;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->ioq_depth > (pwp->max_cmd / (PMCS_IO_IQ_MASK + 1))) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster new_ioq_depth = pwp->max_cmd / (PMCS_IO_IQ_MASK + 1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: I/O queue depth set too high (%d). Setting to %d",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, pwp->ioq_depth, new_ioq_depth);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->ioq_depth = new_ioq_depth;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Allocate consistent memory for OQs and IQs.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->iqp_dma_attr = pwp->oqp_dma_attr = pmcs_dattr;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->iqp_dma_attr.dma_attr_align =
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->oqp_dma_attr.dma_attr_align = PMCS_QENTRY_SIZE;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * The Rev C chip has the ability to do PIO to or from consistent
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * memory anywhere in a 64 bit address space, but the firmware is
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * not presently set up to do so.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->iqp_dma_attr.dma_attr_addr_hi =
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->oqp_dma_attr.dma_attr_addr_hi = 0x000000FFFFFFFFFFull;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < PMCS_NIQ; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_dma_setup(pwp, &pwp->iqp_dma_attr,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster &pwp->iqp_acchdls[i],
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster &pwp->iqp_handles[i], PMCS_QENTRY_SIZE * pwp->ioq_depth,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (caddr_t *)&pwp->iqp[i], &pwp->iqaddr[i]) == B_FALSE) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "Failed to setup DMA for iqp[%d]", i);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster bzero(pwp->iqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < PMCS_NOQ; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_dma_setup(pwp, &pwp->oqp_dma_attr,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster &pwp->oqp_acchdls[i],
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster &pwp->oqp_handles[i], PMCS_QENTRY_SIZE * pwp->ioq_depth,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (caddr_t *)&pwp->oqp[i], &pwp->oqaddr[i]) == B_FALSE) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "Failed to setup DMA for oqp[%d]", i);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster bzero(pwp->oqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Install the IQ and OQ addresses (and null out the rest).
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < pwp->max_iq; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->iqpi_offset[i] = pmcs_rd_iqc_tbl(pwp, PMCS_IQPIOFFX(i));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (i < PMCS_NIQ) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (i != PMCS_IQ_OTHER) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i),
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->ioq_depth | (PMCS_QENTRY_SIZE << 16));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i),
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (1 << 30) | pwp->ioq_depth |
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (PMCS_QENTRY_SIZE << 16));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i),
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster DWORD1(pwp->iqaddr[i]));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i),
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster DWORD0(pwp->iqaddr[i]));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i),
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster DWORD1(pwp->ciaddr+IQ_OFFSET(i)));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i),
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster DWORD0(pwp->ciaddr+IQ_OFFSET(i)));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < pwp->max_oq; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->oqci_offset[i] = pmcs_rd_oqc_tbl(pwp, PMCS_OQCIOFFX(i));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (i < PMCS_NOQ) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), pwp->ioq_depth |
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (PMCS_QENTRY_SIZE << 16) | OQIEX);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i),
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster DWORD1(pwp->oqaddr[i]));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i),
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster DWORD0(pwp->oqaddr[i]));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i),
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster DWORD1(pwp->ciaddr+OQ_OFFSET(i)));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i),
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster DWORD0(pwp->ciaddr+OQ_OFFSET(i)));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i),
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->oqvec[i] << 24);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Set up logging, if defined.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->fwlog) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint64_t logdma = pwp->fwaddr;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBAH, DWORD1(logdma));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBAL, DWORD0(logdma));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBS, PMCS_FWLOG_SIZE >> 1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELSEV, pwp->fwlog);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster logdma += (PMCS_FWLOG_SIZE >> 1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBAH, DWORD1(logdma));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBAL, DWORD0(logdma));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBS, PMCS_FWLOG_SIZE >> 1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELSEV, pwp->fwlog);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Interrupt vectors, outbound queues, and odb_auto_clear
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * MSI/MSI-X:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If we got 4 interrupt vectors, we'll assign one to each outbound
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * queue as well as the fatal interrupt, and auto clear can be set
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * for each.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If we only got 2 vectors, one will be used for I/O completions
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * and the other for the other two vectors. In this case, auto_
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * clear can only be set for I/Os, which is fine. The fatal
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * interrupt will be mapped to the PMCS_FATAL_INTERRUPT bit, which
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * is not an interrupt vector.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * MSI/MSI-X/INT-X:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If we only got 1 interrupt vector, auto_clear must be set to 0,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * and again the fatal interrupt will be mapped to the
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * PMCS_FATAL_INTERRUPT bit (again, not an interrupt vector).
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster switch (pwp->int_type) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster case PMCS_INT_MSIX:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster case PMCS_INT_MSI:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster switch (pwp->intr_cnt) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster case 1:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->odb_auto_clear = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster case 2:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->odb_auto_clear = (1 << PMCS_FATAL_INTERRUPT) |
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (1 << PMCS_MSIX_IODONE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster case 4:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (PMCS_MSIX_FATAL << PMCS_FERIV_SHIFT));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->odb_auto_clear = (1 << PMCS_MSIX_FATAL) |
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (1 << PMCS_MSIX_GENERAL) | (1 << PMCS_MSIX_IODONE) |
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (1 << PMCS_MSIX_EVENTS);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster case PMCS_INT_FIXED:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PMCS_FERRIE | (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->odb_auto_clear = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Enable Interrupt Reassertion
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Default Delay 1000us
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ferr = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_FERR);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((ferr & PMCS_MPI_IRAE) == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ferr &= ~(PMCS_MPI_IRAU | PMCS_MPI_IRAD_MASK);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, ferr | PMCS_MPI_IRAE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR, pwp->odb_auto_clear);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->mpi_table_setup = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Start the Message Passing protocol with the PMC chip.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterint
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_start_mpi(pmcs_hw_t *pwp)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int i;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_msgunit(pwp, PMCS_MSGU_IBDB, PMCS_MSGU_IBDB_MPIINI);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < 1000; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) &
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PMCS_MSGU_IBDB_MPIINI) == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(1000);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & PMCS_MSGU_IBDB_MPIINI) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(500000);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Check to make sure we got to INIT state.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (PMCS_MPI_S(pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE)) !=
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PMCS_MPI_STATE_INIT) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: MPI launch failed (GST 0x%x DBCLR 0x%x)", __func__,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE),
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB_CLEAR));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Stop the Message Passing protocol with the PMC chip.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterint
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_stop_mpi(pmcs_hw_t *pwp)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int i;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < pwp->max_iq; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < pwp->max_oq; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_msgunit(pwp, PMCS_MSGU_IBDB, PMCS_MSGU_IBDB_MPICTU);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < 2000; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) &
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PMCS_MSGU_IBDB_MPICTU) == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(1000);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & PMCS_MSGU_IBDB_MPICTU) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: MPI stop failed", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Do a sequence of ECHO messages to test for MPI functionality,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * all inbound and outbound queue functionality and interrupts.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterint
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_echo_test(pmcs_hw_t *pwp)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster echo_test_t fred;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster struct pmcwork *pwrk;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t *msg, count;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int iqe = 0, iqo = 0, result, rval = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int iterations;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster hrtime_t echo_start, echo_end, echo_total;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(pwp->max_cmd > 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * We want iterations to be max_cmd * 3 to ensure that we run the
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * echo test enough times to iterate through every inbound queue
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * at least twice.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iterations = pwp->max_cmd * 3;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster echo_total = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster count = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster while (count < iterations) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwrk == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_nowrk, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rval = -1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->iqp_lock[iqe]);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg = GET_IQ_ENTRY(pwp, iqe);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (msg == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->iqp_lock[iqe]);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_pwork(pwp, pwrk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_nomsg, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rval = -1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster bzero(msg, PMCS_QENTRY_SIZE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (iqe == PMCS_IQ_OTHER) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* This is on the high priority queue */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[0] = LE_32(PMCS_HIPRI(pwp, iqo, PMCIN_ECHO));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[0] = LE_32(PMCS_IOMB_IN_SAS(iqo, PMCIN_ECHO));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[1] = LE_32(pwrk->htag);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster fred.signature = 0xdeadbeef;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster fred.count = count;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster fred.ptr = &count;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (void) memcpy(&msg[2], &fred, sizeof (fred));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk->state = PMCS_WORK_STATE_ONCHIP;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster INC_IQ_ENTRY(pwp, iqe);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster echo_start = gethrtime();
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster DTRACE_PROBE2(pmcs__echo__test__wait__start,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster hrtime_t, echo_start, uint32_t, pwrk->htag);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (++iqe == PMCS_NIQ) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iqe = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (++iqo == PMCS_NOQ) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iqo = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster WAIT_FOR(pwrk, 250, result);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster echo_end = gethrtime();
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster DTRACE_PROBE2(pmcs__echo__test__wait__end,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster hrtime_t, echo_end, int, result);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster echo_total += (echo_end - echo_start);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_pwork(pwp, pwrk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (result) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: command timed out on echo test #%d",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, count);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rval = -1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * The intr_threshold is adjusted by PMCS_INTR_THRESHOLD in order to
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * remove the overhead of things like the delay in getting signaled
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * for completion.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (echo_total != 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->io_intr_coal.intr_latency =
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (echo_total / iterations) / 2;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->io_intr_coal.intr_threshold =
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PMCS_INTR_THRESHOLD(PMCS_QUANTUM_TIME_USECS * 1000 /
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->io_intr_coal.intr_latency);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (rval);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Start the (real) phys
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterint
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_start_phy(pmcs_hw_t *pwp, int phynum, int linkmode, int speed)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int result;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t *msg;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster struct pmcwork *pwrk;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_phy_t *pptr;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster sas_identify_af_t sap;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr = pwp->root_phys + phynum;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: cannot find port %d", __func__, phynum);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_lock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwrk == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (msg == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_pwork(pwp, pwrk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_PHY_START));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[1] = LE_32(pwrk->htag);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[2] = LE_32(linkmode | speed | phynum);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster bzero(&sap, sizeof (sap));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster sap.device_type = SAS_IF_DTYPE_ENDPOINT;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster sap.ssp_ini_port = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->separate_ports) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wwn2barray(pwp->sas_wwns[phynum], sap.sas_address);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wwn2barray(pwp->sas_wwns[0], sap.sas_address);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(phynum < SAS2_PHYNUM_MAX);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster sap.phy_identifier = phynum & SAS2_PHYNUM_MASK;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (void) memcpy(&msg[3], &sap, sizeof (sas_identify_af_t));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk->state = PMCS_WORK_STATE_ONCHIP;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->state.prog_min_rate = (lowbit((ulong_t)speed) - 1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->state.prog_max_rate = (highbit((ulong_t)speed) - 1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->state.hw_min_rate = PMCS_HW_MIN_LINK_RATE;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->state.hw_max_rate = PMCS_HW_MAX_LINK_RATE;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster WAIT_FOR(pwrk, 1000, result);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_pwork(pwp, pwrk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (result) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->phys_started |= (1 << phynum);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterint
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_start_phys(pmcs_hw_t *pwp)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int i;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < pwp->nphy; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((pwp->phyid_block_mask & (1 << i)) == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_start_phy(pwp, i,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (pwp->phymode << PHY_MODE_SHIFT),
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->physpeed << PHY_LINK_SHIFT)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_clear_diag_counters(pwp, i)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: failed to reset counters on PHY (%d)",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, i);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Called with PHY locked
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterint
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_reset_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint8_t type)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t *msg;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2];
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster const char *mbar;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t amt;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t pdevid;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t stsoff;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t status;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int result, level, phynum;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster struct pmcwork *pwrk;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t htag;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(mutex_owned(&pptr->phy_lock));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster bzero(iomb, PMCS_QENTRY_SIZE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster phynum = pptr->phynum;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster level = pptr->level;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (level > 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pdevid = pptr->parent->device_id;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else if ((level == 0) && (pptr->dtype == EXPANDER)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, pptr->target,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: Not resetting HBA PHY @ %s", __func__, pptr->path);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwrk == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (ENOMEM);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk->arg = iomb;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If level > 0, we need to issue an SMP_REQUEST with a PHY_CONTROL
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * function to do either a link reset or hard reset. If level == 0,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * then we do a LOCAL_PHY_CONTROL IOMB to do link/hard reset to the
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * root (local) PHY
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (level) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster stsoff = 2;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iomb[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PMCIN_SMP_REQUEST));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iomb[1] = LE_32(pwrk->htag);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iomb[2] = LE_32(pdevid);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iomb[3] = LE_32(40 << SMP_REQUEST_LENGTH_SHIFT);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Send SMP PHY CONTROL/HARD or LINK RESET
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iomb[4] = BE_32(0x40910000);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iomb[5] = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (type == PMCS_PHYOP_HARD_RESET) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mbar = "SMP PHY CONTROL/HARD RESET";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iomb[6] = BE_32((phynum << 24) |
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (PMCS_PHYOP_HARD_RESET << 16));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mbar = "SMP PHY CONTROL/LINK RESET";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iomb[6] = BE_32((phynum << 24) |
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (PMCS_PHYOP_LINK_RESET << 16));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: sending %s to %s for phy 0x%x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, mbar, pptr->parent->path, pptr->phynum);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster amt = 7;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Unlike most other Outbound messages, status for
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * a local phy operation is in DWORD 3.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster stsoff = 3;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iomb[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PMCIN_LOCAL_PHY_CONTROL));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iomb[1] = LE_32(pwrk->htag);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (type == PMCS_PHYOP_LINK_RESET) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mbar = "LOCAL PHY LINK RESET";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iomb[2] = LE_32((PMCS_PHYOP_LINK_RESET << 8) | phynum);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mbar = "LOCAL PHY HARD RESET";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iomb[2] = LE_32((PMCS_PHYOP_HARD_RESET << 8) | phynum);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: sending %s to %s", __func__, mbar, pptr->path);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster amt = 3;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (msg == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_pwork(pwp, pwrk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (ENOMEM);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster COPY_MESSAGE(msg, iomb, amt);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster htag = pwrk->htag;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* SMP serialization */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_smp_acquire(pptr->iport);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk->state = PMCS_WORK_STATE_ONCHIP;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster WAIT_FOR(pwrk, 1000, result);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_pwork(pwp, pwrk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Release SMP lock before reacquiring PHY lock */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_smp_release(pptr->iport);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_lock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (result) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_abort(pwp, pptr, htag, 0, 0)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: Unable to issue SMP abort for htag 0x%08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, htag);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: Issuing SMP ABORT for htag 0x%08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, htag);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (EIO);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster status = LE_32(iomb[stsoff]);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (status != PMCOUT_STATUS_OK) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster char buf[32];
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster const char *es = pmcs_status_str(status);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (es == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (void) snprintf(buf, sizeof (buf), "Status 0x%x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster status);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster es = buf;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: %s action returned %s for %s", __func__, mbar, es,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->path);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (status);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Stop the (real) phys. No PHY or softstate locks are required as this only
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * happens during detach.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fostervoid
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_stop_phy(pmcs_hw_t *pwp, int phynum)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int result;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_phy_t *pptr;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t *msg;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster struct pmcwork *pwrk;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr = pwp->root_phys + phynum;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: unable to find port %d", __func__, phynum);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->phys_started & (1 << phynum)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwrk == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_nowrk, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (msg == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_pwork(pwp, pwrk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_nomsg, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_PHY_STOP));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[1] = LE_32(pwrk->htag);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[2] = LE_32(phynum);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk->state = PMCS_WORK_STATE_ONCHIP;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Make this unconfigured now.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster WAIT_FOR(pwrk, 1000, result);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_pwork(pwp, pwrk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (result) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr, NULL, pmcs_timeo, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->phys_started &= ~(1 << phynum);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->configured = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * No locks should be required as this is only called during detach
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fostervoid
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_stop_phys(pmcs_hw_t *pwp)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int i;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < pwp->nphy; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((pwp->phyid_block_mask & (1 << i)) == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_stop_phy(pwp, i);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Run SAS_DIAG_EXECUTE with cmd and cmd_desc passed.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * ERR_CNT_RESET: return status of cmd
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * DIAG_REPORT_GET: return value of the counter
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterint
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_sas_diag_execute(pmcs_hw_t *pwp, uint32_t cmd, uint32_t cmd_desc,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint8_t phynum)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t htag, *ptr, status, msg[PMCS_MSG_SIZE << 1];
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int result;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster struct pmcwork *pwrk;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwrk == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nowrk, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (DDI_FAILURE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk->arg = msg;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster htag = pwrk->htag;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_SAS_DIAG_EXECUTE));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[1] = LE_32(htag);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[2] = LE_32((cmd << PMCS_DIAG_CMD_SHIFT) |
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (cmd_desc << PMCS_DIAG_CMD_DESC_SHIFT) | phynum);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (ptr == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_pwork(pwp, pwrk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nomsg, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (DDI_FAILURE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster COPY_MESSAGE(ptr, msg, 3);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk->state = PMCS_WORK_STATE_ONCHIP;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster WAIT_FOR(pwrk, 1000, result);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_pwork(pwp, pwrk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (result) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_timed_out(pwp, htag, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (DDI_FAILURE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster status = LE_32(msg[3]);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Return for counter reset */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (cmd == PMCS_ERR_CNT_RESET)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (status);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Return for counter value */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (status) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: failed, status (0x%x)", __func__, status);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (DDI_FAILURE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (LE_32(msg[4]));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/* Get the current value of the counter for desc on phynum and return it. */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterint
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_get_diag_report(pmcs_hw_t *pwp, uint32_t desc, uint8_t phynum)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (pmcs_sas_diag_execute(pwp, PMCS_DIAG_REPORT_GET, desc, phynum));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/* Clear all of the counters for phynum. Returns the status of the command. */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterint
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_clear_diag_counters(pmcs_hw_t *pwp, uint8_t phynum)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t cmd = PMCS_ERR_CNT_RESET;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t cmd_desc;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster cmd_desc = PMCS_INVALID_DWORD_CNT;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (DDI_FAILURE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster cmd_desc = PMCS_DISPARITY_ERR_CNT;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (DDI_FAILURE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster cmd_desc = PMCS_LOST_DWORD_SYNC_CNT;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (DDI_FAILURE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster cmd_desc = PMCS_RESET_FAILED_CNT;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (DDI_FAILURE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (DDI_SUCCESS);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Get firmware timestamp
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterint
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_get_time_stamp(pmcs_hw_t *pwp, uint64_t *ts)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t htag, *ptr, msg[PMCS_MSG_SIZE << 1];
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int result;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster struct pmcwork *pwrk;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwrk == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nowrk, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk->arg = msg;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster htag = pwrk->htag;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_GET_TIME_STAMP));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[1] = LE_32(pwrk->htag);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (ptr == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_pwork(pwp, pwrk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nomsg, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster COPY_MESSAGE(ptr, msg, 2);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk->state = PMCS_WORK_STATE_ONCHIP;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster WAIT_FOR(pwrk, 1000, result);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_pwork(pwp, pwrk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (result) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_timed_out(pwp, htag, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *ts = LE_32(msg[2]) | (((uint64_t)LE_32(msg[3])) << 32);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Dump all pertinent registers
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fostervoid
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_register_dump(pmcs_hw_t *pwp)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int i;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t val;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "pmcs%d: Register dump start",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ddi_get_instance(pwp->dip));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "OBDB (intr): 0x%08x (mask): 0x%08x (clear): 0x%08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB),
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB_MASK),
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH0: 0x%08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH0));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH1: 0x%08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH2: 0x%08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH3: 0x%08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH3));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < PMCS_NIQ; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "IQ %d: CI %u PI %u",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster i, pmcs_rd_iqci(pwp, i), pmcs_rd_iqpi(pwp, i));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < PMCS_NOQ; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "OQ %d: CI %u PI %u",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster i, pmcs_rd_oqci(pwp, i), pmcs_rd_oqpi(pwp, i));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster val = pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "GST TABLE BASE: 0x%08x (STATE=0x%x QF=%d GSTLEN=%d HMI_ERR=0x%x)",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster val, PMCS_MPI_S(val), PMCS_QF(val), PMCS_GSTLEN(val) * 4,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PMCS_HMI_ERR(val));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE IQFRZ0: 0x%08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_rd_gst_tbl(pwp, PMCS_GST_IQFRZ0));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE IQFRZ1: 0x%08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_rd_gst_tbl(pwp, PMCS_GST_IQFRZ1));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE MSGU TICK: 0x%08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_rd_gst_tbl(pwp, PMCS_GST_MSGU_TICK));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE IOP TICK: 0x%08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_rd_gst_tbl(pwp, PMCS_GST_IOP_TICK));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < pwp->nphy; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t rerrf, pinfo, started = 0, link = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pinfo = pmcs_rd_gst_tbl(pwp, PMCS_GST_PHY_INFO(i));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pinfo & 1) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster started = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster link = pinfo & 2;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rerrf = pmcs_rd_gst_tbl(pwp, PMCS_GST_RERR_INFO(i));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "GST TABLE PHY%d STARTED=%d LINK=%d RERR=0x%08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster i, started, link, rerrf);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "pmcs%d: Register dump end",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ddi_get_instance(pwp->dip));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Handle SATA Abort and other error processing
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterint
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_abort_handler(pmcs_hw_t *pwp)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_phy_t *pptr, *pnext, *pnext_uplevel[PMCS_MAX_XPND];
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_xscsi_t *tgt;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int r, level = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr = pwp->root_phys;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster while (pptr) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * XXX: Need to make sure this doesn't happen
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * XXX: when non-NCQ commands are running.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_lock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr->need_rl_ext) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(pptr->dtype == SATA);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_acquire_scratch(pwp, B_FALSE)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster goto next_phy;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster r = pmcs_sata_abort_ncq(pwp, pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_release_scratch(pwp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (r == ENOMEM) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster goto next_phy;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (r) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster r = pmcs_reset_phy(pwp, pptr,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PMCS_PHYOP_LINK_RESET);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (r == ENOMEM) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster goto next_phy;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* what if other failures happened? */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->abort_pending = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->abort_sent = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr->abort_pending == 0 || pptr->abort_sent) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster goto next_phy;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->abort_pending = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_abort(pwp, pptr, pptr->device_id, 1, 1) == ENOMEM) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->abort_pending = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster goto next_phy;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->abort_sent = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If the iport is no longer active, flush the queues
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((pptr->iport == NULL) ||
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (pptr->iport->ua_state != UA_ACTIVE)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster tgt = pptr->target;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (tgt) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, tgt,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: Clearing target 0x%p, inactive iport",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, (void *) tgt);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&tgt->statlock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_clear_xp(pwp, tgt);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&tgt->statlock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosternext_phy:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr->children) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pnext = pptr->children;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pnext_uplevel[level++] = pptr->sibling;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pnext = pptr->sibling;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster while ((pnext == NULL) && (level > 0)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pnext = pnext_uplevel[--level];
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr = pnext;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Register a device (get a device handle for it).
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Called with PHY lock held.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterint
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_register_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster struct pmcwork *pwrk;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int result = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t *msg;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t tmp, status;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2];
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (msg == NULL ||
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr)) == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster result = ENOMEM;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster goto out;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk->arg = iomb;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk->dtype = pptr->dtype;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[1] = LE_32(pwrk->htag);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_REGISTER_DEVICE));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster tmp = PMCS_DEVREG_TLR |
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (pptr->link_rate << PMCS_DEVREG_LINK_RATE_SHIFT);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (IS_ROOT_PHY(pptr)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[2] = LE_32(pptr->portid |
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (pptr->phynum << PMCS_PHYID_SHIFT));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[2] = LE_32(pptr->portid);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr->dtype == SATA) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (IS_ROOT_PHY(pptr)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster tmp |= PMCS_DEVREG_TYPE_SATA_DIRECT;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster tmp |= PMCS_DEVREG_TYPE_SATA;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster tmp |= PMCS_DEVREG_TYPE_SAS;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[3] = LE_32(tmp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[4] = LE_32(PMCS_DEVREG_IT_NEXUS_TIMEOUT);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (void) memcpy(&msg[5], pptr->sas_address, 8);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster CLEAN_MESSAGE(msg, 7);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk->state = PMCS_WORK_STATE_ONCHIP;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster WAIT_FOR(pwrk, 250, result);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_lock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_pwork(pwp, pwrk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (result) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster result = ETIMEDOUT;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster goto out;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster status = LE_32(iomb[2]);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster tmp = LE_32(iomb[3]);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster switch (status) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster case PMCS_DEVREG_OK:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster case PMCS_DEVREG_DEVICE_ALREADY_REGISTERED:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster case PMCS_DEVREG_PHY_ALREADY_REGISTERED:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_validate_devid(pwp->root_phys, pptr, tmp) == B_FALSE) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster result = EEXIST;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster goto out;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else if (status != PMCS_DEVREG_OK) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (tmp == 0xffffffff) { /* F/W bug */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: phy %s already has bogus devid 0x%x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, pptr->path, tmp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster result = EIO;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster goto out;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: phy %s already has a device id 0x%x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, pptr->path, tmp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster default:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: status 0x%x when trying to register device %s",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, status, pptr->path);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster result = EIO;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster goto out;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->device_id = tmp;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->valid_device_id = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "Phy %s/" SAS_ADDR_FMT
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster " registered with device_id 0x%x (portid %d)", pptr->path,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster SAS_ADDR_PRT(pptr->sas_address), tmp, pptr->portid);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterout:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (result);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Deregister a device (remove a device handle).
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Called with PHY locked.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fostervoid
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_deregister_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster struct pmcwork *pwrk;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t msg[PMCS_MSG_SIZE], *ptr, status;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2];
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int result;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwrk == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk->arg = iomb;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk->dtype = pptr->dtype;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (ptr == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_pwork(pwp, pwrk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PMCIN_DEREGISTER_DEVICE_HANDLE));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[1] = LE_32(pwrk->htag);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[2] = LE_32(pptr->device_id);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk->state = PMCS_WORK_STATE_ONCHIP;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster COPY_MESSAGE(ptr, msg, 3);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster WAIT_FOR(pwrk, 250, result);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_pwork(pwp, pwrk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_lock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (result) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster status = LE_32(iomb[2]);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (status != PMCOUT_STATUS_OK) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: status 0x%x when trying to deregister device %s",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, status, pptr->path);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: device %s deregistered", __func__, pptr->path);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->valid_device_id = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->device_id = PMCS_INVALID_DEVICE_ID;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->configured = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->deregister_wait = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Deregister all registered devices.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fostervoid
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_deregister_devices(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Start at the maximum level and walk back to level 0. This only
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * gets done during detach after all threads and timers have been
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * destroyed, so there's no need to hold the softstate or PHY lock.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster while (phyp) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (phyp->children) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_deregister_devices(pwp, phyp->children);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (phyp->valid_device_id) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_deregister_device(pwp, phyp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster phyp = phyp->sibling;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Perform a 'soft' reset on the PMC chip
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterint
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_soft_reset(pmcs_hw_t *pwp, boolean_t no_restart)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t s2, sfrbits, gsm, rapchk, wapchk, wdpchk, spc, tsmode;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_phy_t *pptr;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster char *msg = NULL;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int i;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Disable interrupts
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "%s", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->locks_initted) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->blocked = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 1
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((s2 & PMCS_MSGU_HOST_SOFT_RESET_READY) == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_gsm_reg(pwp, RB6_ACCESS, RB6_NMI_SIGNATURE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_gsm_reg(pwp, RB6_ACCESS, RB6_NMI_SIGNATURE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < 100; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) &
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PMCS_MSGU_HOST_SOFT_RESET_READY;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (s2) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(10000);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) &
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PMCS_MSGU_HOST_SOFT_RESET_READY;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (s2 == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: PMCS_MSGU_HOST_SOFT_RESET_READY never came "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "ready", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_register_dump(pwp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PMCS_MSGU_CPU_SOFT_RESET_READY) == 0 ||
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) &
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PMCS_MSGU_CPU_SOFT_RESET_READY) == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->state = STATE_DEAD;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->blocked = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->locks_initted) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 2
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_gsm_reg(pwp, NMI_EN_VPE0_IOP, 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(10);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_gsm_reg(pwp, NMI_EN_VPE0_AAP1, 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(10);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_topunit(pwp, PMCS_EVENT_INT_ENABLE, 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(10);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_topunit(pwp, PMCS_EVENT_INT_STAT,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_rd_topunit(pwp, PMCS_EVENT_INT_STAT));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(10);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_topunit(pwp, PMCS_ERROR_INT_ENABLE, 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(10);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_topunit(pwp, PMCS_ERROR_INT_STAT,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_rd_topunit(pwp, PMCS_ERROR_INT_STAT));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(10);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster sfrbits = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PMCS_MSGU_AAP_SFR_PROGRESS;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster sfrbits ^= PMCS_MSGU_AAP_SFR_PROGRESS;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "PMCS_MSGU_HOST_SCRATCH0 "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%08x -> %08x", pmcs_rd_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH0),
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster HST_SFT_RESET_SIG);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH0, HST_SFT_RESET_SIG);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 3
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster gsm = pmcs_rd_gsm_reg(pwp, GSM_CFG_AND_RESET);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "GSM %08x -> %08x", gsm,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster gsm & ~PMCS_SOFT_RESET_BITS);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_gsm_reg(pwp, GSM_CFG_AND_RESET, gsm & ~PMCS_SOFT_RESET_BITS);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 4
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rapchk = pmcs_rd_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "READ_ADR_PARITY_CHK_EN "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%08x -> %08x", rapchk, 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN, 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster wapchk = pmcs_rd_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_ADR_PARITY_CHK_EN "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%08x -> %08x", wapchk, 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN, 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster wdpchk = pmcs_rd_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_DATA_PARITY_CHK_EN "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%08x -> %08x", wdpchk, 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN, 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 5
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(100);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 5.5 (Temporary workaround for 1.07.xx Beta)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster tsmode = pmcs_rd_gsm_reg(pwp, PMCS_GPIO_TRISTATE_MODE_ADDR);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "GPIO TSMODE %08x -> %08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster tsmode, tsmode & ~(PMCS_GPIO_TSMODE_BIT0|PMCS_GPIO_TSMODE_BIT1));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_gsm_reg(pwp, PMCS_GPIO_TRISTATE_MODE_ADDR,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster tsmode & ~(PMCS_GPIO_TSMODE_BIT0|PMCS_GPIO_TSMODE_BIT1));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(10);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 6
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster spc, spc & ~(PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_topunit(pwp, PMCS_SPC_RESET,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster spc & ~(PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(10);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 7
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster spc, spc & ~(BDMA_CORE_RSTB|OSSP_RSTB));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_topunit(pwp, PMCS_SPC_RESET, spc & ~(BDMA_CORE_RSTB|OSSP_RSTB));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 8
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(100);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 9
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster spc, spc | (BDMA_CORE_RSTB|OSSP_RSTB));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_topunit(pwp, PMCS_SPC_RESET, spc | (BDMA_CORE_RSTB|OSSP_RSTB));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 10
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(100);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 11
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster gsm = pmcs_rd_gsm_reg(pwp, GSM_CFG_AND_RESET);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "GSM %08x -> %08x", gsm,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster gsm | PMCS_SOFT_RESET_BITS);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_gsm_reg(pwp, GSM_CFG_AND_RESET, gsm | PMCS_SOFT_RESET_BITS);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(10);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 12
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "READ_ADR_PARITY_CHK_EN "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%08x -> %08x", pmcs_rd_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN),
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rapchk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN, rapchk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(10);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_ADR_PARITY_CHK_EN "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%08x -> %08x", pmcs_rd_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN),
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster wapchk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN, wapchk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(10);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_DATA_PARITY_CHK_EN "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%08x -> %08x", pmcs_rd_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN),
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster wapchk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN, wdpchk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(10);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 13
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster spc, spc | (PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_topunit(pwp, PMCS_SPC_RESET,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster spc | (PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 14
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(100);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 15
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (spc = 0, i = 0; i < 1000; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(1000);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((spc & PMCS_MSGU_AAP_SFR_PROGRESS) == sfrbits) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((spc & PMCS_MSGU_AAP_SFR_PROGRESS) != sfrbits) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "SFR didn't toggle (sfr 0x%x)", spc);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->state = STATE_DEAD;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->blocked = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->locks_initted) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 16
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Wait for up to 5 seconds for AAP state to come either ready or error.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < 50; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PMCS_MSGU_AAP_STATE_MASK;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (spc == PMCS_MSGU_AAP_STATE_ERROR ||
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster spc == PMCS_MSGU_AAP_STATE_READY) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(100000);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((spc & PMCS_MSGU_AAP_STATE_MASK) != PMCS_MSGU_AAP_STATE_READY) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "soft reset failed (state 0x%x)", spc);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->state = STATE_DEAD;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->blocked = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->locks_initted) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->state == STATE_DEAD || pwp->state == STATE_UNPROBING ||
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->state == STATE_PROBING || pwp->locks_initted == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->blocked = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->locks_initted) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Return at this point if we dont need to startup.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (no_restart) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(pwp->locks_initted != 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Clean up various soft state.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster bzero(pwp->ports, sizeof (pwp->ports));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_free_all_phys(pwp, pwp->root_phys);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_lock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_clear_phy(pwp, pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->targets) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < pwp->max_dev; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_xscsi_t *xp = pwp->targets[i];
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (xp == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster continue;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&xp->statlock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_clear_xp(pwp, xp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&xp->statlock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster bzero(pwp->shadow_iqpi, sizeof (pwp->shadow_iqpi));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < PMCS_NIQ; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->iqp[i]) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster bzero(pwp->iqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_iqpi(pwp, i, 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_iqci(pwp, i, 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < PMCS_NOQ; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->oqp[i]) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster bzero(pwp->oqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqpi(pwp, i, 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqci(pwp, i, 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->fwlogp) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster bzero(pwp->fwlogp, PMCS_FWLOG_SIZE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster STAILQ_INIT(&pwp->wf);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster bzero(pwp->work, sizeof (pmcwork_t) * pwp->max_cmd);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < pwp->max_cmd - 1; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcwork_t *pwrk = &pwp->work[i];
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster STAILQ_INSERT_TAIL(&pwp->wf, pwrk, next);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Clear out any leftover commands sitting in the work list
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < pwp->max_cmd; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcwork_t *pwrk = &pwp->work[i];
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwrk->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwrk->state == PMCS_WORK_STATE_ONCHIP) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster switch (PMCS_TAG_TYPE(pwrk->htag)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster case PMCS_TAG_TYPE_WAIT:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwrk->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster case PMCS_TAG_TYPE_CBACK:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster case PMCS_TAG_TYPE_NONE:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_pwork(pwp, pwrk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster default:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else if (pwrk->state == PMCS_WORK_STATE_IOCOMPQ) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk->dead = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwrk->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * The other states of NIL, READY and INTR
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * should not be visible outside of a lock being held.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_pwork(pwp, pwrk);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Restore Interrupt Mask
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, pwp->intr_mask);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->blocked = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->mpi_table_setup = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Set up MPI again.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_setup(pwp)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg = "unable to setup MPI tables again";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster goto fail_restart;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_report_fwversion(pwp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Restart MPI
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_start_mpi(pwp)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg = "unable to restart MPI again";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster goto fail_restart;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->blocked = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Run any completions
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PMCS_CQ_RUN(pwp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Delay
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usecwait(1000000);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterfail_restart:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->state = STATE_DEAD;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: Failed: %s", __func__, msg);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Reset a device or a logical unit.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterint
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_reset_dev(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint64_t lun)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int rval = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (ENXIO);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_lock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr->dtype == SAS) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Some devices do not support SAS_I_T_NEXUS_RESET as
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * it is not a mandatory (in SAM4) task management
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * function, while LOGIC_UNIT_RESET is mandatory.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * The problem here is that we need to iterate over
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * all known LUNs to emulate the semantics of
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * "RESET_TARGET".
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * XXX: FIX ME
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (lun == (uint64_t)-1) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster lun = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rval = pmcs_ssp_tmf(pwp, pptr, SAS_LOGICAL_UNIT_RESET, 0, lun,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster NULL);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else if (pptr->dtype == SATA) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (lun != 0ull) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (EINVAL);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rval = pmcs_reset_phy(pwp, pptr, PMCS_PHYOP_LINK_RESET);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: cannot reset a SMP device yet (%s)",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, pptr->path);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (EINVAL);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Now harvest any commands killed by this action
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * by issuing an ABORT for all commands on this device.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * We do this even if the the tmf or reset fails (in case there
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * are any dead commands around to be harvested *anyway*).
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * We don't have to await for the abort to complete.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_abort(pwp, pptr, 0, 1, 0)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->abort_pending = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (rval);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Called with PHY locked.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic int
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_get_device_handle(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr->valid_device_id == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int result = pmcs_register_device(pwp, pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If we changed while registering, punt
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr->changed) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster RESTART_DISCOVERY(pwp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If we had a failure to register, check against errors.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * An ENOMEM error means we just retry (temp resource shortage).
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (result == ENOMEM) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PHY_CHANGED(pwp, pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster RESTART_DISCOVERY(pwp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * An ETIMEDOUT error means we retry (if our counter isn't
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * exhausted)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (result == ETIMEDOUT) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (ddi_get_lbolt() < pptr->config_stop) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PHY_CHANGED(pwp, pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster RESTART_DISCOVERY(pwp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: Retries exhausted for %s, killing",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, pptr->path);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->config_stop = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_kill_changed(pwp, pptr, 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Other errors or no valid device id is fatal, but don't
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * preclude a future action.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (result || pptr->valid_device_id == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: %s could not be registered", __func__,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->path);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterint
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_iport_tgtmap_create(pmcs_iport_t *iport)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(iport);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (iport == NULL)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (B_FALSE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* create target map */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (scsi_hba_tgtmap_create(iport->dip, SCSI_TM_FULLSET, tgtmap_usec,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster NULL, NULL, NULL, &iport->iss_tgtmap) != DDI_SUCCESS) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(iport->pwp, PMCS_PRT_DEBUG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: failed to create tgtmap", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (B_FALSE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (B_TRUE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterint
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_iport_tgtmap_destroy(pmcs_iport_t *iport)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(iport && iport->iss_tgtmap);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((iport == NULL) || (iport->iss_tgtmap == NULL))
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (B_FALSE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* destroy target map */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster scsi_hba_tgtmap_destroy(iport->iss_tgtmap);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (B_TRUE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Remove all phys from an iport's phymap and empty it's phylist.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Called when a port has been reset by the host (see pmcs_intr.c).
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fostervoid
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_iport_teardown_phys(pmcs_iport_t *iport)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_hw_t *pwp;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster sas_phymap_phys_t *phys;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int phynum;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(iport);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(mutex_owned(&iport->lock));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp = iport->pwp;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(pwp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Remove all phys from the iport handle's phy list, unset its
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * primary phy and update its state.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_remove_phy_from_iport(iport, NULL);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport->pptr = NULL;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport->ua_state = UA_PEND_DEACTIVATE;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Remove all phys from the phymap */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster phys = sas_phymap_ua2phys(pwp->hss_phymap, iport->ua);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster while ((phynum = sas_phymap_phys_next(phys)) != -1) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster sas_phymap_phy_rem(pwp->hss_phymap, phynum);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster sas_phymap_phys_free(phys);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Query the phymap and populate the iport handle passed in.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Called with iport lock held.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterint
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_iport_configure_phys(pmcs_iport_t *iport)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_hw_t *pwp;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_phy_t *pptr;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster sas_phymap_phys_t *phys;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int phynum;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int inst;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(iport);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(mutex_owned(&iport->lock));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp = iport->pwp;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(pwp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster inst = ddi_get_instance(iport->dip);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(pwp->root_phys != NULL);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Query the phymap regarding the phys in this iport and populate
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * the iport's phys list. Hereafter this list is maintained via
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * port up and down events in pmcs_intr.c
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(list_is_empty(&iport->phys));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster phys = sas_phymap_ua2phys(pwp->hss_phymap, iport->ua);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster while ((phynum = sas_phymap_phys_next(phys)) != -1) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Grab the phy pointer from root_phys */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr = pwp->root_phys + phynum;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_lock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(pptr->phynum == phynum);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Set a back pointer in the phy to this iport.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->iport = iport;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If this phy is the primary, set a pointer to it on our
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * iport handle, and set our portid from it.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (!pptr->subsidiary) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport->pptr = pptr;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport->portid = pptr->portid;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Finally, insert the phy into our list
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_add_phy_to_iport(iport, pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "%s: found "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "phy %d [0x%p] on iport%d, refcnt(%d)", __func__, phynum,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (void *)pptr, inst, iport->refcnt);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster sas_phymap_phys_free(phys);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster RESTART_DISCOVERY(pwp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (DDI_SUCCESS);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Return the iport that ua is associated with, or NULL. If an iport is
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * returned, it will be held and the caller must release the hold.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic pmcs_iport_t *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_get_iport_by_ua(pmcs_hw_t *pwp, char *ua)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_iport_t *iport = NULL;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rw_enter(&pwp->iports_lock, RW_READER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (iport = list_head(&pwp->iports);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport != NULL;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport = list_next(&pwp->iports, iport)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&iport->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (strcmp(iport->ua, ua) == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&iport->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&iport->refcnt_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport->refcnt++;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&iport->refcnt_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&iport->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rw_exit(&pwp->iports_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (iport);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Return the iport that pptr is associated with, or NULL.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If an iport is returned, there is a hold that the caller must release.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_iport_t *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_get_iport_by_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_iport_t *iport = NULL;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster char *ua;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ua = sas_phymap_lookup_ua(pwp->hss_phymap, pwp->sas_wwns[0],
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_barray2wwn(pptr->sas_address));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (ua) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport = pmcs_get_iport_by_ua(pwp, ua);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (iport) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&iport->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport->ua_state = UA_ACTIVE;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "%s: "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "found iport [0x%p] on ua (%s) for phy [0x%p], "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "refcnt (%d)", __func__, (void *)iport, ua,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (void *)pptr, iport->refcnt);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&iport->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (iport);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Promote the next phy on this port to primary, and return it.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Called when the primary PHY on a port is going down, but the port
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * remains up (see pmcs_intr.c).
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_phy_t *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_promote_next_phy(pmcs_phy_t *prev_primary)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_hw_t *pwp;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_iport_t *iport;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_phy_t *pptr, *child;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int portid;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_lock_phy(prev_primary);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster portid = prev_primary->portid;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport = prev_primary->iport;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp = prev_primary->pwp;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Use the first available phy in this port */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((pptr->portid == portid) && (pptr != prev_primary)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pptr->phy_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(prev_primary);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (NULL);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (iport) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&iport->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport->pptr = pptr;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&iport->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Update the phy handle with the data from the previous primary */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->children = prev_primary->children;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster child = pptr->children;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster while (child) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster child->parent = pptr;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster child = child->sibling;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->ncphy = prev_primary->ncphy;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->width = prev_primary->width;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->dtype = prev_primary->dtype;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->pend_dtype = prev_primary->pend_dtype;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->tolerates_sas2 = prev_primary->tolerates_sas2;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->atdt = prev_primary->atdt;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->portid = prev_primary->portid;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->link_rate = prev_primary->link_rate;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->configured = prev_primary->configured;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->iport = prev_primary->iport;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->target = prev_primary->target;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr->target) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->target->phy = pptr;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Update the phy mask properties for the affected PHYs */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Clear the current values... */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_update_phy_pm_props(pptr, pptr->att_port_pm_tmp,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->tgt_port_pm_tmp, B_FALSE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* ...replace with the values from prev_primary... */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_update_phy_pm_props(pptr, prev_primary->att_port_pm_tmp,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster prev_primary->tgt_port_pm_tmp, B_TRUE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* ...then clear prev_primary's PHY values from the new primary */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_update_phy_pm_props(pptr, prev_primary->att_port_pm,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster prev_primary->tgt_port_pm, B_FALSE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Clear the prev_primary's values */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_update_phy_pm_props(prev_primary, prev_primary->att_port_pm_tmp,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster prev_primary->tgt_port_pm_tmp, B_FALSE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->subsidiary = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster prev_primary->subsidiary = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster prev_primary->children = NULL;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster prev_primary->target = NULL;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->device_id = prev_primary->device_id;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->valid_device_id = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(prev_primary);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * We call pmcs_unlock_phy() on pptr because it now contains the
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * list of children.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fostervoid
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_rele_iport(pmcs_iport_t *iport)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Release a refcnt on this iport. If this is the last reference,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * signal the potential waiter in pmcs_iport_unattach().
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(iport->refcnt > 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&iport->refcnt_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport->refcnt--;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&iport->refcnt_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (iport->refcnt == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster cv_signal(&iport->refcnt_cv);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: iport "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "[0x%p] refcnt (%d)", __func__, (void *)iport, iport->refcnt);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fostervoid
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_phymap_activate(void *arg, char *ua, void **privp)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster _NOTE(ARGUNUSED(privp));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_hw_t *pwp = arg;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_iport_t *iport = NULL;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((pwp->state == STATE_UNPROBING) || (pwp->state == STATE_DEAD)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->phymap_active++;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (scsi_hba_iportmap_iport_add(pwp->hss_iportmap, ua, NULL) !=
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster DDI_SUCCESS) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: failed to "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "add iport handle on unit address [%s]", __func__, ua);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "phymap_active count (%d), added iport handle on unit "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "address [%s]", __func__, pwp->phymap_active, ua);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Set the HBA softstate as our private data for this unit address */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *privp = (void *)pwp;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * We are waiting on attach for this iport node, unless it is still
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * attached. This can happen if a consumer has an outstanding open
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * on our iport node, but the port is down. If this is the case, we
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * need to configure our iport here for reuse.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport = pmcs_get_iport_by_ua(pwp, ua);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (iport) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&iport->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_iport_configure_phys(iport) != DDI_SUCCESS) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "failed to configure phys on iport [0x%p] at "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "unit address (%s)", __func__, (void *)iport, ua);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport->ua_state = UA_ACTIVE;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster &iport->nphy);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&iport->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_rele_iport(iport);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fostervoid
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_phymap_deactivate(void *arg, char *ua, void *privp)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster _NOTE(ARGUNUSED(privp));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_hw_t *pwp = arg;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_iport_t *iport;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->phymap_active--;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (scsi_hba_iportmap_iport_remove(pwp->hss_iportmap, ua) !=
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster DDI_SUCCESS) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: failed to "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "remove iport handle on unit address [%s]", __func__, ua);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "phymap_active count (%d), removed iport handle on unit "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "address [%s]", __func__, pwp->phymap_active, ua);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport = pmcs_get_iport_by_ua(pwp, ua);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (iport == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: failed "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "lookup of iport handle on unit addr (%s)", __func__, ua);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&iport->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport->ua_state = UA_INACTIVE;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport->portid = PMCS_IPORT_INVALID_PORT_ID;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_remove_phy_from_iport(iport, NULL);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&iport->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_rele_iport(iport);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Top-level discovery function
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fostervoid
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_discover(pmcs_hw_t *pwp)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_phy_t *pptr;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_phy_t *root_phy;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster boolean_t config_changed;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster DTRACE_PROBE2(pmcs__discover__entry, ulong_t, pwp->work_flags,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster boolean_t, pwp->config_changed);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->state != STATE_RUNNING) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Ensure we have at least one phymap active */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->phymap_active == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: phymap inactive, exiting", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If no iports have attached, but we have PHYs that are up, we
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * are waiting for iport attach to complete. Restart discovery.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rw_enter(&pwp->iports_lock, RW_READER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (!pwp->iports_attached) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rw_exit(&pwp->iports_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: no iports attached, retry discovery", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rw_exit(&pwp->iports_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->config_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->configuring) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->config_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: configuration already in progress", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_acquire_scratch(pwp, B_FALSE)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->config_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: cannot allocate scratch", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->configuring = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->config_changed = B_FALSE;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->config_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "Discovery begin");
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * First, tell SCSA that we're beginning set operations.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_begin_observations(pwp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * The order of the following traversals is important.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * The first one checks for changed expanders.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * The second one aborts commands for dead devices and deregisters them.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * The third one clears the contents of dead expanders from the tree
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * The fourth one clears now dead devices in expanders that remain.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * 1. Check expanders marked changed (but not dead) to see if they still
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * have the same number of phys and the same SAS address. Mark them,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * their subsidiary phys (if wide) and their descendents dead if
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * anything has changed. Check the devices they contain to see if
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * *they* have changed. If they've changed from type NOTHING we leave
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * them marked changed to be configured later (picking up a new SAS
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * address and link rate if possible). Otherwise, any change in type,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * SAS address or removal of target role will cause us to mark them
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * (and their descendents) as dead (and cause any pending commands
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * and associated devices to be removed).
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * NOTE: We don't want to bail on discovery if the config has
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * changed until *after* we run pmcs_kill_devices.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster root_phy = pwp->root_phys;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster config_changed = pmcs_check_expanders(pwp, root_phy);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * 2. Descend the tree looking for dead devices and kill them
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * by aborting all active commands and then deregistering them.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_kill_devices(pwp, root_phy) || config_changed) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster goto out;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * 3. Check for dead expanders and remove their children from the tree.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * By the time we get here, the devices and commands for them have
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * already been terminated and removed.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * We do this independent of the configuration count changing so we can
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * free any dead device PHYs that were discovered while checking
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * expanders. We ignore any subsidiary phys as pmcs_clear_expander
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * will take care of those.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * NOTE: pmcs_clear_expander requires softstate lock
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Call pmcs_clear_expander for every root PHY. It will
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * recurse and determine which (if any) expanders actually
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * need to be cleared.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_lock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_clear_expander(pwp, pptr, 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * 4. Check for dead devices and nullify them. By the time we get here,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * the devices and commands for them have already been terminated
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * and removed. This is different from step 2 in that this just nulls
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * phys that are part of expanders that are still here but used to
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * be something but are no longer something (e.g., after a pulled
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * disk drive). Note that dead expanders had their contained phys
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * removed from the tree- here, the expanders themselves are
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * nullified (unless they were removed by being contained in another
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * expander phy).
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_clear_phys(pwp, root_phy);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * 5. Now check for and configure new devices.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_configure_new_devices(pwp, root_phy)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster goto restart;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterout:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster DTRACE_PROBE2(pmcs__discover__exit, ulong_t, pwp->work_flags,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster boolean_t, pwp->config_changed);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "Discovery end");
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->config_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->config_changed == B_FALSE) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Observation is stable, report what we currently see to
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * the tgtmaps for delta processing. Start by setting
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * BEGIN on all tgtmaps.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->config_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_report_observations(pwp) == B_FALSE) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster goto restart;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->config_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If config_changed is TRUE, we need to reschedule
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * discovery now.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: Config has changed, will re-run discovery", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_release_scratch(pwp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->configuring = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->config_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster#ifdef DEBUG
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr = pmcs_find_phy_needing_work(pwp, pwp->root_phys);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr != NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (!WORK_IS_SCHEDULED(pwp, PMCS_WORK_DISCOVER)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "PHY %s dead=%d changed=%d configured=%d "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "but no work scheduled", pptr->path, pptr->dead,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->changed, pptr->configured);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster#endif
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterrestart:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Clean up and restart discovery */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_release_scratch(pwp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->config_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->configuring = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster RESTART_DISCOVERY_LOCKED(pwp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->config_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Return any PHY that needs to have scheduled work done. The PHY is returned
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * locked.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic pmcs_phy_t *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_find_phy_needing_work(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_phy_t *cphyp, *pnext;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster while (pptr) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_lock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr->changed || (pptr->dead && pptr->valid_device_id)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pnext = pptr->sibling;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr->children) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster cphyp = pptr->children;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster cphyp = pmcs_find_phy_needing_work(pwp, cphyp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (cphyp) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (cphyp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr = pnext;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (NULL);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * We may (or may not) report observations to SCSA. This is prefaced by
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * issuing a set_begin for each iport target map.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic void
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_begin_observations(pmcs_hw_t *pwp)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_iport_t *iport;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster scsi_hba_tgtmap_t *tgtmap;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rw_enter(&pwp->iports_lock, RW_READER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (iport = list_head(&pwp->iports); iport != NULL;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport = list_next(&pwp->iports, iport)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Unless we have at least one phy up, skip this iport.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Note we don't need to lock the iport for report_skip
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * since it is only used here. We are doing the skip so that
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * the phymap and iportmap stabilization times are honored -
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * giving us the ability to recover port operation within the
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * stabilization time without unconfiguring targets using the
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * port.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (!sas_phymap_uahasphys(pwp->hss_phymap, iport->ua)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport->report_skip = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster continue; /* skip set_begin */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport->report_skip = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster tgtmap = iport->iss_tgtmap;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(tgtmap);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (scsi_hba_tgtmap_set_begin(tgtmap) != DDI_SUCCESS) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: cannot set_begin tgtmap ", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rw_exit(&pwp->iports_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: set begin on tgtmap [0x%p]", __func__, (void *)tgtmap);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rw_exit(&pwp->iports_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Report current observations to SCSA.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic boolean_t
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_report_observations(pmcs_hw_t *pwp)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_iport_t *iport;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster scsi_hba_tgtmap_t *tgtmap;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster char *ap;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_phy_t *pptr;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint64_t wwn;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Observation is stable, report what we currently see to the tgtmaps
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * for delta processing.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr = pwp->root_phys;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster while (pptr) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_lock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Skip PHYs that have nothing attached or are dead.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((pptr->dtype == NOTHING) || pptr->dead) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr = pptr->sibling;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster continue;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr->changed) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: oops, PHY %s changed; restart discovery",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, pptr->path);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (B_FALSE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Get the iport for this root PHY, then call the helper
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * to report observations for this iport's targets
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport = pmcs_get_iport_by_phy(pwp, pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (iport == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* No iport for this tgt */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: no iport for this target", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr = pptr->sibling;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster continue;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (!iport->report_skip) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_report_iport_observations(
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp, iport, pptr) == B_FALSE) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_rele_iport(iport);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (B_FALSE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_rele_iport(iport);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr = pptr->sibling;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * The observation is complete, end sets. Note we will skip any
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * iports that are active, but have no PHYs in them (i.e. awaiting
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * unconfigure). Set to restart discovery if we find this.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rw_enter(&pwp->iports_lock, RW_READER);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (iport = list_head(&pwp->iports);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport != NULL;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport = list_next(&pwp->iports, iport)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (iport->report_skip)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster continue; /* skip set_end */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster tgtmap = iport->iss_tgtmap;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(tgtmap);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (scsi_hba_tgtmap_set_end(tgtmap, 0) != DDI_SUCCESS) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: cannot set_end tgtmap ", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rw_exit(&pwp->iports_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (B_FALSE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: set end on tgtmap [0x%p]", __func__, (void *)tgtmap);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Now that discovery is complete, set up the necessary
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * DDI properties on each iport node.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (iport = list_head(&pwp->iports); iport != NULL;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport = list_next(&pwp->iports, iport)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Set up the 'attached-port' property on the iport */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ap = kmem_zalloc(PMCS_MAX_UA_SIZE, KM_SLEEP);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&iport->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr = iport->pptr;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&iport->lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * This iport is down, but has not been
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * removed from our list (unconfigured).
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Set our value to '0'.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (void) snprintf(ap, 1, "%s", "0");
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Otherwise, set it to remote phy's wwn */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_lock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster wwn = pmcs_barray2wwn(pptr->sas_address);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (void) scsi_wwn_to_wwnstr(wwn, 1, ap);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (ndi_prop_update_string(DDI_DEV_T_NONE, iport->dip,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster SCSI_ADDR_PROP_ATTACHED_PORT, ap) != DDI_SUCCESS) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "to set prop ("SCSI_ADDR_PROP_ATTACHED_PORT")",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster kmem_free(ap, PMCS_MAX_UA_SIZE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rw_exit(&pwp->iports_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (B_TRUE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Report observations into a particular iport's target map
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Called with phyp (and all descendents) locked
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic boolean_t
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_report_iport_observations(pmcs_hw_t *pwp, pmcs_iport_t *iport,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_phy_t *phyp)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_phy_t *lphyp;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster scsi_hba_tgtmap_t *tgtmap;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster scsi_tgtmap_tgt_type_t tgt_type;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster char *ua;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint64_t wwn;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster tgtmap = iport->iss_tgtmap;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(tgtmap);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster lphyp = phyp;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster while (lphyp) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster switch (lphyp->dtype) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster default: /* Skip unknown PHYs. */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* for non-root phys, skip to sibling */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster goto next_phy;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster case SATA:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster case SAS:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster tgt_type = SCSI_TGT_SCSI_DEVICE;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster case EXPANDER:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster tgt_type = SCSI_TGT_SMP_DEVICE;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (lphyp->dead) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster goto next_phy;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster wwn = pmcs_barray2wwn(lphyp->sas_address);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ua = scsi_wwn_to_wwnstr(wwn, 1, NULL);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, lphyp, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "iport_observation: adding %s on tgtmap [0x%p] phy [0x%p]",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ua, (void *)tgtmap, (void*)lphyp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (scsi_hba_tgtmap_set_add(tgtmap, tgt_type, ua, NULL) !=
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster DDI_SUCCESS) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: failed to add address %s", __func__, ua);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster scsi_free_wwnstr(ua);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (B_FALSE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster scsi_free_wwnstr(ua);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (lphyp->children) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_report_iport_observations(pwp, iport,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster lphyp->children) == B_FALSE) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (B_FALSE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* for non-root phys, report siblings too */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosternext_phy:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (IS_ROOT_PHY(lphyp)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster lphyp = NULL;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster lphyp = lphyp->sibling;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (B_TRUE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Check for and configure new devices.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If the changed device is a SATA device, add a SATA device.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If the changed device is a SAS device, add a SAS device.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If the changed device is an EXPANDER device, do a REPORT
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * GENERAL SMP command to find out the number of contained phys.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * For each number of contained phys, allocate a phy, do a
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * DISCOVERY SMP command to find out what kind of device it
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * is and add it to the linked list of phys on the *next* level.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * NOTE: pptr passed in by the caller will be a root PHY
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic int
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_configure_new_devices(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int rval = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_iport_t *iport;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_phy_t *pnext, *orig_pptr = pptr, *root_phy, *pchild;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * First, walk through each PHY at this level
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster while (pptr) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_lock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pnext = pptr->sibling;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Set the new dtype if it has changed
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((pptr->pend_dtype != NEW) &&
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (pptr->pend_dtype != pptr->dtype)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->dtype = pptr->pend_dtype;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr->changed == 0 || pptr->dead || pptr->configured) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster goto next_phy;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Confirm that this target's iport is configured
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster root_phy = pmcs_get_root_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iport = pmcs_get_iport_by_phy(pwp, root_phy);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (iport == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* No iport for this tgt, restart */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: iport not yet configured, "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "retry discovery", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pnext = NULL;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rval = -1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster goto next_phy;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster switch (pptr->dtype) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster case NOTHING:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->changed = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster case SATA:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster case SAS:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->iport = iport;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_new_tport(pwp, pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster case EXPANDER:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_configure_expander(pwp, pptr, iport);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_rele_iport(iport);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pwp->config_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->config_changed) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->config_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pnext = NULL;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster goto next_phy;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pwp->config_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosternext_phy:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr = pnext;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (rval != 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (rval);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Now walk through each PHY again, recalling ourselves if they
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * have children
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr = orig_pptr;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster while (pptr) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_lock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pnext = pptr->sibling;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pchild = pptr->children;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pchild) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rval = pmcs_configure_new_devices(pwp, pchild);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (rval != 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr = pnext;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (rval);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Set all phys and descendent phys as changed if changed == B_TRUE, otherwise
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * mark them all as not changed.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Called with parent PHY locked.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fostervoid
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_set_changed(pmcs_hw_t *pwp, pmcs_phy_t *parent, boolean_t changed,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int level)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_phy_t *pptr;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (level == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (changed) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PHY_CHANGED(pwp, parent);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster parent->changed = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (parent->dtype == EXPANDER && parent->level) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster parent->width = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (parent->children) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_set_changed(pwp, parent->children, changed,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster level + 1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr = parent;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster while (pptr) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (changed) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PHY_CHANGED(pwp, pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->changed = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr->dtype == EXPANDER && pptr->level) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->width = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr->children) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_set_changed(pwp, pptr->children, changed,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster level + 1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr = pptr->sibling;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Take the passed phy mark it and its descendants as dead.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Fire up reconfiguration to abort commands and bury it.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Called with the parent PHY locked.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fostervoid
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_kill_changed(pmcs_hw_t *pwp, pmcs_phy_t *parent, int level)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_phy_t *pptr = parent;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster while (pptr) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->link_rate = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->abort_sent = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->abort_pending = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->need_rl_ext = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr->dead == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PHY_CHANGED(pwp, pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster RESTART_DISCOVERY(pwp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->dead = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr->children) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_kill_changed(pwp, pptr->children, level + 1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Only kill siblings at level > 0
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (level == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr = pptr->sibling;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Go through every PHY and clear any that are dead (unless they're expanders)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic void
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_clear_phys(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_phy_t *pnext, *phyp;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster phyp = pptr;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster while (phyp) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (IS_ROOT_PHY(phyp)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_lock_phy(phyp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((phyp->dtype != EXPANDER) && phyp->dead) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_clear_phy(pwp, phyp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (phyp->children) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_clear_phys(pwp, phyp->children);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pnext = phyp->sibling;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (IS_ROOT_PHY(phyp)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_unlock_phy(phyp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster phyp = pnext;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Clear volatile parts of a phy. Called with PHY locked.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fostervoid
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_clear_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "%s: %s",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, pptr->path);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(mutex_owned(&pptr->phy_lock));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* keep sibling */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* keep children */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* keep parent */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->device_id = PMCS_INVALID_DEVICE_ID;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* keep hw_event_ack */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->ncphy = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* keep phynum */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->width = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->ds_recovery_retries = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->ds_prev_good_recoveries = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->last_good_recovery = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->prev_recovery = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* keep dtype */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->config_stop = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->spinup_hold = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->atdt = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* keep portid */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->link_rate = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->valid_device_id = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->abort_sent = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->abort_pending = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->need_rl_ext = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->subsidiary = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->configured = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->deregister_wait = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Only mark dead if it's not a root PHY and its dtype isn't NOTHING */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* XXX: What about directly attached disks? */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (!IS_ROOT_PHY(pptr) && (pptr->dtype != NOTHING))
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->dead = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->changed = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* keep SAS address */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* keep path */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* keep ref_count */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Don't clear iport on root PHYs - they are handled in pmcs_intr.c */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (!IS_ROOT_PHY(pptr)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->iport = NULL;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* keep target */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Allocate softstate for this target if there isn't already one. If there
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * is, just redo our internal configuration. If it is actually "new", we'll
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * soon get a tran_tgt_init for it.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Called with PHY locked.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic void
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_new_tport(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "%s: phy 0x%p @ %s",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, (void *)pptr, pptr->path);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_configure_phy(pwp, pptr) == B_FALSE) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If the config failed, mark the PHY as changed.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PHY_CHANGED(pwp, pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: pmcs_configure_phy failed for phy 0x%p", __func__,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (void *)pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Mark PHY as no longer changed */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->changed = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If the PHY has no target pointer, see if there's a dead PHY that
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * matches.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr->target == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_reap_dead_phy(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Only assign the device if there is a target for this PHY with a
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * matching SAS address. If an iport is disconnected from one piece
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * of storage and connected to another within the iport stabilization
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * time, we can get the PHY/target mismatch situation.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Otherwise, it'll get done in tran_tgt_init.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr->target) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&pptr->target->statlock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_phy_target_match(pptr) == B_FALSE) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pptr->target->statlock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (!IS_ROOT_PHY(pptr)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_dec_phy_ref_count(pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: Not assigning existing tgt %p for PHY %p "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "(WWN mismatch)", __func__, (void *)pptr->target,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (void *)pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->target = NULL;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (!pmcs_assign_device(pwp, pptr->target)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: pmcs_assign_device failed for target 0x%p",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, (void *)pptr->target);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&pptr->target->statlock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Called with PHY lock held.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic boolean_t
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_configure_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster char *dtype;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(mutex_owned(&pptr->phy_lock));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Mark this device as no longer changed.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->changed = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If we don't have a device handle, get one.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_get_device_handle(pwp, pptr)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (B_FALSE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->configured = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster switch (pptr->dtype) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster case SAS:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster dtype = "SAS";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster case SATA:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster dtype = "SATA";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster case EXPANDER:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster dtype = "SMP";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster default:
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster dtype = "???";
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "config_dev: %s "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "dev %s " SAS_ADDR_FMT " dev id 0x%x lr 0x%x", dtype, pptr->path,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster SAS_ADDR_PRT(pptr->sas_address), pptr->device_id, pptr->link_rate);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (B_TRUE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster}
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Called with PHY locked
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterstatic void
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_configure_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr, pmcs_iport_t *iport)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster{
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_phy_t *ctmp, *clist = NULL, *cnext;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int result, i, nphy = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster boolean_t root_phy = B_FALSE;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ASSERT(iport);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 1- clear our "changed" bit. If we need to retry/restart due
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * to resource shortages, we'll set it again. While we're doing
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * configuration, other events may set it again as well. If the PHY
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * is a root PHY and is currently marked as having changed, reset the
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * config_stop timer as well.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (IS_ROOT_PHY(pptr) && pptr->changed) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->config_stop = ddi_get_lbolt() +
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster drv_usectohz(PMCS_MAX_CONFIG_TIME);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->changed = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 2- make sure we don't overflow
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr->level == PMCS_MAX_XPND-1) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_WARN, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: SAS expansion tree too deep", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 3- Check if this expander is part of a wide phy that has
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * already been configured.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * This is known by checking this level for another EXPANDER device
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * with the same SAS address and isn't already marked as a subsidiary
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * phy and a parent whose SAS address is the same as our SAS address
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * (if there are parents).
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (!IS_ROOT_PHY(pptr)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * No need to lock the parent here because we're in discovery
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * and the only time a PHY's children pointer can change is
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * in discovery; either in pmcs_clear_expander (which has
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * already been called) or here, down below. Plus, trying to
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * grab the parent's lock here can cause deadlock.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ctmp = pptr->parent->children;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ctmp = pwp->root_phys;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster root_phy = B_TRUE;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster while (ctmp) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If we've checked all PHYs up to pptr, we stop. Otherwise,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * we'll be checking for a primary PHY with a higher PHY
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * number than pptr, which will never happen. The primary
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * PHY on non-root expanders will ALWAYS be the lowest
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * numbered PHY.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (ctmp == pptr) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster break;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If pptr and ctmp are root PHYs, just grab the mutex on
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * ctmp. No need to lock the entire tree. If they are not
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * root PHYs, there is no need to lock since a non-root PHY's
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * SAS address and other characteristics can only change in
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * discovery anyway.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (root_phy) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_enter(&ctmp->phy_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (ctmp->dtype == EXPANDER && ctmp->width &&
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster memcmp(ctmp->sas_address, pptr->sas_address, 8) == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster int widephy = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If these phys are not root PHYs, compare their SAS
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * addresses too.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (!root_phy) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (memcmp(ctmp->parent->sas_address,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->parent->sas_address, 8) == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster widephy = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster widephy = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (widephy) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ctmp->width++;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->subsidiary = 1;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Update the primary PHY's attached-port-pm
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * and target-port-pm information with the info
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * from this subsidiary
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_update_phy_pm_props(ctmp,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->att_port_pm_tmp,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->tgt_port_pm_tmp, B_TRUE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: PHY %s part of wide PHY %s "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "(now %d wide)", __func__, pptr->path,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ctmp->path, ctmp->width);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (root_phy) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&ctmp->phy_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster cnext = ctmp->sibling;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (root_phy) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster mutex_exit(&ctmp->phy_lock);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ctmp = cnext;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 4- If we don't have a device handle, get one. Since this
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * is the primary PHY, make sure subsidiary is cleared.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->subsidiary = 0;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->iport = iport;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_get_device_handle(pwp, pptr)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster goto out;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster }
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "Config expander %s "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster SAS_ADDR_FMT " dev id 0x%x lr 0x%x", pptr->path,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster SAS_ADDR_PRT(pptr->sas_address), pptr->device_id, pptr->link_rate);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /*
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 5- figure out how many phys are in this expander.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster nphy = pmcs_expander_get_nphy(pwp, pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (nphy <= 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (nphy == 0 && ddi_get_lbolt() < pptr->config_stop) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PHY_CHANGED(pwp, pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster RESTART_DISCOVERY(pwp);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: Retries exhausted for %s, killing", __func__,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->path);
pptr->config_stop = 0;
pmcs_kill_changed(pwp, pptr, 0);
}
goto out;
}
/*
* Step 6- Allocate a list of phys for this expander and figure out
* what each one is.
*/
for (i = 0; i < nphy; i++) {
ctmp = kmem_cache_alloc(pwp->phy_cache, KM_SLEEP);
bzero(ctmp, sizeof (pmcs_phy_t));
ctmp->device_id = PMCS_INVALID_DEVICE_ID;
ctmp->sibling = clist;
ctmp->pend_dtype = NEW; /* Init pending dtype */
ctmp->config_stop = ddi_get_lbolt() +
drv_usectohz(PMCS_MAX_CONFIG_TIME);
clist = ctmp;
}
mutex_enter(&pwp->config_lock);
if (pwp->config_changed) {
RESTART_DISCOVERY_LOCKED(pwp);
mutex_exit(&pwp->config_lock);
/*
* Clean up the newly allocated PHYs and return
*/
while (clist) {
ctmp = clist->sibling;
kmem_cache_free(pwp->phy_cache, clist);
clist = ctmp;
}
return;
}
mutex_exit(&pwp->config_lock);
/*
* Step 7- Now fill in the rest of the static portions of the phy.
*/
for (i = 0, ctmp = clist; ctmp; ctmp = ctmp->sibling, i++) {
ctmp->parent = pptr;
ctmp->pwp = pwp;
ctmp->level = pptr->level+1;
ctmp->portid = pptr->portid;
if (ctmp->tolerates_sas2) {
ASSERT(i < SAS2_PHYNUM_MAX);
ctmp->phynum = i & SAS2_PHYNUM_MASK;
} else {
ASSERT(i < SAS_PHYNUM_MAX);
ctmp->phynum = i & SAS_PHYNUM_MASK;
}
pmcs_phy_name(pwp, ctmp, ctmp->path, sizeof (ctmp->path));
pmcs_lock_phy(ctmp);
}
/*
* Step 8- Discover things about each phy in the expander.
*/
for (i = 0, ctmp = clist; ctmp; ctmp = ctmp->sibling, i++) {
result = pmcs_expander_content_discover(pwp, pptr, ctmp);
if (result <= 0) {
if (ddi_get_lbolt() < pptr->config_stop) {
PHY_CHANGED(pwp, pptr);
RESTART_DISCOVERY(pwp);
} else {
pptr->config_stop = 0;
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
"%s: Retries exhausted for %s, killing",
__func__, pptr->path);
pmcs_kill_changed(pwp, pptr, 0);
}
goto out;
}
/* Set pend_dtype to dtype for 1st time initialization */
ctmp->pend_dtype = ctmp->dtype;
}
/*
* Step 9- Install the new list on the next level. There should be
* no children pointer on this PHY. If there is, we'd need to know
* how it happened (The expander suddenly got more PHYs?).
*/
ASSERT(pptr->children == NULL);
if (pptr->children != NULL) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, "%s: Already child "
"PHYs attached to PHY %s: This should never happen",
__func__, pptr->path);
goto out;
} else {
pptr->children = clist;
}
clist = NULL;
pptr->ncphy = nphy;
pptr->configured = 1;
/*
* We only set width if we're greater than level 0.
*/
if (pptr->level) {
pptr->width = 1;
}
/*
* Now tell the rest of the world about us, as an SMP node.
*/
pptr->iport = iport;
pmcs_new_tport(pwp, pptr);
out:
while (clist) {
ctmp = clist->sibling;
pmcs_unlock_phy(clist);
kmem_cache_free(pwp->phy_cache, clist);
clist = ctmp;
}
}
/*
* 2. Check expanders marked changed (but not dead) to see if they still have
* the same number of phys and the same SAS address. Mark them, their subsidiary
* phys (if wide) and their descendents dead if anything has changed. Check the
* the devices they contain to see if *they* have changed. If they've changed
* from type NOTHING we leave them marked changed to be configured later
* (picking up a new SAS address and link rate if possible). Otherwise, any
* change in type, SAS address or removal of target role will cause us to
* mark them (and their descendents) as dead and cause any pending commands
* and associated devices to be removed.
*
* Called with PHY (pptr) locked.
*/
static void
pmcs_check_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
{
int nphy, result;
pmcs_phy_t *ctmp, *local, *local_list = NULL, *local_tail = NULL;
boolean_t kill_changed, changed;
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
"%s: check %s", __func__, pptr->path);
/*
* Step 1: Mark phy as not changed. We will mark it changed if we need
* to retry.
*/
pptr->changed = 0;
/*
* Reset the config_stop time. Although we're not actually configuring
* anything here, we do want some indication of when to give up trying
* if we can't communicate with the expander.
*/
pptr->config_stop = ddi_get_lbolt() +
drv_usectohz(PMCS_MAX_CONFIG_TIME);
/*
* Step 2: Figure out how many phys are in this expander. If
* pmcs_expander_get_nphy returns 0 we ran out of resources,
* so reschedule and try later. If it returns another error,
* just return.
*/
nphy = pmcs_expander_get_nphy(pwp, pptr);
if (nphy <= 0) {
if ((nphy == 0) && (ddi_get_lbolt() < pptr->config_stop)) {
PHY_CHANGED(pwp, pptr);
RESTART_DISCOVERY(pwp);
} else {
pptr->config_stop = 0;
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
"%s: Retries exhausted for %s, killing", __func__,
pptr->path);
pmcs_kill_changed(pwp, pptr, 0);
}
return;
}
/*
* Step 3: If the number of phys don't agree, kill the old sub-tree.
*/
if (nphy != pptr->ncphy) {
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
"%s: number of contained phys for %s changed from %d to %d",
__func__, pptr->path, pptr->ncphy, nphy);
/*
* Force a rescan of this expander after dead contents
* are cleared and removed.
*/
pmcs_kill_changed(pwp, pptr, 0);
return;
}
/*
* Step 4: if we're at the bottom of the stack, we're done
* (we can't have any levels below us)
*/
if (pptr->level == PMCS_MAX_XPND-1) {
return;
}
/*
* Step 5: Discover things about each phy in this expander. We do
* this by walking the current list of contained phys and doing a
* content discovery for it to a local phy.
*/
ctmp = pptr->children;
ASSERT(ctmp);
if (ctmp == NULL) {
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
"%s: No children attached to expander @ %s?", __func__,
pptr->path);
return;
}
while (ctmp) {
/*
* Allocate a local PHY to contain the proposed new contents
* and link it to the rest of the local PHYs so that they
* can all be freed later.
*/
local = pmcs_clone_phy(ctmp);
if (local_list == NULL) {
local_list = local;
local_tail = local;
} else {
local_tail->sibling = local;
local_tail = local;
}
/*
* Need to lock the local PHY since pmcs_expander_content_
* discovery may call pmcs_clear_phy on it, which expects
* the PHY to be locked.
*/
pmcs_lock_phy(local);
result = pmcs_expander_content_discover(pwp, pptr, local);
pmcs_unlock_phy(local);
if (result <= 0) {
if (ddi_get_lbolt() < pptr->config_stop) {
PHY_CHANGED(pwp, pptr);
RESTART_DISCOVERY(pwp);
} else {
pptr->config_stop = 0;
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
"%s: Retries exhausted for %s, killing",
__func__, pptr->path);
pmcs_kill_changed(pwp, pptr, 0);
}
/*
* Release all the local PHYs that we allocated.
*/
pmcs_free_phys(pwp, local_list);
return;
}
ctmp = ctmp->sibling;
}
/*
* Step 6: Compare the local PHY's contents to our current PHY. If
* there are changes, take the appropriate action.
* This is done in two steps (step 5 above, and 6 here) so that if we
* have to bail during this process (e.g. pmcs_expander_content_discover
* fails), we haven't actually changed the state of any of the real
* PHYs. Next time we come through here, we'll be starting over from
* scratch. This keeps us from marking a changed PHY as no longer
* changed, but then having to bail only to come back next time and
* think that the PHY hadn't changed. If this were to happen, we
* would fail to properly configure the device behind this PHY.
*/
local = local_list;
ctmp = pptr->children;
while (ctmp) {
changed = B_FALSE;
kill_changed = B_FALSE;
/*
* We set local to local_list prior to this loop so that we
* can simply walk the local_list while we walk this list. The
* two lists should be completely in sync.
*
* Clear the changed flag here.
*/
ctmp->changed = 0;
if (ctmp->dtype != local->dtype) {
if (ctmp->dtype != NOTHING) {
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
"%s: %s type changed from %s to %s "
"(killing)", __func__, ctmp->path,
PHY_TYPE(ctmp), PHY_TYPE(local));
/*
* Force a rescan of this expander after dead
* contents are cleared and removed.
*/
changed = B_TRUE;
kill_changed = B_TRUE;
} else {
changed = B_TRUE;
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
"%s: %s type changed from NOTHING to %s",
__func__, ctmp->path, PHY_TYPE(local));
}
} else if (ctmp->atdt != local->atdt) {
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL, "%s: "
"%s attached device type changed from %d to %d "
"(killing)", __func__, ctmp->path, ctmp->atdt,
local->atdt);
/*
* Force a rescan of this expander after dead
* contents are cleared and removed.
*/
changed = B_TRUE;
if (local->atdt == 0) {
kill_changed = B_TRUE;
}
} else if (ctmp->link_rate != local->link_rate) {
pmcs_prt(pwp, PMCS_PRT_INFO, ctmp, NULL, "%s: %s "
"changed speed from %s to %s", __func__, ctmp->path,
pmcs_get_rate(ctmp->link_rate),
pmcs_get_rate(local->link_rate));
/* If the speed changed from invalid, force rescan */
if (!PMCS_VALID_LINK_RATE(ctmp->link_rate)) {
changed = B_TRUE;
RESTART_DISCOVERY(pwp);
} else {
/* Just update to the new link rate */
ctmp->link_rate = local->link_rate;
}
if (!PMCS_VALID_LINK_RATE(local->link_rate)) {
kill_changed = B_TRUE;
}
} else if (memcmp(ctmp->sas_address, local->sas_address,
sizeof (ctmp->sas_address)) != 0) {
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
"%s: SAS Addr for %s changed from " SAS_ADDR_FMT
"to " SAS_ADDR_FMT " (kill old tree)", __func__,
ctmp->path, SAS_ADDR_PRT(ctmp->sas_address),
SAS_ADDR_PRT(local->sas_address));
/*
* Force a rescan of this expander after dead
* contents are cleared and removed.
*/
changed = B_TRUE;
} else {
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
"%s: %s looks the same (type %s)",
__func__, ctmp->path, PHY_TYPE(ctmp));
/*
* If EXPANDER, still mark it changed so we
* re-evaluate its contents. If it's not an expander,
* but it hasn't been configured, also mark it as
* changed so that it will undergo configuration.
*/
if (ctmp->dtype == EXPANDER) {
changed = B_TRUE;
} else if ((ctmp->dtype != NOTHING) &&
!ctmp->configured) {
ctmp->changed = 1;
} else {
/* It simply hasn't changed */
ctmp->changed = 0;
}
}
/*
* If the PHY changed, call pmcs_kill_changed if indicated,
* update its contents to reflect its current state and mark it
* as changed.
*/
if (changed) {
/*
* pmcs_kill_changed will mark the PHY as changed, so
* only do PHY_CHANGED if we did not do kill_changed.
*/
if (kill_changed) {
pmcs_kill_changed(pwp, ctmp, 0);
} else {
/*
* If we're not killing the device, it's not
* dead. Mark the PHY as changed.
*/
PHY_CHANGED(pwp, ctmp);
if (ctmp->dead) {
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
ctmp, NULL, "%s: Unmarking PHY %s "
"dead, restarting discovery",
__func__, ctmp->path);
ctmp->dead = 0;
RESTART_DISCOVERY(pwp);
}
}
/*
* If the dtype of this PHY is now NOTHING, mark it as
* unconfigured. Set pend_dtype to what the new dtype
* is. It'll get updated at the end of the discovery
* process.
*/
if (local->dtype == NOTHING) {
bzero(ctmp->sas_address,
sizeof (local->sas_address));
ctmp->atdt = 0;
ctmp->link_rate = 0;
ctmp->pend_dtype = NOTHING;
ctmp->configured = 0;
} else {
(void) memcpy(ctmp->sas_address,
local->sas_address,
sizeof (local->sas_address));
ctmp->atdt = local->atdt;
ctmp->link_rate = local->link_rate;
ctmp->pend_dtype = local->dtype;
}
}
local = local->sibling;
ctmp = ctmp->sibling;
}
/*
* If we got to here, that means we were able to see all the PHYs
* and we can now update all of the real PHYs with the information
* we got on the local PHYs. Once that's done, free all the local
* PHYs.
*/
pmcs_free_phys(pwp, local_list);
}
/*
* Top level routine to check expanders. We call pmcs_check_expander for
* each expander. Since we're not doing any configuration right now, it
* doesn't matter if this is breadth-first.
*/
static boolean_t
pmcs_check_expanders(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
{
pmcs_phy_t *phyp, *pnext, *pchild;
boolean_t config_changed = B_FALSE;
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
"%s: %s", __func__, pptr->path);
/*
* Check each expander at this level
*/
phyp = pptr;
while (phyp && !config_changed) {
pmcs_lock_phy(phyp);
if ((phyp->dtype == EXPANDER) && phyp->changed &&
!phyp->dead && !phyp->subsidiary &&
phyp->configured) {
pmcs_check_expander(pwp, phyp);
}
pnext = phyp->sibling;
pmcs_unlock_phy(phyp);
mutex_enter(&pwp->config_lock);
config_changed = pwp->config_changed;
mutex_exit(&pwp->config_lock);
phyp = pnext;
}
if (config_changed) {
return (config_changed);
}
/*
* Now check the children
*/
phyp = pptr;
while (phyp && !config_changed) {
pmcs_lock_phy(phyp);
pnext = phyp->sibling;
pchild = phyp->children;
pmcs_unlock_phy(phyp);
if (pchild) {
(void) pmcs_check_expanders(pwp, pchild);
}
mutex_enter(&pwp->config_lock);
config_changed = pwp->config_changed;
mutex_exit(&pwp->config_lock);
phyp = pnext;
}
/*
* We're done
*/
return (config_changed);
}
/*
* Called with softstate and PHY locked
*/
static void
pmcs_clear_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr, int level)
{
pmcs_phy_t *ctmp;
ASSERT(mutex_owned(&pwp->lock));
ASSERT(mutex_owned(&pptr->phy_lock));
ASSERT(pptr->level < PMCS_MAX_XPND - 1);
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
"%s: checking %s", __func__, pptr->path);
ctmp = pptr->children;
while (ctmp) {
/*
* If the expander is dead, mark its children dead
*/
if (pptr->dead) {
ctmp->dead = 1;
}
if (ctmp->dtype == EXPANDER) {
pmcs_clear_expander(pwp, ctmp, level + 1);
}
ctmp = ctmp->sibling;
}
/*
* If this expander is not dead, we're done here.
*/
if (!pptr->dead) {
return;
}
/*
* Now snip out the list of children below us and release them
*/
ctmp = pptr->children;
while (ctmp) {
pmcs_phy_t *nxt = ctmp->sibling;
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
"%s: dead PHY 0x%p (%s) (ref_count %d)", __func__,
(void *)ctmp, ctmp->path, ctmp->ref_count);
/*
* Put this PHY on the dead PHY list for the watchdog to
* clean up after any outstanding work has completed.
*/
mutex_enter(&pwp->dead_phylist_lock);
ctmp->dead_next = pwp->dead_phys;
pwp->dead_phys = ctmp;
mutex_exit(&pwp->dead_phylist_lock);
pmcs_unlock_phy(ctmp);
ctmp = nxt;
}
pptr->children = NULL;
/*
* Clear subsidiary phys as well. Getting the parent's PHY lock
* is only necessary if level == 0 since otherwise the parent is
* already locked.
*/
if (!IS_ROOT_PHY(pptr)) {
if (level == 0) {
mutex_enter(&pptr->parent->phy_lock);
}
ctmp = pptr->parent->children;
if (level == 0) {
mutex_exit(&pptr->parent->phy_lock);
}
} else {
ctmp = pwp->root_phys;
}
while (ctmp) {
if (ctmp == pptr) {
ctmp = ctmp->sibling;
continue;
}
/*
* We only need to lock subsidiary PHYs on the level 0
* expander. Any children of that expander, subsidiaries or
* not, will already be locked.
*/
if (level == 0) {
pmcs_lock_phy(ctmp);
}
if (ctmp->dtype != EXPANDER || ctmp->subsidiary == 0 ||
memcmp(ctmp->sas_address, pptr->sas_address,
sizeof (ctmp->sas_address)) != 0) {
if (level == 0) {
pmcs_unlock_phy(ctmp);
}
ctmp = ctmp->sibling;
continue;
}
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
"%s: subsidiary %s", __func__, ctmp->path);
pmcs_clear_phy(pwp, ctmp);
if (level == 0) {
pmcs_unlock_phy(ctmp);
}
ctmp = ctmp->sibling;
}
pmcs_clear_phy(pwp, pptr);
}
/*
* Called with PHY locked and with scratch acquired. We return 0 if
* we fail to allocate resources or notice that the configuration
* count changed while we were running the command. We return
* less than zero if we had an I/O error or received an unsupported
* configuration. Otherwise we return the number of phys in the
* expander.
*/
#define DFM(m, y) if (m == NULL) m = y
static int
pmcs_expander_get_nphy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
{
struct pmcwork *pwrk;
char buf[64];
const uint_t rdoff = 0x100; /* returned data offset */
smp_response_frame_t *srf;
smp_report_general_resp_t *srgr;
uint32_t msg[PMCS_MSG_SIZE], *ptr, htag, status, ival;
int result;
ival = 0x40001100;
again:
pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
if (pwrk == NULL) {
result = 0;
goto out;
}
(void) memset(pwp->scratch, 0x77, PMCS_SCRATCH_SIZE);
pwrk->arg = pwp->scratch;
pwrk->dtype = pptr->dtype;
mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
if (ptr == NULL) {
mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, NULL,
"%s: GET_IQ_ENTRY failed", __func__);
pmcs_pwork(pwp, pwrk);
result = 0;
goto out;
}
msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST));
msg[1] = LE_32(pwrk->htag);
msg[2] = LE_32(pptr->device_id);
msg[3] = LE_32((4 << SMP_REQUEST_LENGTH_SHIFT) | SMP_INDIRECT_RESPONSE);
/*
* Send SMP REPORT GENERAL (of either SAS1.1 or SAS2 flavors).
*/
msg[4] = BE_32(ival);
msg[5] = 0;
msg[6] = 0;
msg[7] = 0;
msg[8] = 0;
msg[9] = 0;
msg[10] = 0;
msg[11] = 0;
msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff));
msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff));
msg[14] = LE_32(PMCS_SCRATCH_SIZE - rdoff);
msg[15] = 0;
COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
/* SMP serialization */
pmcs_smp_acquire(pptr->iport);
pwrk->state = PMCS_WORK_STATE_ONCHIP;
htag = pwrk->htag;
INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
pmcs_unlock_phy(pptr);
WAIT_FOR(pwrk, 1000, result);
/* Release SMP lock before reacquiring PHY lock */
pmcs_smp_release(pptr->iport);
pmcs_lock_phy(pptr);
pmcs_pwork(pwp, pwrk);
mutex_enter(&pwp->config_lock);
if (pwp->config_changed) {
RESTART_DISCOVERY_LOCKED(pwp);
mutex_exit(&pwp->config_lock);
result = 0;
goto out;
}
mutex_exit(&pwp->config_lock);
if (result) {
pmcs_timed_out(pwp, htag, __func__);
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
"%s: Issuing SMP ABORT for htag 0x%08x", __func__, htag);
if (pmcs_abort(pwp, pptr, htag, 0, 0)) {
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
"%s: Unable to issue SMP ABORT for htag 0x%08x",
__func__, htag);
} else {
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
"%s: Issuing SMP ABORT for htag 0x%08x",
__func__, htag);
}
result = 0;
goto out;
}
ptr = (void *)pwp->scratch;
status = LE_32(ptr[2]);
if (status == PMCOUT_STATUS_UNDERFLOW ||
status == PMCOUT_STATUS_OVERFLOW) {
pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, pptr, NULL,
"%s: over/underflow", __func__);
status = PMCOUT_STATUS_OK;
}
srf = (smp_response_frame_t *)&((uint32_t *)pwp->scratch)[rdoff >> 2];
srgr = (smp_report_general_resp_t *)
&((uint32_t *)pwp->scratch)[(rdoff >> 2)+1];
if (status != PMCOUT_STATUS_OK) {
char *nag = NULL;
(void) snprintf(buf, sizeof (buf),
"%s: SMP op failed (0x%x)", __func__, status);
switch (status) {
case PMCOUT_STATUS_IO_PORT_IN_RESET:
DFM(nag, "I/O Port In Reset");
/* FALLTHROUGH */
case PMCOUT_STATUS_ERROR_HW_TIMEOUT:
DFM(nag, "Hardware Timeout");
/* FALLTHROUGH */
case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE:
DFM(nag, "Internal SMP Resource Failure");
/* FALLTHROUGH */
case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
DFM(nag, "PHY Not Ready");
/* FALLTHROUGH */
case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
DFM(nag, "Connection Rate Not Supported");
/* FALLTHROUGH */
case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
DFM(nag, "Open Retry Timeout");
/* FALLTHROUGH */
case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR:
DFM(nag, "Response Connection Error");
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
"%s: expander %s SMP operation failed (%s)",
__func__, pptr->path, nag);
break;
/*
* For the IO_DS_NON_OPERATIONAL case, we need to kick off
* device state recovery and return 0 so that the caller
* doesn't assume this expander is dead for good.
*/
case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL: {
pmcs_xscsi_t *xp = pptr->target;
pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, pptr, xp,
"%s: expander %s device state non-operational",
__func__, pptr->path);
if (xp == NULL) {
/*
* Kick off recovery right now.
*/
SCHEDULE_WORK(pwp, PMCS_WORK_DS_ERR_RECOVERY);
(void) ddi_taskq_dispatch(pwp->tq, pmcs_worker,
pwp, DDI_NOSLEEP);
} else {
mutex_enter(&xp->statlock);
pmcs_start_dev_state_recovery(xp, pptr);
mutex_exit(&xp->statlock);
}
break;
}
default:
pmcs_print_entry(pwp, PMCS_PRT_DEBUG, buf, ptr);
result = -EIO;
break;
}
} else if (srf->srf_frame_type != SMP_FRAME_TYPE_RESPONSE) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
"%s: bad response frame type 0x%x",
__func__, srf->srf_frame_type);
result = -EINVAL;
} else if (srf->srf_function != SMP_FUNC_REPORT_GENERAL) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
"%s: bad response function 0x%x",
__func__, srf->srf_function);
result = -EINVAL;
} else if (srf->srf_result != 0) {
/*
* Check to see if we have a value of 3 for failure and
* whether we were using a SAS2.0 allocation length value
* and retry without it.
*/
if (srf->srf_result == 3 && (ival & 0xff00)) {
ival &= ~0xff00;
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
"%s: err 0x%x with SAS2 request- retry with SAS1",
__func__, srf->srf_result);
goto again;
}
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
"%s: bad response 0x%x", __func__, srf->srf_result);
result = -EINVAL;
} else if (srgr->srgr_configuring) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
"%s: expander at phy %s is still configuring",
__func__, pptr->path);
result = 0;
} else {
result = srgr->srgr_number_of_phys;
if (ival & 0xff00) {
pptr->tolerates_sas2 = 1;
}
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
"%s has %d phys and %s SAS2", pptr->path, result,
pptr->tolerates_sas2? "tolerates" : "does not tolerate");
}
out:
return (result);
}
/*
* Called with expander locked (and thus, pptr) as well as all PHYs up to
* the root, and scratch acquired. Return 0 if we fail to allocate resources
* or notice that the configuration changed while we were running the command.
*
* We return less than zero if we had an I/O error or received an
* unsupported configuration.
*/
static int
pmcs_expander_content_discover(pmcs_hw_t *pwp, pmcs_phy_t *expander,
pmcs_phy_t *pptr)
{
struct pmcwork *pwrk;
char buf[64];
uint8_t sas_address[8];
uint8_t att_sas_address[8];
smp_response_frame_t *srf;
smp_discover_resp_t *sdr;
const uint_t rdoff = 0x100; /* returned data offset */
uint8_t *roff;
uint32_t status, *ptr, msg[PMCS_MSG_SIZE], htag;
int result;
uint8_t ini_support;
uint8_t tgt_support;
pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, expander);
if (pwrk == NULL) {
result = 0;
goto out;
}
(void) memset(pwp->scratch, 0x77, PMCS_SCRATCH_SIZE);
pwrk->arg = pwp->scratch;
pwrk->dtype = expander->dtype;
msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST));
msg[1] = LE_32(pwrk->htag);
msg[2] = LE_32(expander->device_id);
msg[3] = LE_32((12 << SMP_REQUEST_LENGTH_SHIFT) |
SMP_INDIRECT_RESPONSE);
/*
* Send SMP DISCOVER (of either SAS1.1 or SAS2 flavors).
*/
if (expander->tolerates_sas2) {
msg[4] = BE_32(0x40101B00);
} else {
msg[4] = BE_32(0x40100000);
}
msg[5] = 0;
msg[6] = BE_32((pptr->phynum << 16));
msg[7] = 0;
msg[8] = 0;
msg[9] = 0;
msg[10] = 0;
msg[11] = 0;
msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff));
msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff));
msg[14] = LE_32(PMCS_SCRATCH_SIZE - rdoff);
msg[15] = 0;
mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
if (ptr == NULL) {
mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
result = 0;
goto out;
}
COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
/* SMP serialization */
pmcs_smp_acquire(expander->iport);
pwrk->state = PMCS_WORK_STATE_ONCHIP;
htag = pwrk->htag;
INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
/*
* Drop PHY lock while waiting so other completions aren't potentially
* blocked.
*/
pmcs_unlock_phy(expander);
WAIT_FOR(pwrk, 1000, result);
/* Release SMP lock before reacquiring PHY lock */
pmcs_smp_release(expander->iport);
pmcs_lock_phy(expander);
pmcs_pwork(pwp, pwrk);
mutex_enter(&pwp->config_lock);
if (pwp->config_changed) {
RESTART_DISCOVERY_LOCKED(pwp);
mutex_exit(&pwp->config_lock);
result = 0;
goto out;
}
mutex_exit(&pwp->config_lock);
if (result) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
if (pmcs_abort(pwp, expander, htag, 0, 0)) {
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
"%s: Unable to issue SMP ABORT for htag 0x%08x",
__func__, htag);
} else {
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
"%s: Issuing SMP ABORT for htag 0x%08x",
__func__, htag);
}
result = -ETIMEDOUT;
goto out;
}
ptr = (void *)pwp->scratch;
/*
* Point roff to the DMA offset for returned data
*/
roff = pwp->scratch;
roff += rdoff;
srf = (smp_response_frame_t *)roff;
sdr = (smp_discover_resp_t *)(roff+4);
status = LE_32(ptr[2]);
if (status == PMCOUT_STATUS_UNDERFLOW ||
status == PMCOUT_STATUS_OVERFLOW) {
pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, pptr, NULL,
"%s: over/underflow", __func__);
status = PMCOUT_STATUS_OK;
}
if (status != PMCOUT_STATUS_OK) {
char *nag = NULL;
(void) snprintf(buf, sizeof (buf),
"%s: SMP op failed (0x%x)", __func__, status);
switch (status) {
case PMCOUT_STATUS_ERROR_HW_TIMEOUT:
DFM(nag, "Hardware Timeout");
/* FALLTHROUGH */
case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE:
DFM(nag, "Internal SMP Resource Failure");
/* FALLTHROUGH */
case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
DFM(nag, "PHY Not Ready");
/* FALLTHROUGH */
case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
DFM(nag, "Connection Rate Not Supported");
/* FALLTHROUGH */
case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
DFM(nag, "Open Retry Timeout");
/* FALLTHROUGH */
case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR:
DFM(nag, "Response Connection Error");
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
"%s: expander %s SMP operation failed (%s)",
__func__, pptr->path, nag);
break;
default:
pmcs_print_entry(pwp, PMCS_PRT_DEBUG, buf, ptr);
result = -EIO;
break;
}
goto out;
} else if (srf->srf_frame_type != SMP_FRAME_TYPE_RESPONSE) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
"%s: bad response frame type 0x%x",
__func__, srf->srf_frame_type);
result = -EINVAL;
goto out;
} else if (srf->srf_function != SMP_FUNC_DISCOVER) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
"%s: bad response function 0x%x",
__func__, srf->srf_function);
result = -EINVAL;
goto out;
} else if (srf->srf_result != SMP_RES_FUNCTION_ACCEPTED) {
result = pmcs_smp_function_result(pwp, srf);
/* Need not fail if PHY is Vacant */
if (result != SMP_RES_PHY_VACANT) {
result = -EINVAL;
goto out;
}
}
ini_support = (sdr->sdr_attached_sata_host |
(sdr->sdr_attached_smp_initiator << 1) |
(sdr->sdr_attached_stp_initiator << 2) |
(sdr->sdr_attached_ssp_initiator << 3));
tgt_support = (sdr->sdr_attached_sata_device |
(sdr->sdr_attached_smp_target << 1) |
(sdr->sdr_attached_stp_target << 2) |
(sdr->sdr_attached_ssp_target << 3));
pmcs_wwn2barray(BE_64(sdr->sdr_sas_addr), sas_address);
pmcs_wwn2barray(BE_64(sdr->sdr_attached_sas_addr), att_sas_address);
switch (sdr->sdr_attached_device_type) {
case SAS_IF_DTYPE_ENDPOINT:
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
"exp_content: %s atdt=0x%x lr=%x is=%x ts=%x SAS="
SAS_ADDR_FMT " attSAS=" SAS_ADDR_FMT " atPHY=%x",
pptr->path,
sdr->sdr_attached_device_type,
sdr->sdr_negotiated_logical_link_rate,
ini_support,
tgt_support,
SAS_ADDR_PRT(sas_address),
SAS_ADDR_PRT(att_sas_address),
sdr->sdr_attached_phy_identifier);
if (sdr->sdr_attached_sata_device ||
sdr->sdr_attached_stp_target) {
pptr->dtype = SATA;
} else if (sdr->sdr_attached_ssp_target) {
pptr->dtype = SAS;
} else if (tgt_support || ini_support) {
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
"%s: %s has tgt support=%x init support=(%x)",
__func__, pptr->path, tgt_support, ini_support);
}
pmcs_update_phy_pm_props(pptr, (1ULL <<
sdr->sdr_attached_phy_identifier), (1ULL << pptr->phynum),
B_TRUE);
break;
case SAS_IF_DTYPE_EDGE:
case SAS_IF_DTYPE_FANOUT:
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
"exp_content: %s atdt=0x%x lr=%x is=%x ts=%x SAS="
SAS_ADDR_FMT " attSAS=" SAS_ADDR_FMT " atPHY=%x",
pptr->path,
sdr->sdr_attached_device_type,
sdr->sdr_negotiated_logical_link_rate,
ini_support,
tgt_support,
SAS_ADDR_PRT(sas_address),
SAS_ADDR_PRT(att_sas_address),
sdr->sdr_attached_phy_identifier);
if (sdr->sdr_attached_smp_target) {
/*
* Avoid configuring phys that just point back
* at a parent phy
*/
if (expander->parent &&
memcmp(expander->parent->sas_address,
att_sas_address,
sizeof (expander->parent->sas_address)) == 0) {
pmcs_prt(pwp, PMCS_PRT_DEBUG3, pptr, NULL,
"%s: skipping port back to parent "
"expander (%s)", __func__, pptr->path);
pptr->dtype = NOTHING;
break;
}
pptr->dtype = EXPANDER;
} else if (tgt_support || ini_support) {
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
"%s has tgt support=%x init support=(%x)",
pptr->path, tgt_support, ini_support);
pptr->dtype = EXPANDER;
}
pmcs_update_phy_pm_props(pptr, (1ULL <<
sdr->sdr_attached_phy_identifier), (1ULL << pptr->phynum),
B_TRUE);
break;
default:
pptr->dtype = NOTHING;
break;
}
if (pptr->dtype != NOTHING) {
pmcs_phy_t *ctmp;
/*
* If the attached device is a SATA device and the expander
* is (possibly) a SAS2 compliant expander, check for whether
* there is a NAA=5 WWN field starting at this offset and
* use that for the SAS Address for this device.
*/
if (expander->tolerates_sas2 && pptr->dtype == SATA &&
(roff[SAS_ATTACHED_NAME_OFFSET] >> 8) == 0x5) {
(void) memcpy(pptr->sas_address,
&roff[SAS_ATTACHED_NAME_OFFSET], 8);
} else {
(void) memcpy(pptr->sas_address, att_sas_address, 8);
}
pptr->atdt = (sdr->sdr_attached_device_type);
/*
* Now run up from the expander's parent up to the top to
* make sure we only use the least common link_rate.
*/
for (ctmp = expander->parent; ctmp; ctmp = ctmp->parent) {
if (ctmp->link_rate <
sdr->sdr_negotiated_logical_link_rate) {
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
"%s: derating link rate from %x to %x due "
"to %s being slower", pptr->path,
sdr->sdr_negotiated_logical_link_rate,
ctmp->link_rate,
ctmp->path);
sdr->sdr_negotiated_logical_link_rate =
ctmp->link_rate;
}
}
pptr->link_rate = sdr->sdr_negotiated_logical_link_rate;
pptr->state.prog_min_rate = sdr->sdr_prog_min_phys_link_rate;
pptr->state.hw_min_rate = sdr->sdr_hw_min_phys_link_rate;
pptr->state.prog_max_rate = sdr->sdr_prog_max_phys_link_rate;
pptr->state.hw_max_rate = sdr->sdr_hw_max_phys_link_rate;
PHY_CHANGED(pwp, pptr);
} else {
pmcs_clear_phy(pwp, pptr);
}
result = 1;
out:
return (result);
}
/*
* Get a work structure and assign it a tag with type and serial number
* If a structure is returned, it is returned locked.
*/
pmcwork_t *
pmcs_gwork(pmcs_hw_t *pwp, uint32_t tag_type, pmcs_phy_t *phyp)
{
pmcwork_t *p;
uint16_t snum;
uint32_t off;
mutex_enter(&pwp->wfree_lock);
p = STAILQ_FIRST(&pwp->wf);
if (p == NULL) {
/*
* If we couldn't get a work structure, it's time to bite
* the bullet, grab the pfree_lock and copy over all the
* work structures from the pending free list to the actual
* free list. This shouldn't happen all that often.
*/
mutex_enter(&pwp->pfree_lock);
pwp->wf.stqh_first = pwp->pf.stqh_first;
pwp->wf.stqh_last = pwp->pf.stqh_last;
STAILQ_INIT(&pwp->pf);
mutex_exit(&pwp->pfree_lock);
p = STAILQ_FIRST(&pwp->wf);
if (p == NULL) {
mutex_exit(&pwp->wfree_lock);
return (NULL);
}
}
STAILQ_REMOVE(&pwp->wf, p, pmcwork, next);
snum = pwp->wserno++;
mutex_exit(&pwp->wfree_lock);
off = p - pwp->work;
mutex_enter(&p->lock);
ASSERT(p->state == PMCS_WORK_STATE_NIL);
ASSERT(p->htag == PMCS_TAG_FREE);
p->htag = (tag_type << PMCS_TAG_TYPE_SHIFT) & PMCS_TAG_TYPE_MASK;
p->htag |= ((snum << PMCS_TAG_SERNO_SHIFT) & PMCS_TAG_SERNO_MASK);
p->htag |= ((off << PMCS_TAG_INDEX_SHIFT) & PMCS_TAG_INDEX_MASK);
p->start = gethrtime();
p->state = PMCS_WORK_STATE_READY;
p->ssp_event = 0;
p->dead = 0;
if (phyp) {
p->phy = phyp;
pmcs_inc_phy_ref_count(phyp);
}
return (p);
}
/*
* Called with pwrk lock held. Returned with lock released.
*/
void
pmcs_pwork(pmcs_hw_t *pwp, pmcwork_t *p)
{
ASSERT(p != NULL);
ASSERT(mutex_owned(&p->lock));
p->last_ptr = p->ptr;
p->last_arg = p->arg;
p->last_phy = p->phy;
p->last_xp = p->xp;
p->last_htag = p->htag;
p->last_state = p->state;
p->finish = gethrtime();
if (p->phy) {
pmcs_dec_phy_ref_count(p->phy);
}
p->state = PMCS_WORK_STATE_NIL;
p->htag = PMCS_TAG_FREE;
p->xp = NULL;
p->ptr = NULL;
p->arg = NULL;
p->phy = NULL;
p->abt_htag = 0;
p->timer = 0;
mutex_exit(&p->lock);
if (mutex_tryenter(&pwp->wfree_lock) == 0) {
mutex_enter(&pwp->pfree_lock);
STAILQ_INSERT_TAIL(&pwp->pf, p, next);
mutex_exit(&pwp->pfree_lock);
} else {
STAILQ_INSERT_TAIL(&pwp->wf, p, next);
mutex_exit(&pwp->wfree_lock);
}
}
/*
* Find a work structure based upon a tag and make sure that the tag
* serial number matches the work structure we've found.
* If a structure is found, its lock is held upon return.
*/
pmcwork_t *
pmcs_tag2wp(pmcs_hw_t *pwp, uint32_t htag)
{
pmcwork_t *p;
uint32_t idx = PMCS_TAG_INDEX(htag);
p = &pwp->work[idx];
mutex_enter(&p->lock);
if (p->htag == htag) {
return (p);
}
mutex_exit(&p->lock);
pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
"INDEX 0x%x HTAG 0x%x got p->htag 0x%x", idx, htag, p->htag);
return (NULL);
}
/*
* Issue an abort for a command or for all commands.
*
* Since this can be called from interrupt context,
* we don't wait for completion if wait is not set.
*
* Called with PHY lock held.
*/
int
pmcs_abort(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint32_t tag, int all_cmds,
int wait)
{
pmcwork_t *pwrk;
pmcs_xscsi_t *tgt;
uint32_t msg[PMCS_MSG_SIZE], *ptr;
int result, abt_type;
uint32_t abt_htag, status;
if (pptr->abort_all_start) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, "%s: ABORT_ALL for "
"(%s) already in progress.", __func__, pptr->path);
return (EBUSY);
}
switch (pptr->dtype) {
case SAS:
abt_type = PMCIN_SSP_ABORT;
break;
case SATA:
abt_type = PMCIN_SATA_ABORT;
break;
case EXPANDER:
abt_type = PMCIN_SMP_ABORT;
break;
default:
return (0);
}
pwrk = pmcs_gwork(pwp, wait ? PMCS_TAG_TYPE_WAIT : PMCS_TAG_TYPE_NONE,
pptr);
if (pwrk == NULL) {
pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__);
return (ENOMEM);
}
pwrk->dtype = pptr->dtype;
if (wait) {
pwrk->arg = msg;
}
if (pptr->valid_device_id == 0) {
pmcs_pwork(pwp, pwrk);
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
"%s: Invalid DeviceID", __func__);
return (ENODEV);
}
msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, abt_type));
msg[1] = LE_32(pwrk->htag);
msg[2] = LE_32(pptr->device_id);
if (all_cmds) {
msg[3] = 0;
msg[4] = LE_32(1);
pwrk->ptr = NULL;
pptr->abort_all_start = gethrtime();
} else {
msg[3] = LE_32(tag);
msg[4] = 0;
pwrk->abt_htag = tag;
}
mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
if (ptr == NULL) {
mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
pmcs_pwork(pwp, pwrk);
pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__);
return (ENOMEM);
}
COPY_MESSAGE(ptr, msg, 5);
if (all_cmds) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
"%s: aborting all commands for %s device %s. (htag=0x%x)",
__func__, pmcs_get_typename(pptr->dtype), pptr->path,
msg[1]);
} else {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
"%s: aborting tag 0x%x for %s device %s. (htag=0x%x)",
__func__, tag, pmcs_get_typename(pptr->dtype), pptr->path,
msg[1]);
}
pwrk->state = PMCS_WORK_STATE_ONCHIP;
INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
if (!wait) {
mutex_exit(&pwrk->lock);
return (0);
}
abt_htag = pwrk->htag;
pmcs_unlock_phy(pwrk->phy);
WAIT_FOR(pwrk, 1000, result);
pmcs_lock_phy(pwrk->phy);
tgt = pwrk->xp;
pmcs_pwork(pwp, pwrk);
if (tgt != NULL) {
mutex_enter(&tgt->aqlock);
if (!STAILQ_EMPTY(&tgt->aq)) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
"%s: Abort complete (result=0x%x), but "
"aq not empty (tgt 0x%p), waiting",
__func__, result, (void *)tgt);
cv_wait(&tgt->abort_cv, &tgt->aqlock);
}
mutex_exit(&tgt->aqlock);
}
if (all_cmds) {
pptr->abort_all_start = 0;
cv_signal(&pptr->abort_all_cv);
}
if (result) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
"%s: Abort (htag 0x%08x) request timed out",
__func__, abt_htag);
if (tgt != NULL) {
mutex_enter(&tgt->statlock);
if ((tgt->dev_state != PMCS_DEVICE_STATE_IN_RECOVERY) &&
(tgt->dev_state !=
PMCS_DEVICE_STATE_NON_OPERATIONAL)) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
"%s: Trying DS error recovery for tgt 0x%p",
__func__, (void *)tgt);
(void) pmcs_send_err_recovery_cmd(pwp,
PMCS_DEVICE_STATE_IN_RECOVERY, pptr, tgt);
}
mutex_exit(&tgt->statlock);
}
return (ETIMEDOUT);
}
status = LE_32(msg[2]);
if (status != PMCOUT_STATUS_OK) {
/*
* The only non-success status are IO_NOT_VALID &
* IO_ABORT_IN_PROGRESS.
* In case of IO_ABORT_IN_PROGRESS, the other ABORT cmd's
* status is of concern and this duplicate cmd status can
* be ignored.
* If IO_NOT_VALID, that's not an error per-se.
* For abort of single I/O complete the command anyway.
* If, however, we were aborting all, that is a problem
* as IO_NOT_VALID really means that the IO or device is
* not there. So, discovery process will take of the cleanup.
*/
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
"%s: abort result 0x%x", __func__, LE_32(msg[2]));
if (all_cmds) {
PHY_CHANGED(pwp, pptr);
RESTART_DISCOVERY(pwp);
} else {
return (EINVAL);
}
return (0);
}
if (tgt != NULL) {
mutex_enter(&tgt->statlock);
if (tgt->dev_state == PMCS_DEVICE_STATE_IN_RECOVERY) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
"%s: Restoring OPERATIONAL dev_state for tgt 0x%p",
__func__, (void *)tgt);
(void) pmcs_send_err_recovery_cmd(pwp,
PMCS_DEVICE_STATE_OPERATIONAL, pptr, tgt);
}
mutex_exit(&tgt->statlock);
}
return (0);
}
/*
* Issue a task management function to an SSP device.
*
* Called with PHY lock held.
* statlock CANNOT be held upon entry.
*/
int
pmcs_ssp_tmf(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint8_t tmf, uint32_t tag,
uint64_t lun, uint32_t *response)
{
int result, ds;
uint8_t local[PMCS_QENTRY_SIZE << 1], *xd;
sas_ssp_rsp_iu_t *rptr = (void *)local;
static const uint8_t ssp_rsp_evec[] = {
0x58, 0x61, 0x56, 0x72, 0x00
};
uint32_t msg[PMCS_MSG_SIZE], *ptr, status;
struct pmcwork *pwrk;
pmcs_xscsi_t *xp;
pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
if (pwrk == NULL) {
pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__);
return (ENOMEM);
}
/*
* NB: We use the PMCS_OQ_GENERAL outbound queue
* NB: so as to not get entangled in normal I/O
* NB: processing.
*/
msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
PMCIN_SSP_INI_TM_START));
msg[1] = LE_32(pwrk->htag);
msg[2] = LE_32(pptr->device_id);
if (tmf == SAS_ABORT_TASK || tmf == SAS_QUERY_TASK) {
msg[3] = LE_32(tag);
} else {
msg[3] = 0;
}
msg[4] = LE_32(tmf);
msg[5] = BE_32((uint32_t)lun);
msg[6] = BE_32((uint32_t)(lun >> 32));
msg[7] = LE_32(PMCIN_MESSAGE_REPORT);
mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
if (ptr == NULL) {
mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
pmcs_pwork(pwp, pwrk);
pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__);
return (ENOMEM);
}
COPY_MESSAGE(ptr, msg, 7);
pwrk->arg = msg;
pwrk->dtype = pptr->dtype;
xp = pptr->target;
if (xp != NULL) {
mutex_enter(&xp->statlock);
if (xp->dev_state == PMCS_DEVICE_STATE_NON_OPERATIONAL) {
mutex_exit(&xp->statlock);
mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
pmcs_pwork(pwp, pwrk);
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, "%s: Not "
"sending '%s' because DS is '%s'", __func__,
pmcs_tmf2str(tmf), pmcs_status_str
(PMCOUT_STATUS_IO_DS_NON_OPERATIONAL));
return (EIO);
}
mutex_exit(&xp->statlock);
}
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
"%s: sending '%s' to %s (lun %llu) tag 0x%x", __func__,
pmcs_tmf2str(tmf), pptr->path, (unsigned long long) lun, tag);
pwrk->state = PMCS_WORK_STATE_ONCHIP;
INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
pmcs_unlock_phy(pptr);
/*
* This is a command sent to the target device, so it can take
* significant amount of time to complete when path & device is busy.
* Set a timeout to 20 seconds
*/
WAIT_FOR(pwrk, 20000, result);
pmcs_lock_phy(pptr);
pmcs_pwork(pwp, pwrk);
if (result) {
if (xp == NULL) {
return (ETIMEDOUT);
}
mutex_enter(&xp->statlock);
pmcs_start_dev_state_recovery(xp, pptr);
mutex_exit(&xp->statlock);
return (ETIMEDOUT);
}
status = LE_32(msg[2]);
if (status != PMCOUT_STATUS_OK) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
"%s: status %s for TMF %s action to %s, lun %llu",
__func__, pmcs_status_str(status), pmcs_tmf2str(tmf),
pptr->path, (unsigned long long) lun);
if ((status == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) ||
(status == PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK) ||
(status == PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS)) {
ds = PMCS_DEVICE_STATE_NON_OPERATIONAL;
} else if (status == PMCOUT_STATUS_IO_DS_IN_RECOVERY) {
/*
* If the status is IN_RECOVERY, it's an indication
* that it's now time for us to request to have the
* device state set to OPERATIONAL since we're the ones
* that requested recovery to begin with.
*/
ds = PMCS_DEVICE_STATE_OPERATIONAL;
} else {
ds = PMCS_DEVICE_STATE_IN_RECOVERY;
}
if (xp != NULL) {
mutex_enter(&xp->statlock);
if (xp->dev_state != ds) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
"%s: Sending err recovery cmd"
" for tgt 0x%p (status = %s)",
__func__, (void *)xp,
pmcs_status_str(status));
(void) pmcs_send_err_recovery_cmd(pwp, ds,
pptr, xp);
}
mutex_exit(&xp->statlock);
}
return (EIO);
} else {
ds = PMCS_DEVICE_STATE_OPERATIONAL;
if (xp != NULL) {
mutex_enter(&xp->statlock);
if (xp->dev_state != ds) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
"%s: Sending err recovery cmd"
" for tgt 0x%p (status = %s)",
__func__, (void *)xp,
pmcs_status_str(status));
(void) pmcs_send_err_recovery_cmd(pwp, ds,
pptr, xp);
}
mutex_exit(&xp->statlock);
}
}
if (LE_32(msg[3]) == 0) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
"TMF completed with no response");
return (EIO);
}
pmcs_endian_transform(pwp, local, &msg[5], ssp_rsp_evec);
xd = (uint8_t *)(&msg[5]);
xd += SAS_RSP_HDR_SIZE;
if (rptr->datapres != SAS_RSP_DATAPRES_RESPONSE_DATA) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
"%s: TMF response not RESPONSE DATA (0x%x)",
__func__, rptr->datapres);
return (EIO);
}
if (rptr->response_data_length != 4) {
pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
"Bad SAS RESPONSE DATA LENGTH", msg);
return (EIO);
}
(void) memcpy(&status, xd, sizeof (uint32_t));
status = BE_32(status);
if (response != NULL)
*response = status;
/*
* The status is actually in the low-order byte. The upper three
* bytes contain additional information for the TMFs that support them.
* However, at this time we do not issue any of those. In the other
* cases, the upper three bytes are supposed to be 0, but it appears
* they aren't always. Just mask them off.
*/
switch (status & 0xff) {
case SAS_RSP_TMF_COMPLETE:
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
"%s: TMF complete", __func__);
result = 0;
break;
case SAS_RSP_TMF_SUCCEEDED:
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
"%s: TMF succeeded", __func__);
result = 0;
break;
case SAS_RSP_INVALID_FRAME:
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
"%s: TMF returned INVALID FRAME", __func__);
result = EIO;
break;
case SAS_RSP_TMF_NOT_SUPPORTED:
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
"%s: TMF returned TMF NOT SUPPORTED", __func__);
result = EIO;
break;
case SAS_RSP_TMF_FAILED:
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
"%s: TMF returned TMF FAILED", __func__);
result = EIO;
break;
case SAS_RSP_TMF_INCORRECT_LUN:
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
"%s: TMF returned INCORRECT LUN", __func__);
result = EIO;
break;
case SAS_RSP_OVERLAPPED_OIPTTA:
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
"%s: TMF returned OVERLAPPED INITIATOR PORT TRANSFER TAG "
"ATTEMPTED", __func__);
result = EIO;
break;
default:
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
"%s: TMF returned unknown code 0x%x", __func__, status);
result = EIO;
break;
}
return (result);
}
/*
* Called with PHY lock held and scratch acquired
*/
int
pmcs_sata_abort_ncq(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
{
const char *utag_fail_fmt = "%s: untagged NCQ command failure";
const char *tag_fail_fmt = "%s: NCQ command failure (tag 0x%x)";
uint32_t msg[PMCS_QENTRY_SIZE], *ptr, result, status;
uint8_t *fp = pwp->scratch, ds;
fis_t fis;
pmcwork_t *pwrk;
pmcs_xscsi_t *tgt;
pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
if (pwrk == NULL) {
return (ENOMEM);
}
msg[0] = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE,
PMCIN_SATA_HOST_IO_START));
msg[1] = LE_32(pwrk->htag);
msg[2] = LE_32(pptr->device_id);
msg[3] = LE_32(512);
msg[4] = LE_32(SATA_PROTOCOL_PIO | PMCIN_DATADIR_2_INI);
msg[5] = LE_32((READ_LOG_EXT << 16) | (C_BIT << 8) | FIS_REG_H2DEV);
msg[6] = LE_32(0x10);
msg[8] = LE_32(1);
msg[9] = 0;
msg[10] = 0;
msg[11] = 0;
msg[12] = LE_32(DWORD0(pwp->scratch_dma));
msg[13] = LE_32(DWORD1(pwp->scratch_dma));
msg[14] = LE_32(512);
msg[15] = 0;
pwrk->arg = msg;
pwrk->dtype = pptr->dtype;
mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
if (ptr == NULL) {
mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
pmcs_pwork(pwp, pwrk);
return (ENOMEM);
}
COPY_MESSAGE(ptr, msg, PMCS_QENTRY_SIZE);
pwrk->state = PMCS_WORK_STATE_ONCHIP;
INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
pmcs_unlock_phy(pptr);
WAIT_FOR(pwrk, 250, result);
pmcs_lock_phy(pptr);
pmcs_pwork(pwp, pwrk);
tgt = pptr->target;
if (result) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, pmcs_timeo, __func__);
return (EIO);
}
status = LE_32(msg[2]);
if (status != PMCOUT_STATUS_OK || LE_32(msg[3])) {
if (tgt == NULL) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
"%s: cannot find target for phy 0x%p for "
"dev state recovery", __func__, (void *)pptr);
return (EIO);
}
mutex_enter(&tgt->statlock);
pmcs_print_entry(pwp, PMCS_PRT_DEBUG, "READ LOG EXT", msg);
if ((status == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) ||
(status == PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK) ||
(status == PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS)) {
ds = PMCS_DEVICE_STATE_NON_OPERATIONAL;
} else {
ds = PMCS_DEVICE_STATE_IN_RECOVERY;
}
if (tgt->dev_state != ds) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, "%s: Trying "
"SATA DS Recovery for tgt(0x%p) for status(%s)",
__func__, (void *)tgt, pmcs_status_str(status));
(void) pmcs_send_err_recovery_cmd(pwp, ds, pptr, tgt);
}
mutex_exit(&tgt->statlock);
return (EIO);
}
fis[0] = (fp[4] << 24) | (fp[3] << 16) | (fp[2] << 8) | FIS_REG_D2H;
fis[1] = (fp[8] << 24) | (fp[7] << 16) | (fp[6] << 8) | fp[5];
fis[2] = (fp[12] << 24) | (fp[11] << 16) | (fp[10] << 8) | fp[9];
fis[3] = (fp[16] << 24) | (fp[15] << 16) | (fp[14] << 8) | fp[13];
fis[4] = 0;
if (fp[0] & 0x80) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
utag_fail_fmt, __func__);
} else {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
tag_fail_fmt, __func__, fp[0] & 0x1f);
}
pmcs_fis_dump(pwp, fis);
pptr->need_rl_ext = 0;
return (0);
}
/*
* Transform a structure from CPU to Device endian format, or
* vice versa, based upon a transformation vector.
*
* A transformation vector is an array of bytes, each byte
* of which is defined thusly:
*
* bit 7: from CPU to desired endian, otherwise from desired endian
* to CPU format
* bit 6: Big Endian, else Little Endian
* bits 5-4:
* 00 Undefined
* 01 One Byte quantities
* 02 Two Byte quantities
* 03 Four Byte quantities
*
* bits 3-0:
* 00 Undefined
* Number of quantities to transform
*
* The vector is terminated by a 0 value.
*/
void
pmcs_endian_transform(pmcs_hw_t *pwp, void *orig_out, void *orig_in,
const uint8_t *xfvec)
{
uint8_t c, *out = orig_out, *in = orig_in;
if (xfvec == NULL) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"%s: null xfvec", __func__);
return;
}
if (out == NULL) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"%s: null out", __func__);
return;
}
if (in == NULL) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"%s: null in", __func__);
return;
}
while ((c = *xfvec++) != 0) {
int nbyt = (c & 0xf);
int size = (c >> 4) & 0x3;
int bige = (c >> 4) & 0x4;
switch (size) {
case 1:
{
while (nbyt-- > 0) {
*out++ = *in++;
}
break;
}
case 2:
{
uint16_t tmp;
while (nbyt-- > 0) {
(void) memcpy(&tmp, in, sizeof (uint16_t));
if (bige) {
tmp = BE_16(tmp);
} else {
tmp = LE_16(tmp);
}
(void) memcpy(out, &tmp, sizeof (uint16_t));
out += sizeof (uint16_t);
in += sizeof (uint16_t);
}
break;
}
case 3:
{
uint32_t tmp;
while (nbyt-- > 0) {
(void) memcpy(&tmp, in, sizeof (uint32_t));
if (bige) {
tmp = BE_32(tmp);
} else {
tmp = LE_32(tmp);
}
(void) memcpy(out, &tmp, sizeof (uint32_t));
out += sizeof (uint32_t);
in += sizeof (uint32_t);
}
break;
}
default:
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"%s: bad size", __func__);
return;
}
}
}
const char *
pmcs_get_rate(unsigned int linkrt)
{
const char *rate;
switch (linkrt) {
case SAS_LINK_RATE_1_5GBIT:
rate = "1.5";
break;
case SAS_LINK_RATE_3GBIT:
rate = "3.0";
break;
case SAS_LINK_RATE_6GBIT:
rate = "6.0";
break;
default:
rate = "???";
break;
}
return (rate);
}
const char *
pmcs_get_typename(pmcs_dtype_t type)
{
switch (type) {
case NOTHING:
return ("NIL");
case SATA:
return ("SATA");
case SAS:
return ("SSP");
case EXPANDER:
return ("EXPANDER");
}
return ("????");
}
const char *
pmcs_tmf2str(int tmf)
{
switch (tmf) {
case SAS_ABORT_TASK:
return ("Abort Task");
case SAS_ABORT_TASK_SET:
return ("Abort Task Set");
case SAS_CLEAR_TASK_SET:
return ("Clear Task Set");
case SAS_LOGICAL_UNIT_RESET:
return ("Logical Unit Reset");
case SAS_I_T_NEXUS_RESET:
return ("I_T Nexus Reset");
case SAS_CLEAR_ACA:
return ("Clear ACA");
case SAS_QUERY_TASK:
return ("Query Task");
case SAS_QUERY_TASK_SET:
return ("Query Task Set");
case SAS_QUERY_UNIT_ATTENTION:
return ("Query Unit Attention");
default:
return ("Unknown");
}
}
const char *
pmcs_status_str(uint32_t status)
{
switch (status) {
case PMCOUT_STATUS_OK:
return ("OK");
case PMCOUT_STATUS_ABORTED:
return ("ABORTED");
case PMCOUT_STATUS_OVERFLOW:
return ("OVERFLOW");
case PMCOUT_STATUS_UNDERFLOW:
return ("UNDERFLOW");
case PMCOUT_STATUS_FAILED:
return ("FAILED");
case PMCOUT_STATUS_ABORT_RESET:
return ("ABORT_RESET");
case PMCOUT_STATUS_IO_NOT_VALID:
return ("IO_NOT_VALID");
case PMCOUT_STATUS_NO_DEVICE:
return ("NO_DEVICE");
case PMCOUT_STATUS_ILLEGAL_PARAMETER:
return ("ILLEGAL_PARAMETER");
case PMCOUT_STATUS_LINK_FAILURE:
return ("LINK_FAILURE");
case PMCOUT_STATUS_PROG_ERROR:
return ("PROG_ERROR");
case PMCOUT_STATUS_EDC_IN_ERROR:
return ("EDC_IN_ERROR");
case PMCOUT_STATUS_EDC_OUT_ERROR:
return ("EDC_OUT_ERROR");
case PMCOUT_STATUS_ERROR_HW_TIMEOUT:
return ("ERROR_HW_TIMEOUT");
case PMCOUT_STATUS_XFER_ERR_BREAK:
return ("XFER_ERR_BREAK");
case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
return ("XFER_ERR_PHY_NOT_READY");
case PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED:
return ("OPEN_CNX_PROTOCOL_NOT_SUPPORTED");
case PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION:
return ("OPEN_CNX_ERROR_ZONE_VIOLATION");
case PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK:
return ("OPEN_CNX_ERROR_BREAK");
case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
return ("OPEN_CNX_ERROR_IT_NEXUS_LOSS");
case PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION:
return ("OPENCNX_ERROR_BAD_DESTINATION");
case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
return ("OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED");
case PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
return ("OPEN_CNX_ERROR_STP_RESOURCES_BUSY");
case PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION:
return ("OPEN_CNX_ERROR_WRONG_DESTINATION");
case PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_EROOR:
return ("OPEN_CNX_ERROR_UNKNOWN_EROOR");
case PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED:
return ("IO_XFER_ERROR_NAK_RECEIVED");
case PMCOUT_STATUS_XFER_ERROR_ACK_NAK_TIMEOUT:
return ("XFER_ERROR_ACK_NAK_TIMEOUT");
case PMCOUT_STATUS_XFER_ERROR_PEER_ABORTED:
return ("XFER_ERROR_PEER_ABORTED");
case PMCOUT_STATUS_XFER_ERROR_RX_FRAME:
return ("XFER_ERROR_RX_FRAME");
case PMCOUT_STATUS_IO_XFER_ERROR_DMA:
return ("IO_XFER_ERROR_DMA");
case PMCOUT_STATUS_XFER_ERROR_CREDIT_TIMEOUT:
return ("XFER_ERROR_CREDIT_TIMEOUT");
case PMCOUT_STATUS_XFER_ERROR_SATA_LINK_TIMEOUT:
return ("XFER_ERROR_SATA_LINK_TIMEOUT");
case PMCOUT_STATUS_XFER_ERROR_SATA:
return ("XFER_ERROR_SATA");
case PMCOUT_STATUS_XFER_ERROR_REJECTED_NCQ_MODE:
return ("XFER_ERROR_REJECTED_NCQ_MODE");
case PMCOUT_STATUS_XFER_ERROR_ABORTED_DUE_TO_SRST:
return ("XFER_ERROR_ABORTED_DUE_TO_SRST");
case PMCOUT_STATUS_XFER_ERROR_ABORTED_NCQ_MODE:
return ("XFER_ERROR_ABORTED_NCQ_MODE");
case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
return ("IO_XFER_OPEN_RETRY_TIMEOUT");
case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR:
return ("SMP_RESP_CONNECTION_ERROR");
case PMCOUT_STATUS_XFER_ERROR_UNEXPECTED_PHASE:
return ("XFER_ERROR_UNEXPECTED_PHASE");
case PMCOUT_STATUS_XFER_ERROR_RDY_OVERRUN:
return ("XFER_ERROR_RDY_OVERRUN");
case PMCOUT_STATUS_XFER_ERROR_RDY_NOT_EXPECTED:
return ("XFER_ERROR_RDY_NOT_EXPECTED");
case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT:
return ("XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT");
case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK:
return ("XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK");
case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK:
return ("XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK");
case PMCOUT_STATUS_XFER_ERROR_OFFSET_MISMATCH:
return ("XFER_ERROR_OFFSET_MISMATCH");
case PMCOUT_STATUS_XFER_ERROR_ZERO_DATA_LEN:
return ("XFER_ERROR_ZERO_DATA_LEN");
case PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED:
return ("XFER_CMD_FRAME_ISSUED");
case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE:
return ("ERROR_INTERNAL_SMP_RESOURCE");
case PMCOUT_STATUS_IO_PORT_IN_RESET:
return ("IO_PORT_IN_RESET");
case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL:
return ("DEVICE STATE NON-OPERATIONAL");
case PMCOUT_STATUS_IO_DS_IN_RECOVERY:
return ("DEVICE STATE IN RECOVERY");
default:
return (NULL);
}
}
uint64_t
pmcs_barray2wwn(uint8_t ba[8])
{
uint64_t result = 0;
int i;
for (i = 0; i < 8; i++) {
result <<= 8;
result |= ba[i];
}
return (result);
}
void
pmcs_wwn2barray(uint64_t wwn, uint8_t ba[8])
{
int i;
for (i = 0; i < 8; i++) {
ba[7 - i] = wwn & 0xff;
wwn >>= 8;
}
}
void
pmcs_report_fwversion(pmcs_hw_t *pwp)
{
const char *fwsupport;
switch (PMCS_FW_TYPE(pwp)) {
case PMCS_FW_TYPE_RELEASED:
fwsupport = "Released";
break;
case PMCS_FW_TYPE_DEVELOPMENT:
fwsupport = "Development";
break;
case PMCS_FW_TYPE_ALPHA:
fwsupport = "Alpha";
break;
case PMCS_FW_TYPE_BETA:
fwsupport = "Beta";
break;
default:
fwsupport = "Special";
break;
}
pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
"Chip Revision: %c; F/W Revision %x.%x.%x %s", 'A' + pwp->chiprev,
PMCS_FW_MAJOR(pwp), PMCS_FW_MINOR(pwp), PMCS_FW_MICRO(pwp),
fwsupport);
}
void
pmcs_phy_name(pmcs_hw_t *pwp, pmcs_phy_t *pptr, char *obuf, size_t olen)
{
if (pptr->parent) {
pmcs_phy_name(pwp, pptr->parent, obuf, olen);
(void) snprintf(obuf, olen, "%s.%02x", obuf, pptr->phynum);
} else {
(void) snprintf(obuf, olen, "pp%02x", pptr->phynum);
}
}
/*
* Implementation for pmcs_find_phy_by_devid.
* If the PHY is found, it is returned locked.
*/
static pmcs_phy_t *
pmcs_find_phy_by_devid_impl(pmcs_phy_t *phyp, uint32_t device_id)
{
pmcs_phy_t *match, *cphyp, *nphyp;
ASSERT(!mutex_owned(&phyp->phy_lock));
while (phyp) {
pmcs_lock_phy(phyp);
if ((phyp->valid_device_id) && (phyp->device_id == device_id)) {
return (phyp);
}
if (phyp->children) {
cphyp = phyp->children;
pmcs_unlock_phy(phyp);
match = pmcs_find_phy_by_devid_impl(cphyp, device_id);
if (match) {
ASSERT(mutex_owned(&match->phy_lock));
return (match);
}
pmcs_lock_phy(phyp);
}
if (IS_ROOT_PHY(phyp)) {
pmcs_unlock_phy(phyp);
phyp = NULL;
} else {
nphyp = phyp->sibling;
pmcs_unlock_phy(phyp);
phyp = nphyp;
}
}
return (NULL);
}
/*
* If the PHY is found, it is returned locked
*/
pmcs_phy_t *
pmcs_find_phy_by_devid(pmcs_hw_t *pwp, uint32_t device_id)
{
pmcs_phy_t *phyp, *match = NULL;
phyp = pwp->root_phys;
while (phyp) {
match = pmcs_find_phy_by_devid_impl(phyp, device_id);
if (match) {
ASSERT(mutex_owned(&match->phy_lock));
return (match);
}
phyp = phyp->sibling;
}
return (NULL);
}
/*
* This function is called as a sanity check to ensure that a newly registered
* PHY doesn't have a device_id that exists with another registered PHY.
*/
static boolean_t
pmcs_validate_devid(pmcs_phy_t *parent, pmcs_phy_t *phyp, uint32_t device_id)
{
pmcs_phy_t *pptr;
boolean_t rval;
pptr = parent;
while (pptr) {
if (pptr->valid_device_id && (pptr != phyp) &&
(pptr->device_id == device_id)) {
pmcs_prt(pptr->pwp, PMCS_PRT_DEBUG, pptr, NULL,
"%s: phy %s already exists as %s with "
"device id 0x%x", __func__, phyp->path,
pptr->path, device_id);
return (B_FALSE);
}
if (pptr->children) {
rval = pmcs_validate_devid(pptr->children, phyp,
device_id);
if (rval == B_FALSE) {
return (rval);
}
}
pptr = pptr->sibling;
}
/* This PHY and device_id are valid */
return (B_TRUE);
}
/*
* If the PHY is found, it is returned locked
*/
static pmcs_phy_t *
pmcs_find_phy_by_wwn_impl(pmcs_phy_t *phyp, uint8_t *wwn)
{
pmcs_phy_t *matched_phy, *cphyp, *nphyp;
ASSERT(!mutex_owned(&phyp->phy_lock));
while (phyp) {
pmcs_lock_phy(phyp);
if (phyp->valid_device_id) {
if (memcmp(phyp->sas_address, wwn, 8) == 0) {
return (phyp);
}
}
if (phyp->children) {
cphyp = phyp->children;
pmcs_unlock_phy(phyp);
matched_phy = pmcs_find_phy_by_wwn_impl(cphyp, wwn);
if (matched_phy) {
ASSERT(mutex_owned(&matched_phy->phy_lock));
return (matched_phy);
}
pmcs_lock_phy(phyp);
}
/*
* Only iterate through non-root PHYs
*/
if (IS_ROOT_PHY(phyp)) {
pmcs_unlock_phy(phyp);
phyp = NULL;
} else {
nphyp = phyp->sibling;
pmcs_unlock_phy(phyp);
phyp = nphyp;
}
}
return (NULL);
}
pmcs_phy_t *
pmcs_find_phy_by_wwn(pmcs_hw_t *pwp, uint64_t wwn)
{
uint8_t ebstr[8];
pmcs_phy_t *pptr, *matched_phy;
pmcs_wwn2barray(wwn, ebstr);
pptr = pwp->root_phys;
while (pptr) {
matched_phy = pmcs_find_phy_by_wwn_impl(pptr, ebstr);
if (matched_phy) {
ASSERT(mutex_owned(&matched_phy->phy_lock));
return (matched_phy);
}
pptr = pptr->sibling;
}
return (NULL);
}
/*
* pmcs_find_phy_by_sas_address
*
* Find a PHY that both matches "sas_addr" and is on "iport".
* If a matching PHY is found, it is returned locked.
*/
pmcs_phy_t *
pmcs_find_phy_by_sas_address(pmcs_hw_t *pwp, pmcs_iport_t *iport,
pmcs_phy_t *root, char *sas_addr)
{
int ua_form = 1;
uint64_t wwn;
char addr[PMCS_MAX_UA_SIZE];
pmcs_phy_t *pptr, *pnext, *pchild;
if (root == NULL) {
pptr = pwp->root_phys;
} else {
pptr = root;
}
while (pptr) {
pmcs_lock_phy(pptr);
/*
* If the PHY is dead or does not have a valid device ID,
* skip it.
*/
if ((pptr->dead) || (!pptr->valid_device_id)) {
goto next_phy;
}
if (pptr->iport != iport) {
goto next_phy;
}
wwn = pmcs_barray2wwn(pptr->sas_address);
(void *) scsi_wwn_to_wwnstr(wwn, ua_form, addr);
if (strncmp(addr, sas_addr, strlen(addr)) == 0) {
return (pptr);
}
if (pptr->children) {
pchild = pptr->children;
pmcs_unlock_phy(pptr);
pnext = pmcs_find_phy_by_sas_address(pwp, iport, pchild,
sas_addr);
if (pnext) {
return (pnext);
}
pmcs_lock_phy(pptr);
}
next_phy:
pnext = pptr->sibling;
pmcs_unlock_phy(pptr);
pptr = pnext;
}
return (NULL);
}
void
pmcs_fis_dump(pmcs_hw_t *pwp, fis_t fis)
{
switch (fis[0] & 0xff) {
case FIS_REG_H2DEV:
pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
"FIS REGISTER HOST TO DEVICE: "
"OP=0x%02x Feature=0x%04x Count=0x%04x Device=0x%02x "
"LBA=%llu", BYTE2(fis[0]), BYTE3(fis[2]) << 8 |
BYTE3(fis[0]), WORD0(fis[3]), BYTE3(fis[1]),
(unsigned long long)
(((uint64_t)fis[2] & 0x00ffffff) << 24 |
((uint64_t)fis[1] & 0x00ffffff)));
break;
case FIS_REG_D2H:
pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
"FIS REGISTER DEVICE TO HOST: Status=0x%02x "
"Error=0x%02x Dev=0x%02x Count=0x%04x LBA=%llu",
BYTE2(fis[0]), BYTE3(fis[0]), BYTE3(fis[1]), WORD0(fis[3]),
(unsigned long long)(((uint64_t)fis[2] & 0x00ffffff) << 24 |
((uint64_t)fis[1] & 0x00ffffff)));
break;
default:
pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
"FIS: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x",
fis[0], fis[1], fis[2], fis[3], fis[4], fis[5], fis[6]);
break;
}
}
void
pmcs_print_entry(pmcs_hw_t *pwp, int level, char *msg, void *arg)
{
uint32_t *mb = arg;
size_t i;
pmcs_prt(pwp, level, NULL, NULL, msg);
for (i = 0; i < (PMCS_QENTRY_SIZE / sizeof (uint32_t)); i += 4) {
pmcs_prt(pwp, level, NULL, NULL,
"Offset %2lu: 0x%08x 0x%08x 0x%08x 0x%08x",
i * sizeof (uint32_t), LE_32(mb[i]),
LE_32(mb[i+1]), LE_32(mb[i+2]), LE_32(mb[i+3]));
}
}
/*
* If phyp == NULL we're being called from the worker thread, in which
* case we need to check all the PHYs. In this case, the softstate lock
* will be held.
* If phyp is non-NULL, just issue the spinup release for the specified PHY
* (which will already be locked).
*/
void
pmcs_spinup_release(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
{
uint32_t *msg;
struct pmcwork *pwrk;
pmcs_phy_t *tphyp;
if (phyp != NULL) {
ASSERT(mutex_owned(&phyp->phy_lock));
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, NULL,
"%s: Issuing spinup release only for PHY %s", __func__,
phyp->path);
mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
if (msg == NULL || (pwrk =
pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) {
mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
SCHEDULE_WORK(pwp, PMCS_WORK_SPINUP_RELEASE);
return;
}
phyp->spinup_hold = 0;
bzero(msg, PMCS_QENTRY_SIZE);
msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
PMCIN_LOCAL_PHY_CONTROL));
msg[1] = LE_32(pwrk->htag);
msg[2] = LE_32((0x10 << 8) | phyp->phynum);
pwrk->dtype = phyp->dtype;
pwrk->state = PMCS_WORK_STATE_ONCHIP;
mutex_exit(&pwrk->lock);
INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
return;
}
ASSERT(mutex_owned(&pwp->lock));
tphyp = pwp->root_phys;
while (tphyp) {
pmcs_lock_phy(tphyp);
if (tphyp->spinup_hold == 0) {
pmcs_unlock_phy(tphyp);
tphyp = tphyp->sibling;
continue;
}
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, NULL,
"%s: Issuing spinup release for PHY %s", __func__,
phyp->path);
mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
if (msg == NULL || (pwrk =
pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) {
pmcs_unlock_phy(tphyp);
mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
SCHEDULE_WORK(pwp, PMCS_WORK_SPINUP_RELEASE);
break;
}
tphyp->spinup_hold = 0;
bzero(msg, PMCS_QENTRY_SIZE);
msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
PMCIN_LOCAL_PHY_CONTROL));
msg[1] = LE_32(pwrk->htag);
msg[2] = LE_32((0x10 << 8) | tphyp->phynum);
pwrk->dtype = phyp->dtype;
pwrk->state = PMCS_WORK_STATE_ONCHIP;
mutex_exit(&pwrk->lock);
INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
pmcs_unlock_phy(tphyp);
tphyp = tphyp->sibling;
}
}
/*
* Abort commands on dead PHYs and deregister them as well as removing
* the associated targets.
*/
static int
pmcs_kill_devices(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
{
pmcs_phy_t *pnext, *pchild;
boolean_t remove_device;
int rval = 0;
while (phyp) {
pmcs_lock_phy(phyp);
pchild = phyp->children;
pnext = phyp->sibling;
pmcs_unlock_phy(phyp);
if (pchild) {
rval = pmcs_kill_devices(pwp, pchild);
if (rval) {
return (rval);
}
}
/*
* pmcs_remove_device requires the softstate lock.
*/
mutex_enter(&pwp->lock);
pmcs_lock_phy(phyp);
if (phyp->dead && phyp->valid_device_id) {
remove_device = B_TRUE;
} else {
remove_device = B_FALSE;
}
if (remove_device) {
pmcs_remove_device(pwp, phyp);
mutex_exit(&pwp->lock);
rval = pmcs_kill_device(pwp, phyp);
if (rval) {
pmcs_unlock_phy(phyp);
return (rval);
}
} else {
mutex_exit(&pwp->lock);
}
pmcs_unlock_phy(phyp);
phyp = pnext;
}
return (rval);
}
/*
* Called with PHY locked
*/
int
pmcs_kill_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
{
int r, result;
uint32_t msg[PMCS_MSG_SIZE], *ptr, status;
struct pmcwork *pwrk;
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, "kill %s device @ %s",
pmcs_get_typename(pptr->dtype), pptr->path);
/*
* There may be an outstanding ABORT_ALL running, which we wouldn't
* know just by checking abort_pending. We can, however, check
* abort_all_start. If it's non-zero, there is one, and we'll just
* sit here and wait for it to complete. If we don't, we'll remove
* the device while there are still commands pending.
*/
if (pptr->abort_all_start) {
while (pptr->abort_all_start) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
"%s: Waiting for outstanding ABORT_ALL on PHY 0x%p",
__func__, (void *)pptr);
cv_wait(&pptr->abort_all_cv, &pptr->phy_lock);
}
} else if (pptr->abort_pending) {
r = pmcs_abort(pwp, pptr, pptr->device_id, 1, 1);
if (r) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
"%s: ABORT_ALL returned non-zero status (%d) for "
"PHY 0x%p", __func__, r, (void *)pptr);
return (r);
}
pptr->abort_pending = 0;
}
if (pptr->valid_device_id == 0) {
return (0);
}
if ((pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr)) == NULL) {
pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__);
return (ENOMEM);
}
pwrk->arg = msg;
pwrk->dtype = pptr->dtype;
msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
PMCIN_DEREGISTER_DEVICE_HANDLE));
msg[1] = LE_32(pwrk->htag);
msg[2] = LE_32(pptr->device_id);
mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
if (ptr == NULL) {
mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
mutex_exit(&pwrk->lock);
pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__);
return (ENOMEM);
}
COPY_MESSAGE(ptr, msg, 3);
pwrk->state = PMCS_WORK_STATE_ONCHIP;
INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
pmcs_unlock_phy(pptr);
WAIT_FOR(pwrk, 250, result);
pmcs_lock_phy(pptr);
pmcs_pwork(pwp, pwrk);
if (result) {
return (ETIMEDOUT);
}
status = LE_32(msg[2]);
if (status != PMCOUT_STATUS_OK) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
"%s: status 0x%x when trying to deregister device %s",
__func__, status, pptr->path);
}
pptr->device_id = PMCS_INVALID_DEVICE_ID;
PHY_CHANGED(pwp, pptr);
RESTART_DISCOVERY(pwp);
pptr->valid_device_id = 0;
return (0);
}
/*
* Acknowledge the SAS h/w events that need acknowledgement.
* This is only needed for first level PHYs.
*/
void
pmcs_ack_events(pmcs_hw_t *pwp)
{
uint32_t msg[PMCS_MSG_SIZE], *ptr;
struct pmcwork *pwrk;
pmcs_phy_t *pptr;
for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
pmcs_lock_phy(pptr);
if (pptr->hw_event_ack == 0) {
pmcs_unlock_phy(pptr);
continue;
}
mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
if ((ptr == NULL) || (pwrk =
pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) {
mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
pmcs_unlock_phy(pptr);
SCHEDULE_WORK(pwp, PMCS_WORK_SAS_HW_ACK);
break;
}
msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
PMCIN_SAW_HW_EVENT_ACK));
msg[1] = LE_32(pwrk->htag);
msg[2] = LE_32(pptr->hw_event_ack);
mutex_exit(&pwrk->lock);
pwrk->dtype = pptr->dtype;
pptr->hw_event_ack = 0;
COPY_MESSAGE(ptr, msg, 3);
INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
pmcs_unlock_phy(pptr);
}
}
/*
* Load DMA
*/
int
pmcs_dma_load(pmcs_hw_t *pwp, pmcs_cmd_t *sp, uint32_t *msg)
{
ddi_dma_cookie_t *sg;
pmcs_dmachunk_t *tc;
pmcs_dmasgl_t *sgl, *prior;
int seg, tsc;
uint64_t sgl_addr;
/*
* If we have no data segments, we're done.
*/
if (CMD2PKT(sp)->pkt_numcookies == 0) {
return (0);
}
/*
* Get the S/G list pointer.
*/
sg = CMD2PKT(sp)->pkt_cookies;
/*
* If we only have one dma segment, we can directly address that
* data within the Inbound message itself.
*/
if (CMD2PKT(sp)->pkt_numcookies == 1) {
msg[12] = LE_32(DWORD0(sg->dmac_laddress));
msg[13] = LE_32(DWORD1(sg->dmac_laddress));
msg[14] = LE_32(sg->dmac_size);
msg[15] = 0;
return (0);
}
/*
* Otherwise, we'll need one or more external S/G list chunks.
* Get the first one and its dma address into the Inbound message.
*/
mutex_enter(&pwp->dma_lock);
tc = pwp->dma_freelist;
if (tc == NULL) {
SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS);
mutex_exit(&pwp->dma_lock);
pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
"%s: out of SG lists", __func__);
return (-1);
}
pwp->dma_freelist = tc->nxt;
mutex_exit(&pwp->dma_lock);
tc->nxt = NULL;
sp->cmd_clist = tc;
sgl = tc->chunks;
(void) memset(tc->chunks, 0, PMCS_SGL_CHUNKSZ);
sgl_addr = tc->addr;
msg[12] = LE_32(DWORD0(sgl_addr));
msg[13] = LE_32(DWORD1(sgl_addr));
msg[14] = 0;
msg[15] = LE_32(PMCS_DMASGL_EXTENSION);
prior = sgl;
tsc = 0;
for (seg = 0; seg < CMD2PKT(sp)->pkt_numcookies; seg++) {
/*
* If the current segment count for this chunk is one less than
* the number s/g lists per chunk and we have more than one seg
* to go, we need another chunk. Get it, and make sure that the
* tail end of the the previous chunk points the new chunk
* (if remembering an offset can be called 'pointing to').
*
* Note that we can store the offset into our command area that
* represents the new chunk in the length field of the part
* that points the PMC chip at the next chunk- the PMC chip
* ignores this field when the EXTENSION bit is set.
*
* This is required for dma unloads later.
*/
if (tsc == (PMCS_SGL_NCHUNKS - 1) &&
seg < (CMD2PKT(sp)->pkt_numcookies - 1)) {
mutex_enter(&pwp->dma_lock);
tc = pwp->dma_freelist;
if (tc == NULL) {
SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS);
mutex_exit(&pwp->dma_lock);
pmcs_dma_unload(pwp, sp);
pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
"%s: out of SG lists", __func__);
return (-1);
}
pwp->dma_freelist = tc->nxt;
tc->nxt = sp->cmd_clist;
mutex_exit(&pwp->dma_lock);
sp->cmd_clist = tc;
(void) memset(tc->chunks, 0, PMCS_SGL_CHUNKSZ);
sgl = tc->chunks;
sgl_addr = tc->addr;
prior[PMCS_SGL_NCHUNKS-1].sglal =
LE_32(DWORD0(sgl_addr));
prior[PMCS_SGL_NCHUNKS-1].sglah =
LE_32(DWORD1(sgl_addr));
prior[PMCS_SGL_NCHUNKS-1].sglen = 0;
prior[PMCS_SGL_NCHUNKS-1].flags =
LE_32(PMCS_DMASGL_EXTENSION);
prior = sgl;
tsc = 0;
}
sgl[tsc].sglal = LE_32(DWORD0(sg->dmac_laddress));
sgl[tsc].sglah = LE_32(DWORD1(sg->dmac_laddress));
sgl[tsc].sglen = LE_32(sg->dmac_size);
sgl[tsc++].flags = 0;
sg++;
}
return (0);
}
/*
* Unload DMA
*/
void
pmcs_dma_unload(pmcs_hw_t *pwp, pmcs_cmd_t *sp)
{
pmcs_dmachunk_t *cp;
mutex_enter(&pwp->dma_lock);
while ((cp = sp->cmd_clist) != NULL) {
sp->cmd_clist = cp->nxt;
cp->nxt = pwp->dma_freelist;
pwp->dma_freelist = cp;
}
mutex_exit(&pwp->dma_lock);
}
/*
* Take a chunk of consistent memory that has just been allocated and inserted
* into the cip indices and prepare it for DMA chunk usage and add it to the
* freelist.
*
* Called with dma_lock locked (except during attach when it's unnecessary)
*/
void
pmcs_idma_chunks(pmcs_hw_t *pwp, pmcs_dmachunk_t *dcp,
pmcs_chunk_t *pchunk, unsigned long lim)
{
unsigned long off, n;
pmcs_dmachunk_t *np = dcp;
pmcs_chunk_t *tmp_chunk;
if (pwp->dma_chunklist == NULL) {
pwp->dma_chunklist = pchunk;
} else {
tmp_chunk = pwp->dma_chunklist;
while (tmp_chunk->next) {
tmp_chunk = tmp_chunk->next;
}
tmp_chunk->next = pchunk;
}
/*
* Install offsets into chunk lists.
*/
for (n = 0, off = 0; off < lim; off += PMCS_SGL_CHUNKSZ, n++) {
np->chunks = (void *)&pchunk->addrp[off];
np->addr = pchunk->dma_addr + off;
np->acc_handle = pchunk->acc_handle;
np->dma_handle = pchunk->dma_handle;
if ((off + PMCS_SGL_CHUNKSZ) < lim) {
np = np->nxt;
}
}
np->nxt = pwp->dma_freelist;
pwp->dma_freelist = dcp;
pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
"added %lu DMA chunks ", n);
}
/*
* Change the value of the interrupt coalescing timer. This is done currently
* only for I/O completions. If we're using the "auto clear" feature, it can
* be turned back on when interrupt coalescing is turned off and must be
* turned off when the coalescing timer is on.
* NOTE: PMCS_MSIX_GENERAL and PMCS_OQ_IODONE are the same value. As long
* as that's true, we don't need to distinguish between them.
*/
void
pmcs_set_intr_coal_timer(pmcs_hw_t *pwp, pmcs_coal_timer_adj_t adj)
{
if (adj == DECREASE_TIMER) {
/* If the timer is already off, nothing to do. */
if (pwp->io_intr_coal.timer_on == B_FALSE) {
return;
}
pwp->io_intr_coal.intr_coal_timer -= PMCS_COAL_TIMER_GRAN;
if (pwp->io_intr_coal.intr_coal_timer == 0) {
/* Disable the timer */
pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_CONTROL, 0);
if (pwp->odb_auto_clear & (1 << PMCS_MSIX_IODONE)) {
pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR,
pwp->odb_auto_clear);
}
pwp->io_intr_coal.timer_on = B_FALSE;
pwp->io_intr_coal.max_io_completions = B_FALSE;
pwp->io_intr_coal.num_intrs = 0;
pwp->io_intr_coal.int_cleared = B_FALSE;
pwp->io_intr_coal.num_io_completions = 0;
DTRACE_PROBE1(pmcs__intr__coalesce__timer__off,
pmcs_io_intr_coal_t *, &pwp->io_intr_coal);
} else {
pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_TIMER,
pwp->io_intr_coal.intr_coal_timer);
}
} else {
/*
* If the timer isn't on yet, do the setup for it now.
*/
if (pwp->io_intr_coal.timer_on == B_FALSE) {
/* If auto clear is being used, turn it off. */
if (pwp->odb_auto_clear & (1 << PMCS_MSIX_IODONE)) {
pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR,
(pwp->odb_auto_clear &
~(1 << PMCS_MSIX_IODONE)));
}
pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_CONTROL,
(1 << PMCS_MSIX_IODONE));
pwp->io_intr_coal.timer_on = B_TRUE;
pwp->io_intr_coal.intr_coal_timer =
PMCS_COAL_TIMER_GRAN;
DTRACE_PROBE1(pmcs__intr__coalesce__timer__on,
pmcs_io_intr_coal_t *, &pwp->io_intr_coal);
} else {
pwp->io_intr_coal.intr_coal_timer +=
PMCS_COAL_TIMER_GRAN;
}
if (pwp->io_intr_coal.intr_coal_timer > PMCS_MAX_COAL_TIMER) {
pwp->io_intr_coal.intr_coal_timer = PMCS_MAX_COAL_TIMER;
}
pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_TIMER,
pwp->io_intr_coal.intr_coal_timer);
}
/*
* Adjust the interrupt threshold based on the current timer value
*/
pwp->io_intr_coal.intr_threshold =
PMCS_INTR_THRESHOLD(PMCS_QUANTUM_TIME_USECS * 1000 /
(pwp->io_intr_coal.intr_latency +
(pwp->io_intr_coal.intr_coal_timer * 1000)));
}
/*
* Register Access functions
*/
uint32_t
pmcs_rd_iqci(pmcs_hw_t *pwp, uint32_t qnum)
{
uint32_t iqci;
if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORKERNEL) !=
DDI_SUCCESS) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"%s: ddi_dma_sync failed?", __func__);
}
iqci = LE_32(
((uint32_t *)((void *)pwp->cip))[IQ_OFFSET(qnum) >> 2]);
return (iqci);
}
uint32_t
pmcs_rd_oqpi(pmcs_hw_t *pwp, uint32_t qnum)
{
uint32_t oqpi;
if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORKERNEL) !=
DDI_SUCCESS) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"%s: ddi_dma_sync failed?", __func__);
}
oqpi = LE_32(
((uint32_t *)((void *)pwp->cip))[OQ_OFFSET(qnum) >> 2]);
return (oqpi);
}
uint32_t
pmcs_rd_gsm_reg(pmcs_hw_t *pwp, uint32_t off)
{
uint32_t rv, newaxil, oldaxil;
newaxil = off & ~GSM_BASE_MASK;
off &= GSM_BASE_MASK;
mutex_enter(&pwp->axil_lock);
oldaxil = ddi_get32(pwp->top_acc_handle,
&pwp->top_regs[PMCS_AXI_TRANS >> 2]);
ddi_put32(pwp->top_acc_handle,
&pwp->top_regs[PMCS_AXI_TRANS >> 2], newaxil);
drv_usecwait(10);
if (ddi_get32(pwp->top_acc_handle,
&pwp->top_regs[PMCS_AXI_TRANS >> 2]) != newaxil) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"AXIL register update failed");
}
rv = ddi_get32(pwp->gsm_acc_handle, &pwp->gsm_regs[off >> 2]);
ddi_put32(pwp->top_acc_handle,
&pwp->top_regs[PMCS_AXI_TRANS >> 2], oldaxil);
drv_usecwait(10);
if (ddi_get32(pwp->top_acc_handle,
&pwp->top_regs[PMCS_AXI_TRANS >> 2]) != oldaxil) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"AXIL register restore failed");
}
mutex_exit(&pwp->axil_lock);
return (rv);
}
void
pmcs_wr_gsm_reg(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
{
uint32_t newaxil, oldaxil;
newaxil = off & ~GSM_BASE_MASK;
off &= GSM_BASE_MASK;
mutex_enter(&pwp->axil_lock);
oldaxil = ddi_get32(pwp->top_acc_handle,
&pwp->top_regs[PMCS_AXI_TRANS >> 2]);
ddi_put32(pwp->top_acc_handle,
&pwp->top_regs[PMCS_AXI_TRANS >> 2], newaxil);
drv_usecwait(10);
if (ddi_get32(pwp->top_acc_handle,
&pwp->top_regs[PMCS_AXI_TRANS >> 2]) != newaxil) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"AXIL register update failed");
}
ddi_put32(pwp->gsm_acc_handle, &pwp->gsm_regs[off >> 2], val);
ddi_put32(pwp->top_acc_handle,
&pwp->top_regs[PMCS_AXI_TRANS >> 2], oldaxil);
drv_usecwait(10);
if (ddi_get32(pwp->top_acc_handle,
&pwp->top_regs[PMCS_AXI_TRANS >> 2]) != oldaxil) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"AXIL register restore failed");
}
mutex_exit(&pwp->axil_lock);
}
uint32_t
pmcs_rd_topunit(pmcs_hw_t *pwp, uint32_t off)
{
switch (off) {
case PMCS_SPC_RESET:
case PMCS_SPC_BOOT_STRAP:
case PMCS_SPC_DEVICE_ID:
case PMCS_DEVICE_REVISION:
off = pmcs_rd_gsm_reg(pwp, off);
break;
default:
off = ddi_get32(pwp->top_acc_handle,
&pwp->top_regs[off >> 2]);
break;
}
return (off);
}
void
pmcs_wr_topunit(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
{
switch (off) {
case PMCS_SPC_RESET:
case PMCS_DEVICE_REVISION:
pmcs_wr_gsm_reg(pwp, off, val);
break;
default:
ddi_put32(pwp->top_acc_handle, &pwp->top_regs[off >> 2], val);
break;
}
}
uint32_t
pmcs_rd_msgunit(pmcs_hw_t *pwp, uint32_t off)
{
return (ddi_get32(pwp->msg_acc_handle, &pwp->msg_regs[off >> 2]));
}
uint32_t
pmcs_rd_mpi_tbl(pmcs_hw_t *pwp, uint32_t off)
{
return (ddi_get32(pwp->mpi_acc_handle,
&pwp->mpi_regs[(pwp->mpi_offset + off) >> 2]));
}
uint32_t
pmcs_rd_gst_tbl(pmcs_hw_t *pwp, uint32_t off)
{
return (ddi_get32(pwp->mpi_acc_handle,
&pwp->mpi_regs[(pwp->mpi_gst_offset + off) >> 2]));
}
uint32_t
pmcs_rd_iqc_tbl(pmcs_hw_t *pwp, uint32_t off)
{
return (ddi_get32(pwp->mpi_acc_handle,
&pwp->mpi_regs[(pwp->mpi_iqc_offset + off) >> 2]));
}
uint32_t
pmcs_rd_oqc_tbl(pmcs_hw_t *pwp, uint32_t off)
{
return (ddi_get32(pwp->mpi_acc_handle,
&pwp->mpi_regs[(pwp->mpi_oqc_offset + off) >> 2]));
}
uint32_t
pmcs_rd_iqpi(pmcs_hw_t *pwp, uint32_t qnum)
{
return (ddi_get32(pwp->mpi_acc_handle,
&pwp->mpi_regs[pwp->iqpi_offset[qnum] >> 2]));
}
uint32_t
pmcs_rd_oqci(pmcs_hw_t *pwp, uint32_t qnum)
{
return (ddi_get32(pwp->mpi_acc_handle,
&pwp->mpi_regs[pwp->oqci_offset[qnum] >> 2]));
}
void
pmcs_wr_msgunit(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
{
ddi_put32(pwp->msg_acc_handle, &pwp->msg_regs[off >> 2], val);
}
void
pmcs_wr_mpi_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
{
ddi_put32(pwp->mpi_acc_handle,
&pwp->mpi_regs[(pwp->mpi_offset + off) >> 2], (val));
}
void
pmcs_wr_gst_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
{
ddi_put32(pwp->mpi_acc_handle,
&pwp->mpi_regs[(pwp->mpi_gst_offset + off) >> 2], val);
}
void
pmcs_wr_iqc_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
{
ddi_put32(pwp->mpi_acc_handle,
&pwp->mpi_regs[(pwp->mpi_iqc_offset + off) >> 2], val);
}
void
pmcs_wr_oqc_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
{
ddi_put32(pwp->mpi_acc_handle,
&pwp->mpi_regs[(pwp->mpi_oqc_offset + off) >> 2], val);
}
void
pmcs_wr_iqci(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
{
((uint32_t *)((void *)pwp->cip))[IQ_OFFSET(qnum) >> 2] = val;
if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORDEV) !=
DDI_SUCCESS) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"%s: ddi_dma_sync failed?", __func__);
}
}
void
pmcs_wr_iqpi(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
{
ddi_put32(pwp->mpi_acc_handle,
&pwp->mpi_regs[pwp->iqpi_offset[qnum] >> 2], val);
}
void
pmcs_wr_oqci(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
{
ddi_put32(pwp->mpi_acc_handle,
&pwp->mpi_regs[pwp->oqci_offset[qnum] >> 2], val);
}
void
pmcs_wr_oqpi(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
{
((uint32_t *)((void *)pwp->cip))[OQ_OFFSET(qnum) >> 2] = val;
if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORDEV) !=
DDI_SUCCESS) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"%s: ddi_dma_sync failed?", __func__);
}
}
/*
* Check the status value of an outbound IOMB and report anything bad
*/
void
pmcs_check_iomb_status(pmcs_hw_t *pwp, uint32_t *iomb)
{
uint16_t opcode;
int offset;
if (iomb == NULL) {
return;
}
opcode = LE_32(iomb[0]) & 0xfff;
switch (opcode) {
/*
* The following have no status field, so ignore them
*/
case PMCOUT_ECHO:
case PMCOUT_SAS_HW_EVENT:
case PMCOUT_GET_DEVICE_HANDLE:
case PMCOUT_SATA_EVENT:
case PMCOUT_SSP_EVENT:
case PMCOUT_DEVICE_HANDLE_ARRIVED:
case PMCOUT_SMP_REQUEST_RECEIVED:
case PMCOUT_GPIO:
case PMCOUT_GPIO_EVENT:
case PMCOUT_GET_TIME_STAMP:
case PMCOUT_SKIP_ENTRIES:
case PMCOUT_GET_NVMD_DATA: /* Actually lower 16 bits of word 3 */
case PMCOUT_SET_NVMD_DATA: /* but ignore - we don't use these */
case PMCOUT_DEVICE_HANDLE_REMOVED:
case PMCOUT_SSP_REQUEST_RECEIVED:
return;
case PMCOUT_GENERAL_EVENT:
offset = 1;
break;
case PMCOUT_SSP_COMPLETION:
case PMCOUT_SMP_COMPLETION:
case PMCOUT_DEVICE_REGISTRATION:
case PMCOUT_DEREGISTER_DEVICE_HANDLE:
case PMCOUT_SATA_COMPLETION:
case PMCOUT_DEVICE_INFO:
case PMCOUT_FW_FLASH_UPDATE:
case PMCOUT_SSP_ABORT:
case PMCOUT_SATA_ABORT:
case PMCOUT_SAS_DIAG_MODE_START_END:
case PMCOUT_SAS_HW_EVENT_ACK_ACK:
case PMCOUT_SMP_ABORT:
case PMCOUT_SET_DEVICE_STATE:
case PMCOUT_GET_DEVICE_STATE:
case PMCOUT_SET_DEVICE_INFO:
offset = 2;
break;
case PMCOUT_LOCAL_PHY_CONTROL:
case PMCOUT_SAS_DIAG_EXECUTE:
case PMCOUT_PORT_CONTROL:
offset = 3;
break;
case PMCOUT_GET_INFO:
case PMCOUT_GET_VPD:
case PMCOUT_SAS_ASSISTED_DISCOVERY_EVENT:
case PMCOUT_SATA_ASSISTED_DISCOVERY_EVENT:
case PMCOUT_SET_VPD:
case PMCOUT_TWI:
pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
"Got response for deprecated opcode", iomb);
return;
default:
pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
"Got response for unknown opcode", iomb);
return;
}
if (LE_32(iomb[offset]) != PMCOUT_STATUS_OK) {
pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
"bad status on TAG_TYPE_NONE command", iomb);
}
}
/*
* Called with statlock held
*/
void
pmcs_clear_xp(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
{
_NOTE(ARGUNUSED(pwp));
ASSERT(mutex_owned(&xp->statlock));
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, "%s: Device 0x%p is gone.",
__func__, (void *)xp);
/*
* Clear the dip now. This keeps pmcs_remove_device from attempting
* to call us on the same device while we're still flushing queues.
* The only side effect is we can no longer update SM-HBA properties,
* but this device is going away anyway, so no matter.
*/
xp->dip = NULL;
xp->sd = NULL;
xp->smpd = NULL;
xp->special_running = 0;
xp->recovering = 0;
xp->recover_wait = 0;
xp->draining = 0;
xp->new = 0;
xp->assigned = 0;
xp->dev_state = 0;
xp->tagmap = 0;
xp->dev_gone = 1;
xp->event_recovery = 0;
xp->dtype = NOTHING;
xp->wq_recovery_tail = NULL;
/* Don't clear xp->phy */
/* Don't clear xp->actv_cnt */
/* Don't clear xp->actv_pkts */
/*
* Flush all target queues
*/
pmcs_flush_target_queues(pwp, xp, PMCS_TGT_ALL_QUEUES);
}
static int
pmcs_smp_function_result(pmcs_hw_t *pwp, smp_response_frame_t *srf)
{
int result = srf->srf_result;
switch (result) {
case SMP_RES_UNKNOWN_FUNCTION:
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"%s: SMP DISCOVER Response "
"Function Result: Unknown SMP Function(0x%x)",
__func__, result);
break;
case SMP_RES_FUNCTION_FAILED:
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"%s: SMP DISCOVER Response "
"Function Result: SMP Function Failed(0x%x)",
__func__, result);
break;
case SMP_RES_INVALID_REQUEST_FRAME_LENGTH:
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"%s: SMP DISCOVER Response "
"Function Result: Invalid Request Frame Length(0x%x)",
__func__, result);
break;
case SMP_RES_INCOMPLETE_DESCRIPTOR_LIST:
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"%s: SMP DISCOVER Response "
"Function Result: Incomplete Descriptor List(0x%x)",
__func__, result);
break;
case SMP_RES_PHY_DOES_NOT_EXIST:
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"%s: SMP DISCOVER Response "
"Function Result: PHY does not exist(0x%x)",
__func__, result);
break;
case SMP_RES_PHY_VACANT:
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"%s: SMP DISCOVER Response "
"Function Result: PHY Vacant(0x%x)",
__func__, result);
break;
default:
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"%s: SMP DISCOVER Response "
"Function Result: (0x%x)",
__func__, result);
break;
}
return (result);
}
/*
* Do all the repetitive stuff necessary to setup for DMA
*
* pwp: Used for dip
* dma_attr: ddi_dma_attr_t to use for the mapping
* acch: ddi_acc_handle_t to use for the mapping
* dmah: ddi_dma_handle_t to use
* length: Amount of memory for mapping
* kvap: Pointer filled in with kernel virtual address on successful return
* dma_addr: Pointer filled in with DMA address on successful return
*/
boolean_t
pmcs_dma_setup(pmcs_hw_t *pwp, ddi_dma_attr_t *dma_attr, ddi_acc_handle_t *acch,
ddi_dma_handle_t *dmah, size_t length, caddr_t *kvap, uint64_t *dma_addr)
{
dev_info_t *dip = pwp->dip;
ddi_dma_cookie_t cookie;
size_t real_length;
uint_t ddma_flag = DDI_DMA_CONSISTENT;
uint_t ddabh_flag = DDI_DMA_CONSISTENT | DDI_DMA_RDWR;
uint_t cookie_cnt;
ddi_device_acc_attr_t mattr = {
DDI_DEVICE_ATTR_V0,
DDI_NEVERSWAP_ACC,
DDI_STRICTORDER_ACC,
DDI_DEFAULT_ACC
};
*acch = NULL;
*dmah = NULL;
if (ddi_dma_alloc_handle(dip, dma_attr, DDI_DMA_SLEEP, NULL, dmah) !=
DDI_SUCCESS) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"Failed to allocate DMA handle");
return (B_FALSE);
}
if (ddi_dma_mem_alloc(*dmah, length, &mattr, ddma_flag, DDI_DMA_SLEEP,
NULL, kvap, &real_length, acch) != DDI_SUCCESS) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"Failed to allocate DMA mem");
ddi_dma_free_handle(dmah);
*dmah = NULL;
return (B_FALSE);
}
if (ddi_dma_addr_bind_handle(*dmah, NULL, *kvap, real_length,
ddabh_flag, DDI_DMA_SLEEP, NULL, &cookie, &cookie_cnt)
!= DDI_DMA_MAPPED) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "Failed to bind DMA");
ddi_dma_free_handle(dmah);
ddi_dma_mem_free(acch);
*dmah = NULL;
*acch = NULL;
return (B_FALSE);
}
if (cookie_cnt != 1) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "Multiple cookies");
if (ddi_dma_unbind_handle(*dmah) != DDI_SUCCESS) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "Condition "
"failed at %s():%d", __func__, __LINE__);
}
ddi_dma_free_handle(dmah);
ddi_dma_mem_free(acch);
*dmah = NULL;
*acch = NULL;
return (B_FALSE);
}
*dma_addr = cookie.dmac_laddress;
return (B_TRUE);
}
/*
* Flush requested queues for a particular target. Called with statlock held
*/
void
pmcs_flush_target_queues(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt, uint8_t queues)
{
pmcs_cmd_t *sp;
pmcwork_t *pwrk;
ASSERT(pwp != NULL);
ASSERT(tgt != NULL);
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, tgt,
"%s: Flushing queues (%d) for target 0x%p", __func__,
queues, (void *)tgt);
/*
* Commands on the wait queue (or the special queue below) don't have
* work structures associated with them.
*/
if (queues & PMCS_TGT_WAIT_QUEUE) {
mutex_enter(&tgt->wqlock);
while ((sp = STAILQ_FIRST(&tgt->wq)) != NULL) {
STAILQ_REMOVE(&tgt->wq, sp, pmcs_cmd, cmd_next);
pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, tgt,
"%s: Removing cmd 0x%p from wq for target 0x%p",
__func__, (void *)sp, (void *)tgt);
CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
mutex_exit(&tgt->wqlock);
pmcs_dma_unload(pwp, sp);
mutex_enter(&pwp->cq_lock);
STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
mutex_exit(&pwp->cq_lock);
mutex_enter(&tgt->wqlock);
}
mutex_exit(&tgt->wqlock);
}
/*
* Commands on the active queue will have work structures associated
* with them.
*/
if (queues & PMCS_TGT_ACTIVE_QUEUE) {
mutex_enter(&tgt->aqlock);
while ((sp = STAILQ_FIRST(&tgt->aq)) != NULL) {
STAILQ_REMOVE(&tgt->aq, sp, pmcs_cmd, cmd_next);
pwrk = pmcs_tag2wp(pwp, sp->cmd_tag);
mutex_exit(&tgt->aqlock);
mutex_exit(&tgt->statlock);
/*
* If we found a work structure, mark it as dead
* and complete it
*/
if (pwrk != NULL) {
pwrk->dead = 1;
CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
pmcs_complete_work_impl(pwp, pwrk, NULL, 0);
}
pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, tgt,
"%s: Removing cmd 0x%p from aq for target 0x%p",
__func__, (void *)sp, (void *)tgt);
pmcs_dma_unload(pwp, sp);
mutex_enter(&pwp->cq_lock);
STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
mutex_exit(&pwp->cq_lock);
mutex_enter(&tgt->aqlock);
mutex_enter(&tgt->statlock);
}
mutex_exit(&tgt->aqlock);
}
if (queues & PMCS_TGT_SPECIAL_QUEUE) {
while ((sp = STAILQ_FIRST(&tgt->sq)) != NULL) {
STAILQ_REMOVE(&tgt->sq, sp, pmcs_cmd, cmd_next);
pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, tgt,
"%s: Removing cmd 0x%p from sq for target 0x%p",
__func__, (void *)sp, (void *)tgt);
CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
pmcs_dma_unload(pwp, sp);
mutex_enter(&pwp->cq_lock);
STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
mutex_exit(&pwp->cq_lock);
}
}
}
void
pmcs_complete_work_impl(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *iomb,
size_t amt)
{
switch (PMCS_TAG_TYPE(pwrk->htag)) {
case PMCS_TAG_TYPE_CBACK:
{
pmcs_cb_t callback = (pmcs_cb_t)pwrk->ptr;
(*callback)(pwp, pwrk, iomb);
break;
}
case PMCS_TAG_TYPE_WAIT:
if (pwrk->arg && iomb && amt) {
(void) memcpy(pwrk->arg, iomb, amt);
}
cv_signal(&pwrk->sleep_cv);
mutex_exit(&pwrk->lock);
break;
case PMCS_TAG_TYPE_NONE:
#ifdef DEBUG
pmcs_check_iomb_status(pwp, iomb);
#endif
pmcs_pwork(pwp, pwrk);
break;
default:
/*
* We will leak a structure here if we don't know
* what happened
*/
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"%s: Unknown PMCS_TAG_TYPE (%x)",
__func__, PMCS_TAG_TYPE(pwrk->htag));
break;
}
}
/*
* Determine if iport still has targets. During detach(9E), if SCSA is
* successfull in its guarantee of tran_tgt_free(9E) before detach(9E),
* this should always return B_FALSE.
*/
boolean_t
pmcs_iport_has_targets(pmcs_hw_t *pwp, pmcs_iport_t *iport)
{
pmcs_xscsi_t *xp;
int i;
mutex_enter(&pwp->lock);
if (!pwp->targets || !pwp->max_dev) {
mutex_exit(&pwp->lock);
return (B_FALSE);
}
for (i = 0; i < pwp->max_dev; i++) {
xp = pwp->targets[i];
if ((xp == NULL) || (xp->phy == NULL) ||
(xp->phy->iport != iport)) {
continue;
}
mutex_exit(&pwp->lock);
return (B_TRUE);
}
mutex_exit(&pwp->lock);
return (B_FALSE);
}
/*
* Called with softstate lock held
*/
void
pmcs_destroy_target(pmcs_xscsi_t *target)
{
pmcs_hw_t *pwp = target->pwp;
pmcs_iport_t *iport;
ASSERT(pwp);
ASSERT(mutex_owned(&pwp->lock));
if (!target->ua) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, target,
"%s: target %p iport address is null",
__func__, (void *)target);
}
iport = pmcs_get_iport_by_ua(pwp, target->ua);
if (iport == NULL) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, target,
"%s: no iport associated with tgt(0x%p)",
__func__, (void *)target);
return;
}
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, target,
"%s: free target %p", __func__, (void *)target);
if (target->ua) {
strfree(target->ua);
}
mutex_destroy(&target->wqlock);
mutex_destroy(&target->aqlock);
mutex_destroy(&target->statlock);
cv_destroy(&target->reset_cv);
cv_destroy(&target->abort_cv);
ddi_soft_state_bystr_fini(&target->lun_sstate);
ddi_soft_state_bystr_free(iport->tgt_sstate, target->unit_address);
pmcs_rele_iport(iport);
}
/*
* pmcs_lock_phy_impl
*
* This function is what does the actual work for pmcs_lock_phy. It will
* lock all PHYs from phyp down in a top-down fashion.
*
* Locking notes:
* 1. level starts from 0 for the PHY ("parent") that's passed in. It is
* not a reflection of the actual level of the PHY in the SAS topology.
* 2. If parent is an expander, then parent is locked along with all its
* descendents.
* 3. Expander subsidiary PHYs at level 0 are not locked. It is the
* responsibility of the caller to individually lock expander subsidiary PHYs
* at level 0 if necessary.
* 4. Siblings at level 0 are not traversed due to the possibility that we're
* locking a PHY on the dead list. The siblings could be pointing to invalid
* PHYs. We don't lock siblings at level 0 anyway.
*/
static void
pmcs_lock_phy_impl(pmcs_phy_t *phyp, int level)
{
pmcs_phy_t *tphyp;
ASSERT((phyp->dtype == SAS) || (phyp->dtype == SATA) ||
(phyp->dtype == EXPANDER) || (phyp->dtype == NOTHING));
/*
* Start walking the PHYs.
*/
tphyp = phyp;
while (tphyp) {
/*
* If we're at the top level, only lock ourselves. For anything
* at level > 0, traverse children while locking everything.
*/
if ((level > 0) || (tphyp == phyp)) {
pmcs_prt(tphyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, tphyp,
NULL, "%s: PHY 0x%p parent 0x%p path %s lvl %d",
__func__, (void *)tphyp, (void *)tphyp->parent,
tphyp->path, level);
mutex_enter(&tphyp->phy_lock);
if (tphyp->children) {
pmcs_lock_phy_impl(tphyp->children, level + 1);
}
}
if (level == 0) {
return;
}
tphyp = tphyp->sibling;
}
}
/*
* pmcs_lock_phy
*
* This function is responsible for locking a PHY and all its descendents
*/
void
pmcs_lock_phy(pmcs_phy_t *phyp)
{
#ifdef DEBUG
char *callername = NULL;
ulong_t off;
ASSERT(phyp != NULL);
callername = modgetsymname((uintptr_t)caller(), &off);
if (callername == NULL) {
pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
"%s: PHY 0x%p path %s caller: unknown", __func__,
(void *)phyp, phyp->path);
} else {
pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
"%s: PHY 0x%p path %s caller: %s+%lx", __func__,
(void *)phyp, phyp->path, callername, off);
}
#else
pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
"%s: PHY 0x%p path %s", __func__, (void *)phyp, phyp->path);
#endif
pmcs_lock_phy_impl(phyp, 0);
}
/*
* pmcs_unlock_phy_impl
*
* Unlock all PHYs from phyp down in a bottom-up fashion.
*/
static void
pmcs_unlock_phy_impl(pmcs_phy_t *phyp, int level)
{
pmcs_phy_t *phy_next;
ASSERT((phyp->dtype == SAS) || (phyp->dtype == SATA) ||
(phyp->dtype == EXPANDER) || (phyp->dtype == NOTHING));
/*
* Recurse down to the bottom PHYs
*/
if (level == 0) {
if (phyp->children) {
pmcs_unlock_phy_impl(phyp->children, level + 1);
}
} else {
phy_next = phyp;
while (phy_next) {
if (phy_next->children) {
pmcs_unlock_phy_impl(phy_next->children,
level + 1);
}
phy_next = phy_next->sibling;
}
}
/*
* Iterate through PHYs unlocking all at level > 0 as well the top PHY
*/
phy_next = phyp;
while (phy_next) {
if ((level > 0) || (phy_next == phyp)) {
pmcs_prt(phy_next->pwp, PMCS_PRT_DEBUG_PHY_LOCKING,
phy_next, NULL,
"%s: PHY 0x%p parent 0x%p path %s lvl %d",
__func__, (void *)phy_next,
(void *)phy_next->parent, phy_next->path, level);
mutex_exit(&phy_next->phy_lock);
}
if (level == 0) {
return;
}
phy_next = phy_next->sibling;
}
}
/*
* pmcs_unlock_phy
*
* Unlock a PHY and all its descendents
*/
void
pmcs_unlock_phy(pmcs_phy_t *phyp)
{
#ifdef DEBUG
char *callername = NULL;
ulong_t off;
ASSERT(phyp != NULL);
callername = modgetsymname((uintptr_t)caller(), &off);
if (callername == NULL) {
pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
"%s: PHY 0x%p path %s caller: unknown", __func__,
(void *)phyp, phyp->path);
} else {
pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
"%s: PHY 0x%p path %s caller: %s+%lx", __func__,
(void *)phyp, phyp->path, callername, off);
}
#else
pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
"%s: PHY 0x%p path %s", __func__, (void *)phyp, phyp->path);
#endif
pmcs_unlock_phy_impl(phyp, 0);
}
/*
* pmcs_get_root_phy
*
* For a given phy pointer return its root phy.
* The caller must be holding the lock on every PHY from phyp up to the root.
*/
pmcs_phy_t *
pmcs_get_root_phy(pmcs_phy_t *phyp)
{
ASSERT(phyp);
while (phyp) {
if (IS_ROOT_PHY(phyp)) {
break;
}
phyp = phyp->parent;
}
return (phyp);
}
/*
* pmcs_free_dma_chunklist
*
* Free DMA S/G chunk list
*/
void
pmcs_free_dma_chunklist(pmcs_hw_t *pwp)
{
pmcs_chunk_t *pchunk;
while (pwp->dma_chunklist) {
pchunk = pwp->dma_chunklist;
pwp->dma_chunklist = pwp->dma_chunklist->next;
if (pchunk->dma_handle) {
if (ddi_dma_unbind_handle(pchunk->dma_handle) !=
DDI_SUCCESS) {
pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
"Condition failed at %s():%d",
__func__, __LINE__);
}
ddi_dma_free_handle(&pchunk->dma_handle);
ddi_dma_mem_free(&pchunk->acc_handle);
}
kmem_free(pchunk, sizeof (pmcs_chunk_t));
}
}
/*ARGSUSED2*/
int
pmcs_phy_constructor(void *buf, void *arg, int kmflags)
{
pmcs_hw_t *pwp = (pmcs_hw_t *)arg;
pmcs_phy_t *phyp = (pmcs_phy_t *)buf;
mutex_init(&phyp->phy_lock, NULL, MUTEX_DRIVER,
DDI_INTR_PRI(pwp->intr_pri));
cv_init(&phyp->abort_all_cv, NULL, CV_DRIVER, NULL);
return (0);
}
/*ARGSUSED1*/
void
pmcs_phy_destructor(void *buf, void *arg)
{
pmcs_phy_t *phyp = (pmcs_phy_t *)buf;
cv_destroy(&phyp->abort_all_cv);
mutex_destroy(&phyp->phy_lock);
}
/*
* Free all PHYs from the kmem_cache starting at phyp as well as everything
* on the dead_phys list.
*
* NOTE: This function does not free root PHYs as they are not allocated
* from the kmem_cache.
*
* No PHY locks are acquired as this should only be called during DDI_DETACH
* or soft reset (while pmcs interrupts are disabled).
*/
void
pmcs_free_all_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
{
pmcs_phy_t *tphyp, *nphyp;
if (phyp == NULL) {
return;
}
tphyp = phyp;
while (tphyp) {
nphyp = tphyp->sibling;
if (tphyp->children) {
pmcs_free_all_phys(pwp, tphyp->children);
tphyp->children = NULL;
}
if (!IS_ROOT_PHY(tphyp)) {
kmem_cache_free(pwp->phy_cache, tphyp);
}
tphyp = nphyp;
}
tphyp = pwp->dead_phys;
while (tphyp) {
nphyp = tphyp->sibling;
kmem_cache_free(pwp->phy_cache, tphyp);
tphyp = nphyp;
}
pwp->dead_phys = NULL;
}
/*
* Free a list of PHYs linked together by the sibling pointer back to the
* kmem cache from whence they came. This function does not recurse, so the
* caller must ensure there are no children.
*/
void
pmcs_free_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
{
pmcs_phy_t *next_phy;
while (phyp) {
next_phy = phyp->sibling;
ASSERT(!mutex_owned(&phyp->phy_lock));
kmem_cache_free(pwp->phy_cache, phyp);
phyp = next_phy;
}
}
/*
* Make a copy of an existing PHY structure. This is used primarily in
* discovery to compare the contents of an existing PHY with what gets
* reported back by an expander.
*
* This function must not be called from any context where sleeping is
* not possible.
*
* The new PHY is returned unlocked.
*/
static pmcs_phy_t *
pmcs_clone_phy(pmcs_phy_t *orig_phy)
{
pmcs_phy_t *local;
local = kmem_cache_alloc(orig_phy->pwp->phy_cache, KM_SLEEP);
/*
* Go ahead and just copy everything...
*/
*local = *orig_phy;
/*
* But the following must be set appropriately for this copy
*/
local->sibling = NULL;
local->children = NULL;
mutex_init(&local->phy_lock, NULL, MUTEX_DRIVER,
DDI_INTR_PRI(orig_phy->pwp->intr_pri));
return (local);
}
int
pmcs_check_acc_handle(ddi_acc_handle_t handle)
{
ddi_fm_error_t de;
if (handle == NULL) {
return (DDI_FAILURE);
}
ddi_fm_acc_err_get(handle, &de, DDI_FME_VER0);
return (de.fme_status);
}
int
pmcs_check_dma_handle(ddi_dma_handle_t handle)
{
ddi_fm_error_t de;
if (handle == NULL) {
return (DDI_FAILURE);
}
ddi_fm_dma_err_get(handle, &de, DDI_FME_VER0);
return (de.fme_status);
}
void
pmcs_fm_ereport(pmcs_hw_t *pwp, char *detail)
{
uint64_t ena;
char buf[FM_MAX_CLASS];
(void) snprintf(buf, FM_MAX_CLASS, "%s.%s", DDI_FM_DEVICE, detail);
ena = fm_ena_generate(0, FM_ENA_FMT1);
if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities)) {
ddi_fm_ereport_post(pwp->dip, buf, ena, DDI_NOSLEEP,
FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, NULL);
}
}
int
pmcs_check_acc_dma_handle(pmcs_hw_t *pwp)
{
pmcs_chunk_t *pchunk;
int i;
/* check all acc & dma handles allocated in attach */
if ((pmcs_check_acc_handle(pwp->pci_acc_handle) != DDI_SUCCESS) ||
(pmcs_check_acc_handle(pwp->msg_acc_handle) != DDI_SUCCESS) ||
(pmcs_check_acc_handle(pwp->top_acc_handle) != DDI_SUCCESS) ||
(pmcs_check_acc_handle(pwp->mpi_acc_handle) != DDI_SUCCESS) ||
(pmcs_check_acc_handle(pwp->gsm_acc_handle) != DDI_SUCCESS)) {
goto check_failed;
}
for (i = 0; i < PMCS_NIQ; i++) {
if ((pmcs_check_dma_handle(
pwp->iqp_handles[i]) != DDI_SUCCESS) ||
(pmcs_check_acc_handle(
pwp->iqp_acchdls[i]) != DDI_SUCCESS)) {
goto check_failed;
}
}
for (i = 0; i < PMCS_NOQ; i++) {
if ((pmcs_check_dma_handle(
pwp->oqp_handles[i]) != DDI_SUCCESS) ||
(pmcs_check_acc_handle(
pwp->oqp_acchdls[i]) != DDI_SUCCESS)) {
goto check_failed;
}
}
if ((pmcs_check_dma_handle(pwp->cip_handles) != DDI_SUCCESS) ||
(pmcs_check_acc_handle(pwp->cip_acchdls) != DDI_SUCCESS)) {
goto check_failed;
}
if (pwp->fwlog &&
((pmcs_check_dma_handle(pwp->fwlog_hndl) != DDI_SUCCESS) ||
(pmcs_check_acc_handle(pwp->fwlog_acchdl) != DDI_SUCCESS))) {
goto check_failed;
}
if (pwp->regdump_hndl && pwp->regdump_acchdl &&
((pmcs_check_dma_handle(pwp->regdump_hndl) != DDI_SUCCESS) ||
(pmcs_check_acc_handle(pwp->regdump_acchdl)
!= DDI_SUCCESS))) {
goto check_failed;
}
pchunk = pwp->dma_chunklist;
while (pchunk) {
if ((pmcs_check_acc_handle(pchunk->acc_handle)
!= DDI_SUCCESS) ||
(pmcs_check_dma_handle(pchunk->dma_handle)
!= DDI_SUCCESS)) {
goto check_failed;
}
pchunk = pchunk->next;
}
return (0);
check_failed:
return (1);
}
/*
* pmcs_handle_dead_phys
*
* If the PHY has no outstanding work associated with it, remove it from
* the dead PHY list and free it.
*
* If pwp->ds_err_recovering or pwp->configuring is set, don't run.
* This keeps routines that need to submit work to the chip from having to
* hold PHY locks to ensure that PHYs don't disappear while they do their work.
*/
void
pmcs_handle_dead_phys(pmcs_hw_t *pwp)
{
pmcs_phy_t *phyp, *nphyp, *pphyp;
mutex_enter(&pwp->lock);
mutex_enter(&pwp->config_lock);
if (pwp->configuring | pwp->ds_err_recovering) {
mutex_exit(&pwp->config_lock);
mutex_exit(&pwp->lock);
return;
}
/*
* Check every PHY in the dead PHY list
*/
mutex_enter(&pwp->dead_phylist_lock);
phyp = pwp->dead_phys;
pphyp = NULL; /* Set previous PHY to NULL */
while (phyp != NULL) {
pmcs_lock_phy(phyp);
ASSERT(phyp->dead);
nphyp = phyp->dead_next;
/*
* Check for outstanding work
*/
if (phyp->ref_count > 0) {
pmcs_unlock_phy(phyp);
pphyp = phyp; /* This PHY becomes "previous" */
} else if (phyp->target) {
pmcs_unlock_phy(phyp);
pmcs_prt(pwp, PMCS_PRT_DEBUG1, phyp, phyp->target,
"%s: Not freeing PHY 0x%p: target 0x%p is not free",
__func__, (void *)phyp, (void *)phyp->target);
pphyp = phyp;
} else {
/*
* No outstanding work or target references. Remove it
* from the list and free it
*/
pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, phyp->target,
"%s: Freeing inactive dead PHY 0x%p @ %s "
"target = 0x%p", __func__, (void *)phyp,
phyp->path, (void *)phyp->target);
/*
* If pphyp is NULL, then phyp was the head of the list,
* so just reset the head to nphyp. Otherwise, the
* previous PHY will now point to nphyp (the next PHY)
*/
if (pphyp == NULL) {
pwp->dead_phys = nphyp;
} else {
pphyp->dead_next = nphyp;
}
/*
* If the target still points to this PHY, remove
* that linkage now.
*/
if (phyp->target) {
mutex_enter(&phyp->target->statlock);
if (phyp->target->phy == phyp) {
phyp->target->phy = NULL;
}
mutex_exit(&phyp->target->statlock);
}
pmcs_unlock_phy(phyp);
kmem_cache_free(pwp->phy_cache, phyp);
}
phyp = nphyp;
}
mutex_exit(&pwp->dead_phylist_lock);
mutex_exit(&pwp->config_lock);
mutex_exit(&pwp->lock);
}
void
pmcs_inc_phy_ref_count(pmcs_phy_t *phyp)
{
atomic_inc_32(&phyp->ref_count);
}
void
pmcs_dec_phy_ref_count(pmcs_phy_t *phyp)
{
ASSERT(phyp->ref_count != 0);
atomic_dec_32(&phyp->ref_count);
}
/*
* pmcs_reap_dead_phy
*
* This function is called from pmcs_new_tport when we have a PHY
* without a target pointer. It's possible in that case that this PHY
* may have a "brother" on the dead_phys list. That is, it may be the same as
* this one but with a different root PHY number (e.g. pp05 vs. pp04). If
* that's the case, update the dead PHY and this new PHY. If that's not the
* case, we should get a tran_tgt_init on this after it's reported to SCSA.
*
* Called with PHY locked.
*/
static void
pmcs_reap_dead_phy(pmcs_phy_t *phyp)
{
pmcs_hw_t *pwp = phyp->pwp;
pmcs_phy_t *ctmp;
ASSERT(mutex_owned(&phyp->phy_lock));
/*
* Check the dead PHYs list
*/
mutex_enter(&pwp->dead_phylist_lock);
ctmp = pwp->dead_phys;
while (ctmp) {
if ((ctmp->iport != phyp->iport) ||
(memcmp((void *)&ctmp->sas_address[0],
(void *)&phyp->sas_address[0], 8))) {
ctmp = ctmp->dead_next;
continue;
}
/*
* Same SAS address on same iport. Now check to see if
* the PHY path is the same with the possible exception
* of the root PHY number.
* The "5" is the string length of "pp00."
*/
if ((strnlen(phyp->path, 5) >= 5) &&
(strnlen(ctmp->path, 5) >= 5)) {
if (memcmp((void *)&phyp->path[5],
(void *)&ctmp->path[5],
strnlen(phyp->path, 32) - 5) == 0) {
break;
}
}
ctmp = ctmp->dead_next;
}
mutex_exit(&pwp->dead_phylist_lock);
/*
* Found a match. Remove the target linkage and drop the
* ref count on the old PHY. Then, increment the ref count
* on the new PHY to compensate.
*/
if (ctmp) {
pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
"%s: Found match in dead PHY list for new PHY %s",
__func__, phyp->path);
if (ctmp->target) {
/*
* If there is a pointer to the target in the dead
* PHY, and that PHY's ref_count drops to 0, we can
* clear the target linkage now. If the PHY's
* ref_count is > 1, then there may be multiple
* LUNs still remaining, so leave the linkage.
*/
pmcs_inc_phy_ref_count(phyp);
pmcs_dec_phy_ref_count(ctmp);
phyp->target = ctmp->target;
/*
* Update the target's linkage as well
*/
mutex_enter(&phyp->target->statlock);
phyp->target->phy = phyp;
phyp->target->dtype = phyp->dtype;
mutex_exit(&phyp->target->statlock);
if (ctmp->ref_count == 0) {
ctmp->target = NULL;
}
}
}
}
/*
* Called with iport lock held
*/
void
pmcs_add_phy_to_iport(pmcs_iport_t *iport, pmcs_phy_t *phyp)
{
ASSERT(mutex_owned(&iport->lock));
ASSERT(phyp);
ASSERT(!list_link_active(&phyp->list_node));
iport->nphy++;
list_insert_tail(&iport->phys, phyp);
pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
&iport->nphy);
mutex_enter(&iport->refcnt_lock);
iport->refcnt++;
mutex_exit(&iport->refcnt_lock);
}
/*
* Called with the iport lock held
*/
void
pmcs_remove_phy_from_iport(pmcs_iport_t *iport, pmcs_phy_t *phyp)
{
pmcs_phy_t *pptr, *next_pptr;
ASSERT(mutex_owned(&iport->lock));
/*
* If phyp is NULL, remove all PHYs from the iport
*/
if (phyp == NULL) {
for (pptr = list_head(&iport->phys); pptr != NULL;
pptr = next_pptr) {
next_pptr = list_next(&iport->phys, pptr);
mutex_enter(&pptr->phy_lock);
pptr->iport = NULL;
mutex_exit(&pptr->phy_lock);
pmcs_rele_iport(iport);
list_remove(&iport->phys, pptr);
}
iport->nphy = 0;
return;
}
ASSERT(phyp);
ASSERT(iport->nphy > 0);
ASSERT(list_link_active(&phyp->list_node));
iport->nphy--;
list_remove(&iport->phys, phyp);
pmcs_update_phy_pm_props(phyp, phyp->att_port_pm_tmp,
phyp->tgt_port_pm_tmp, B_FALSE);
pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
&iport->nphy);
pmcs_rele_iport(iport);
}
/*
* This function checks to see if the target pointed to by phyp is still
* correct. This is done by comparing the target's unit address with the
* SAS address in phyp.
*
* Called with PHY locked and target statlock held
*/
static boolean_t
pmcs_phy_target_match(pmcs_phy_t *phyp)
{
uint64_t wwn;
char unit_address[PMCS_MAX_UA_SIZE];
boolean_t rval = B_FALSE;
ASSERT(phyp);
ASSERT(phyp->target);
ASSERT(mutex_owned(&phyp->phy_lock));
ASSERT(mutex_owned(&phyp->target->statlock));
wwn = pmcs_barray2wwn(phyp->sas_address);
(void) scsi_wwn_to_wwnstr(wwn, 1, unit_address);
if (memcmp((void *)unit_address, (void *)phyp->target->unit_address,
strnlen(phyp->target->unit_address, PMCS_MAX_UA_SIZE)) == 0) {
rval = B_TRUE;
}
return (rval);
}
/*
* Commands used to serialize SMP requests.
*
* The SPC only allows 2 SMP commands per SMP target: 1 cmd pending and 1 cmd
* queued for the same SMP target. If a third SMP cmd is sent to the SPC for an
* SMP target that already has a SMP cmd pending and one queued, then the
* SPC responds with the ERROR_INTERNAL_SMP_RESOURCE response.
*
* Additionally, the SPC has an 8 entry deep cmd queue and the number of SMP
* cmds that can be queued is controlled by the PORT_CONTROL IOMB. The
* SPC default is 1 SMP command/port (iport). These 2 queued SMP cmds would
* have to be for different SMP targets. The INTERNAL_SMP_RESOURCE error will
* also be returned if a 2nd SMP cmd is sent to the controller when there is
* already 1 SMP cmd queued for that port or if a 3rd SMP cmd is sent to the
* queue if there are already 2 queued SMP cmds.
*/
void
pmcs_smp_acquire(pmcs_iport_t *iport)
{
ASSERT(iport);
if (iport == NULL) {
pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL,
"%s: iport is NULL...", __func__);
return;
}
mutex_enter(&iport->smp_lock);
while (iport->smp_active) {
pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL,
"%s: SMP is active on thread 0x%p, waiting", __func__,
(void *)iport->smp_active_thread);
cv_wait(&iport->smp_cv, &iport->smp_lock);
}
iport->smp_active = B_TRUE;
iport->smp_active_thread = curthread;
pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL,
"%s: SMP acquired by thread 0x%p", __func__,
(void *)iport->smp_active_thread);
mutex_exit(&iport->smp_lock);
}
void
pmcs_smp_release(pmcs_iport_t *iport)
{
ASSERT(iport);
if (iport == NULL) {
pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL,
"%s: iport is NULL...", __func__);
return;
}
mutex_enter(&iport->smp_lock);
pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL,
"%s: SMP released by thread 0x%p", __func__, (void *)curthread);
iport->smp_active = B_FALSE;
iport->smp_active_thread = NULL;
cv_signal(&iport->smp_cv);
mutex_exit(&iport->smp_lock);
}
/*
* Update a PHY's attached-port-pm and target-port-pm properties
*
* phyp: PHY whose properties are to be updated
*
* att_bv: Bit value of the attached-port-pm property to be updated in the
* 64-bit holding area for the PHY.
*
* tgt_bv: Bit value of the target-port-pm property to update in the 64-bit
* holding area for the PHY.
*
* prop_add_val: If TRUE, we're adding bits into the property value.
* Otherwise, we're taking them out. Either way, the properties for this
* PHY will be updated.
*
* NOTE: The devinfo tree view of attached port and target port is reversed
* from SAS. Thus, the attached port phymask corresponds to the target port
* phymask and vice-versa.
*/
void
pmcs_update_phy_pm_props(pmcs_phy_t *phyp, uint64_t att_bv, uint64_t tgt_bv,
boolean_t prop_add_val)
{
if (prop_add_val) {
/*
* If the values are currently 0, then we're setting the
* phymask for just this PHY as well.
*/
if (phyp->att_port_pm_tmp == 0) {
phyp->att_port_pm = tgt_bv;
phyp->tgt_port_pm = att_bv;
}
phyp->att_port_pm_tmp |= tgt_bv;
phyp->tgt_port_pm_tmp |= att_bv;
(void) snprintf(phyp->att_port_pm_str, PMCS_PM_MAX_NAMELEN,
"%"PRIx64, phyp->att_port_pm_tmp);
(void) snprintf(phyp->tgt_port_pm_str, PMCS_PM_MAX_NAMELEN,
"%"PRIx64, phyp->tgt_port_pm_tmp);
} else {
phyp->att_port_pm_tmp &= ~tgt_bv;
phyp->tgt_port_pm_tmp &= ~att_bv;
if (phyp->att_port_pm_tmp) {
(void) snprintf(phyp->att_port_pm_str,
PMCS_PM_MAX_NAMELEN, "%"PRIx64,
phyp->att_port_pm_tmp);
} else {
phyp->att_port_pm_str[0] = '\0';
phyp->att_port_pm = 0;
}
if (phyp->tgt_port_pm_tmp) {
(void) snprintf(phyp->tgt_port_pm_str,
PMCS_PM_MAX_NAMELEN, "%"PRIx64,
phyp->tgt_port_pm_tmp);
} else {
phyp->tgt_port_pm_str[0] = '\0';
phyp->tgt_port_pm = 0;
}
}
if (phyp->target == NULL) {
return;
}
if (phyp->target->sd) {
(void) scsi_device_prop_update_string(phyp->target->sd,
SCSI_DEVICE_PROP_PATH, SCSI_ADDR_PROP_ATTACHED_PORT_PM,
phyp->att_port_pm_str);
(void) scsi_device_prop_update_string(phyp->target->sd,
SCSI_DEVICE_PROP_PATH, SCSI_ADDR_PROP_TARGET_PORT_PM,
phyp->tgt_port_pm_str);
} else if (phyp->target->smpd) {
(void) smp_device_prop_update_string(phyp->target->smpd,
SCSI_ADDR_PROP_ATTACHED_PORT_PM,
phyp->att_port_pm_str);
(void) smp_device_prop_update_string(phyp->target->smpd,
SCSI_ADDR_PROP_TARGET_PORT_PM,
phyp->tgt_port_pm_str);
}
}
/* ARGSUSED */
void
pmcs_deregister_device_work(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
{
pmcs_phy_t *pptr;
for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
pmcs_lock_phy(pptr);
if (pptr->deregister_wait) {
pmcs_deregister_device(pwp, pptr);
}
pmcs_unlock_phy(pptr);
}
}