pmcs_subr.c revision 601c90f161ff0319c1b4a2c3362b466043a65d8d
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * CDDL HEADER START
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 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * See the License for the specific language governing permissions
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * and limitations under the License.
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 * CDDL HEADER END
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Use is subject to license terms.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * This file contains various support routines.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Local static data
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * SAS Topology Configuration
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 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 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 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 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 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 * Often used strings
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 * Some Initial setup steps.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t i, scratch, regbar, regoff, barbar, baroff;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Check current state. If we're not at READY state,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * we can't go further.
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 "%s: AAP Error State (0x%x)",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
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 if ((scratch & PMCS_MSGU_AAP_STATE_MASK) != PMCS_MSGU_AAP_STATE_READY) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: AAP unit not ready (state 0x%x)",
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 * Read the offset from the Message Unit scratchpad 0 register.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * This allows us to read the MPI Configuration table.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Check its signature for validity.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster regoff = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH0);
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 "%s: bad MPI BAR (register BAROFF=0x%08x, "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "passed BAROFF=0x%08x)", __func__, regbar, barbar);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_AS) != PMCS_SIGNATURE) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: Bad MPI Configuration Table Signature 0x%x", __func__,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IR) != PMCS_MPI_REVISION1) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: Bad MPI Configuration Revision 0x%x", __func__,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
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 pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_GSTO);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IQCTO);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_OQCTO);
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 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 "%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 "%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 return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (PMCS_HPIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1))) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (PMCS_OQ_EVENTS << DEVICE_HANDLE_REMOVED_SHIFT));
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 "%s: I/O queue depth set to 0. Setting to %d",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: I/O queue depth set too low (%d). Setting to %d",
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 "%s: I/O queue depth set too high (%d). Setting to %d",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Allocate consistent memory for OQs and IQs.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->iqp_dma_attr = pwp->oqp_dma_attr = pmcs_dattr;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->oqp_dma_attr.dma_attr_align = PMCS_QENTRY_SIZE;
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 pwp->oqp_dma_attr.dma_attr_addr_hi = 0x000000FFFFFFFFFFull;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < PMCS_NIQ; 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 "Failed to setup DMA for iqp[%d]", i);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster bzero(pwp->iqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < PMCS_NOQ; 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 "Failed to setup DMA for oqp[%d]", i);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster bzero(pwp->oqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Install the IQ and OQ addresses (and null out the rest).
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->iqpi_offset[i] = pmcs_rd_iqc_tbl(pwp, PMCS_IQPIOFFX(i));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->oqci_offset[i] = pmcs_rd_oqc_tbl(pwp, PMCS_OQCIOFFX(i));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), pwp->ioq_depth |
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Set up logging, if defined.
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 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 * Interrupt vectors, outbound queues, and odb_auto_clear
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 * 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 * 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 pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->odb_auto_clear = (1 << PMCS_FATAL_INTERRUPT) |
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (1 << PMCS_MSIX_GENERAL) | (1 << PMCS_MSIX_IODONE) |
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster PMCS_FERRIE | (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Enable Interrupt Reassertion
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Default Delay 1000us
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, ferr | PMCS_MPI_IRAE);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR, pwp->odb_auto_clear);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Start the Message Passing protocol with the PMC chip.
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) & PMCS_MSGU_IBDB_MPIINI) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Check to make sure we got to INIT state.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (PMCS_MPI_S(pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE)) !=
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: MPI launch failed (GST 0x%x DBCLR 0x%x)", __func__,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Stop the Message Passing protocol with the PMC chip.
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) & PMCS_MSGU_IBDB_MPICTU) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
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 * 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 pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* This is on the high priority queue */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[0] = LE_32(PMCS_HIPRI(pwp, iqo, PMCIN_ECHO));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[0] = LE_32(PMCS_IOMB_IN_SAS(iqo, PMCIN_ECHO));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: command timed out on echo test #%d",
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 PMCS_INTR_THRESHOLD(PMCS_QUANTUM_TIME_USECS * 1000 /
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Start the (real) phys
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_start_phy(pmcs_hw_t *pwp, int phynum, int linkmode, int speed)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_PHY_START));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wwn2barray(pwp->sas_wwns[phynum], sap.sas_address);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wwn2barray(pwp->sas_wwns[0], sap.sas_address);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster sap.phy_identifier = phynum & SAS2_PHYNUM_MASK;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (void) memcpy(&msg[3], &sap, sizeof (sas_identify_af_t));
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 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: failed to reset counters on PHY (%d)",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Called with PHY locked
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_reset_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint8_t type)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster const char *mbar;
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 pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__);
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 iomb[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iomb[3] = LE_32(40 << SMP_REQUEST_LENGTH_SHIFT);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Send SMP PHY CONTROL/HARD or LINK RESET
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: sending %s to %s for phy 0x%x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster __func__, mbar, pptr->parent->path, pptr->phynum);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Unlike most other Outbound messages, status for
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * a local phy operation is in DWORD 3.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iomb[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iomb[2] = LE_32((PMCS_PHYOP_LINK_RESET << 8) | phynum);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster iomb[2] = LE_32((PMCS_PHYOP_HARD_RESET << 8) | phynum);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: sending %s to %s", __func__, mbar, pptr->path);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* SMP serialization */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Release SMP lock before reacquiring PHY lock */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
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 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: Issuing SMP ABORT for htag 0x%08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (void) snprintf(buf, sizeof (buf), "Status 0x%x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: %s action returned %s for %s", __func__, mbar, es,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Stop the (real) phys. No PHY or softstate locks are required as this only
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * happens during detach.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: unable to find port %d", __func__, phynum);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_PHY_STOP));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Make this unconfigured now.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * No locks should be required as this is only called during detach
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 Fosterpmcs_sas_diag_execute(pmcs_hw_t *pwp, uint32_t cmd, uint32_t cmd_desc,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t htag, *ptr, status, msg[PMCS_MSG_SIZE << 1];
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nowrk, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_SAS_DIAG_EXECUTE));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (cmd_desc << PMCS_DIAG_CMD_DESC_SHIFT) | phynum);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nomsg, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Return for counter reset */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Return for counter value */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: failed, status (0x%x)", __func__, status);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/* Get the current value of the counter for desc on phynum and return it. */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_get_diag_report(pmcs_hw_t *pwp, uint32_t desc, uint8_t phynum)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (pmcs_sas_diag_execute(pwp, PMCS_DIAG_REPORT_GET, desc, phynum));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster/* Clear all of the counters for phynum. Returns the status of the command. */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_clear_diag_counters(pmcs_hw_t *pwp, uint8_t phynum)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Get firmware timestamp
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_get_time_stamp(pmcs_hw_t *pwp, uint64_t *ts)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nowrk, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_GET_TIME_STAMP));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nomsg, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster *ts = LE_32(msg[2]) | (((uint64_t)LE_32(msg[3])) << 32);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Dump all pertinent registers
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "pmcs%d: Register dump start",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "OBDB (intr): 0x%08x (mask): 0x%08x (clear): 0x%08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH0: 0x%08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH1: 0x%08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH2: 0x%08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH3: 0x%08x",
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 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 "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_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE IQFRZ0: 0x%08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE IQFRZ1: 0x%08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE MSGU TICK: 0x%08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE IOP TICK: 0x%08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pinfo = pmcs_rd_gst_tbl(pwp, PMCS_GST_PHY_INFO(i));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rerrf = pmcs_rd_gst_tbl(pwp, PMCS_GST_RERR_INFO(i));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "GST TABLE PHY%d STARTED=%d LINK=%d RERR=0x%08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "pmcs%d: Register dump end",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Handle SATA Abort and other error processing
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_phy_t *pptr, *pnext, *pnext_uplevel[PMCS_MAX_XPND];
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * XXX: Need to make sure this doesn't happen
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * XXX: when non-NCQ commands are running.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* what if other failures happened? */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr->abort_pending == 0 || pptr->abort_sent) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_abort(pwp, pptr, pptr->device_id, 1, 1) == ENOMEM) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If the iport is no longer active, flush the queues
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, tgt,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: Clearing target 0x%p, inactive iport",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Register a device (get a device handle for it).
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Called with PHY lock held.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_register_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr)) == NULL) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_REGISTER_DEVICE));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster (pptr->link_rate << PMCS_DEVREG_LINK_RATE_SHIFT);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_validate_devid(pwp->root_phys, pptr, tmp) == B_FALSE) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: phy %s already has bogus devid 0x%x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: phy %s already has a device id 0x%x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: status 0x%x when trying to register device %s",
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 Foster * Deregister a device (remove a device handle).
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Called with PHY locked.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_deregister_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: status 0x%x when trying to deregister device %s",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: device %s deregistered", __func__, pptr->path);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Deregister all registered devices.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_deregister_devices(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
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 * Perform a 'soft' reset on the PMC chip
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_soft_reset(pmcs_hw_t *pwp, boolean_t no_restart)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster uint32_t s2, sfrbits, gsm, rapchk, wapchk, wdpchk, spc, tsmode;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Disable interrupts
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "%s", __func__);
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 s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) &
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: PMCS_MSGU_HOST_SOFT_RESET_READY never came "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_topunit(pwp, PMCS_EVENT_INT_ENABLE, 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_topunit(pwp, PMCS_ERROR_INT_ENABLE, 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster sfrbits = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
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 pmcs_wr_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH0, HST_SFT_RESET_SIG);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "GSM %08x -> %08x", gsm,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_gsm_reg(pwp, GSM_CFG_AND_RESET, gsm & ~PMCS_SOFT_RESET_BITS);
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 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 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 pmcs_wr_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN, 0);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 5.5 (Temporary workaround for 1.07.xx Beta)
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 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_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_topunit(pwp, PMCS_SPC_RESET, spc & ~(BDMA_CORE_RSTB|OSSP_RSTB));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_topunit(pwp, PMCS_SPC_RESET, spc | (BDMA_CORE_RSTB|OSSP_RSTB));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "GSM %08x -> %08x", gsm,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_gsm_reg(pwp, GSM_CFG_AND_RESET, gsm | PMCS_SOFT_RESET_BITS);
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 pmcs_wr_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN, rapchk);
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 pmcs_wr_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN, wapchk);
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 pmcs_wr_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN, wdpchk);
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 spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((spc & PMCS_MSGU_AAP_SFR_PROGRESS) == sfrbits) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((spc & PMCS_MSGU_AAP_SFR_PROGRESS) != sfrbits) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Wait for up to 5 seconds for AAP state to come either ready or error.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < 50; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
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 return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pwp->state == STATE_DEAD || pwp->state == STATE_UNPROBING ||
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pwp->state == STATE_PROBING || pwp->locks_initted == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Return at this point if we dont need to startup.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Clean up various soft state.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster bzero(pwp->shadow_iqpi, sizeof (pwp->shadow_iqpi));
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < PMCS_NIQ; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster bzero(pwp->iqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (i = 0; i < PMCS_NOQ; i++) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster bzero(pwp->oqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster bzero(pwp->work, sizeof (pmcwork_t) * pwp->max_cmd);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Clear out any leftover commands sitting in the work list
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster } else if (pwrk->state == PMCS_WORK_STATE_IOCOMPQ) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * The other states of NIL, READY and INTR
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * should not be visible outside of a lock being held.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Restore Interrupt Mask
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 * Set up MPI again.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Restart MPI
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Run any completions
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Reset a device or a logical unit.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_reset_dev(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint64_t lun)
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 * 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 * XXX: FIX ME
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rval = pmcs_ssp_tmf(pwp, pptr, SAS_LOGICAL_UNIT_RESET, 0, lun,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rval = pmcs_reset_phy(pwp, pptr, PMCS_PHYOP_LINK_RESET);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: cannot reset a SMP device yet (%s)",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Now harvest any commands killed by this action
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * by issuing an ABORT for all commands on this device.
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 * Called with PHY locked.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_get_device_handle(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If we changed while registering, punt
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
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 return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * An ETIMEDOUT error means we retry (if our counter isn't
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * exhausted)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: Retries exhausted for %s, killing",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Other errors or no valid device id is fatal, but don't
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * preclude a future action.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster return (-1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s", __func__);
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 if ((iport == NULL) || (iport->iss_tgtmap == NULL))
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* destroy target map */
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 * Remove all phys from the iport handle's phy list, unset its
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * primary phy and update its state.
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 * Query the phymap and populate the iport handle passed in.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Called with iport lock held.
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 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 * Set a back pointer in the phy to this iport.
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 * Finally, insert the phy into our list
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 * 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 * 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 Fosterpmcs_get_iport_by_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster ua = sas_phymap_lookup_ua(pwp->hss_phymap, pwp->sas_wwns[0],
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 * 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 /* 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 /* Update the phy handle with the data from the previous primary */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr->tolerates_sas2 = prev_primary->tolerates_sas2;
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 /* ...replace with the values from prev_primary... */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_update_phy_pm_props(pptr, prev_primary->att_port_pm_tmp,
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 /* Clear the prev_primary's values */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_update_phy_pm_props(prev_primary, prev_primary->att_port_pm_tmp,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * We call pmcs_unlock_phy() on pptr because it now contains the
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * list of children.
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 pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: iport "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "[0x%p] refcnt (%d)", __func__, (void *)iport, iport->refcnt);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_phymap_activate(void *arg, char *ua, void **privp)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if ((pwp->state == STATE_UNPROBING) || (pwp->state == STATE_DEAD)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (scsi_hba_iportmap_iport_add(pwp->hss_iportmap, ua, NULL) !=
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 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 /* Set the HBA softstate as our private data for this unit address */
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 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 pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_phymap_deactivate(void *arg, char *ua, void *privp)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (scsi_hba_iportmap_iport_remove(pwp->hss_iportmap, ua) !=
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 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 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 * Top-level discovery function
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster DTRACE_PROBE2(pmcs__discover__entry, ulong_t, pwp->work_flags,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Ensure we have at least one phymap active */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
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 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: no iports attached, retry discovery", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: configuration already in progress", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "Discovery begin");
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * First, tell SCSA that we're beginning set operations.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * The order of the following traversals is important.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * The first one checks for changed expanders.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * The second one aborts commands for dead devices and deregisters them.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * The third one clears the contents of dead expanders from the tree
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * The fourth one clears now dead devices in expanders that remain.
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 * 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 config_changed = pmcs_check_expanders(pwp, root_phy);
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 if (pmcs_kill_devices(pwp, root_phy) || config_changed) {
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 * 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 * NOTE: pmcs_clear_expander requires softstate lock
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
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 * 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 * 5. Now check for and configure new devices.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_configure_new_devices(pwp, root_phy)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster DTRACE_PROBE2(pmcs__discover__exit, ulong_t, pwp->work_flags,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "Discovery end");
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 if (pmcs_report_observations(pwp) == B_FALSE) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If config_changed is TRUE, we need to reschedule
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * discovery now.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: Config has changed, will re-run discovery", __func__);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pptr = pmcs_find_phy_needing_work(pwp, pwp->root_phys);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (!WORK_IS_SCHEDULED(pwp, PMCS_WORK_DISCOVER)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "PHY %s dead=%d changed=%d configured=%d "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "but no work scheduled", pptr->path, pptr->dead,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Clean up and restart discovery */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Return any PHY that needs to have scheduled work done. The PHY is returned
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_find_phy_needing_work(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr->changed || (pptr->dead && pptr->valid_device_id)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster cphyp = pmcs_find_phy_needing_work(pwp, cphyp);
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 for (iport = list_head(&pwp->iports); iport != NULL;
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 if (!sas_phymap_uahasphys(pwp->hss_phymap, iport->ua)) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster continue; /* skip set_begin */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (scsi_hba_tgtmap_set_begin(tgtmap) != DDI_SUCCESS) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: set begin on tgtmap [0x%p]", __func__, (void *)tgtmap);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Report current observations to SCSA.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Observation is stable, report what we currently see to the tgtmaps
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * for delta processing.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Skip PHYs that have nothing attached or are dead.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: oops, PHY %s changed; restart discovery",
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 /* No iport for this tgt */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
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 continue; /* skip set_end */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (scsi_hba_tgtmap_set_end(tgtmap, 0) != DDI_SUCCESS) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: set end on tgtmap [0x%p]", __func__, (void *)tgtmap);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Now that discovery is complete, set up the necessary
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * DDI properties on each iport node.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster for (iport = list_head(&pwp->iports); iport != NULL;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* Set up the 'attached-port' property on the iport */
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 /* Otherwise, set it to remote phy's wwn */
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 * Report observations into a particular iport's target map
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Called with phyp (and all descendents) locked
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_report_iport_observations(pmcs_hw_t *pwp, pmcs_iport_t *iport,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster default: /* Skip unknown PHYs. */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* for non-root phys, skip to sibling */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "iport_observation: adding %s on tgtmap [0x%p] phy [0x%p]",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (scsi_hba_tgtmap_set_add(tgtmap, tgt_type, ua, NULL) !=
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* for non-root phys, report siblings too */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Check for and configure new devices.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If the changed device is a SATA device, add a SATA device.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If the changed device is a SAS device, add a SAS device.
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 * 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 * NOTE: pptr passed in by the caller will be a root PHY
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_configure_new_devices(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_phy_t *pnext, *orig_pptr = pptr, *root_phy, *pchild;
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * First, walk through each PHY at this level
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Set the new dtype if it has changed
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pptr->changed == 0 || pptr->dead || pptr->configured) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Confirm that this target's iport is configured
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 * Now walk through each PHY again, recalling ourselves if they
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * have children
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster rval = pmcs_configure_new_devices(pwp, pchild);
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 * Called with parent PHY locked.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_set_changed(pmcs_hw_t *pwp, pmcs_phy_t *parent, boolean_t changed,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (parent->dtype == EXPANDER && parent->level) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_set_changed(pwp, parent->children, changed,
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 * Called with the parent PHY locked.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_kill_changed(pmcs_hw_t *pwp, pmcs_phy_t *parent, int level)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_kill_changed(pwp, pptr->children, level + 1);
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Only kill siblings at level > 0
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Go through every PHY and clear any that are dead (unless they're expanders)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_clear_phys(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Clear volatile parts of a phy. Called with PHY locked.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_clear_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "%s: %s",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* keep sibling */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* keep children */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* keep parent */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* keep hw_event_ack */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* keep phynum */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* keep dtype */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster /* keep portid */
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 /* 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 /* keep target */
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 * Called with PHY locked.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_new_tport(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "%s: phy 0x%p @ %s",
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (pmcs_configure_phy(pwp, pptr) == B_FALSE) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If the config failed, mark the PHY as changed.
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 /* Mark PHY as no longer changed */
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If the PHY has no target pointer, see if there's a dead PHY that
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 * Otherwise, it'll get done in tran_tgt_init.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: Not assigning existing tgt %p for PHY %p "
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "(WWN mismatch)", __func__, (void *)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 * Called with PHY lock held.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_configure_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Mark this device as no longer changed.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If we don't have a device handle, get one.
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 * Called with PHY locked
8af80418ba1ec431c8027fa9668e5678658d3611Allan Fosterpmcs_configure_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr, pmcs_iport_t *iport)
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 * Step 2- make sure we don't overflow
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * Step 3- Check if this expander is part of a wide phy that has
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * already been configured.
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 * 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 * 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 * 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 memcmp(ctmp->sas_address, pptr->sas_address, 8) == 0) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * If these phys are not root PHYs, compare their SAS
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster * addresses too.
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 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: PHY %s part of wide PHY %s "
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 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 * Step 5- figure out how many phys are in this expander.
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster if (nphy == 0 && ddi_get_lbolt() < pptr->config_stop) {
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
8af80418ba1ec431c8027fa9668e5678658d3611Allan Foster "%s: Retries exhausted for %s, killing", __func__,
goto out;
for (i = 0; i < nphy; i++) {
while (clist) {
if (result <= 0) {
goto out;
goto out;
out:
while (clist) {
if (nphy <= 0) {
while (ctmp) {
if (result <= 0) {
while (ctmp) {
if (changed) {
if (kill_changed) {
static boolean_t
if (config_changed) {
return (config_changed);
if (pchild) {
return (config_changed);
while (ctmp) {
while (ctmp) {
if (level == 0) {
if (level == 0) {
while (ctmp) {
if (level == 0) {
if (level == 0) {
if (level == 0) {
int result;
result = 0;
goto out;
result = 0;
goto out;
result = 0;
goto out;
if (result) {
result = 0;
goto out;
switch (status) {
goto again;
result = 0;
out:
return (result);
int result;
result = 0;
goto out;
result = 0;
goto out;
result = 0;
goto out;
if (result) {
goto out;
switch (status) {
goto out;
goto out;
goto out;
goto out;
case SAS_IF_DTYPE_ENDPOINT:
B_TRUE);
case SAS_IF_DTYPE_EDGE:
case SAS_IF_DTYPE_FANOUT:
B_TRUE);
out:
return (result);
pmcwork_t *p;
if (p == NULL) {
if (p == NULL) {
return (NULL);
p->ssp_event = 0;
p->dead = 0;
if (phyp) {
if (p->phy) {
p->abt_htag = 0;
p->timer = 0;
pmcwork_t *p;
return (NULL);
int wait)
return (EBUSY);
case SAS:
case SATA:
case EXPANDER:
pptr);
return (ENOMEM);
if (wait) {
return (ENODEV);
if (all_cmds) {
return (ENOMEM);
if (all_cmds) {
if (!wait) {
if (all_cmds) {
if (result) {
return (ETIMEDOUT);
if (all_cmds) {
return (EINVAL);
return (ENOMEM);
return (ENOMEM);
return (EIO);
if (result) {
return (ETIMEDOUT);
return (ETIMEDOUT);
return (EIO);
return (EIO);
return (EIO);
return (EIO);
case SAS_RSP_TMF_COMPLETE:
result = 0;
case SAS_RSP_TMF_SUCCEEDED:
result = 0;
case SAS_RSP_INVALID_FRAME:
case SAS_RSP_TMF_FAILED:
return (result);
return (ENOMEM);
return (ENOMEM);
if (result) {
return (EIO);
return (EIO);
return (EIO);
while ((c = *xfvec++) != 0) {
switch (size) {
while (nbyt-- > 0) {
while (nbyt-- > 0) {
if (bige) {
while (nbyt-- > 0) {
if (bige) {
const char *rate;
switch (linkrt) {
case SAS_LINK_RATE_1_5GBIT:
case SAS_LINK_RATE_3GBIT:
case SAS_LINK_RATE_6GBIT:
return (rate);
switch (type) {
case NOTHING:
case SATA:
case SAS:
case EXPANDER:
switch (tmf) {
case SAS_ABORT_TASK:
case SAS_ABORT_TASK_SET:
case SAS_CLEAR_TASK_SET:
case SAS_LOGICAL_UNIT_RESET:
case SAS_I_T_NEXUS_RESET:
case SAS_CLEAR_ACA:
case SAS_QUERY_TASK:
case SAS_QUERY_TASK_SET:
case SAS_QUERY_UNIT_ATTENTION:
switch (status) {
case PMCOUT_STATUS_OK:
case PMCOUT_STATUS_ABORTED:
case PMCOUT_STATUS_OVERFLOW:
case PMCOUT_STATUS_UNDERFLOW:
case PMCOUT_STATUS_FAILED:
case PMCOUT_STATUS_NO_DEVICE:
case PMCOUT_STATUS_PROG_ERROR:
return (NULL);
return (result);
const char *fwsupport;
case PMCS_FW_TYPE_RELEASED:
case PMCS_FW_TYPE_DEVELOPMENT:
case PMCS_FW_TYPE_ALPHA:
case PMCS_FW_TYPE_BETA:
static pmcs_phy_t *
while (phyp) {
return (phyp);
if (match) {
return (match);
return (NULL);
while (phyp) {
if (match) {
return (match);
return (NULL);
static boolean_t
while (pptr) {
return (B_FALSE);
return (rval);
return (B_TRUE);
static pmcs_phy_t *
while (phyp) {
return (phyp);
if (matched_phy) {
return (matched_phy);
return (NULL);
while (pptr) {
if (matched_phy) {
return (matched_phy);
return (NULL);
while (pptr) {
goto next_phy;
goto next_phy;
return (pptr);
sas_addr);
if (pnext) {
return (pnext);
return (NULL);
case FIS_REG_H2DEV:
case FIS_REG_D2H:
size_t i;
while (tphyp) {
int rval = 0;
while (phyp) {
if (pchild) {
if (rval) {
return (rval);
if (remove_device) {
if (rval) {
return (rval);
return (rval);
int r, result;
return (ENOMEM);
return (ENOMEM);
if (result) {
return (ETIMEDOUT);
tsc = 0;
tsc = 0;
sg++;
unsigned long off, n;
DDI_SUCCESS) {
return (iqci);
DDI_SUCCESS) {
return (oqpi);
return (rv);
switch (off) {
case PMCS_SPC_RESET:
case PMCS_SPC_BOOT_STRAP:
case PMCS_SPC_DEVICE_ID:
case PMCS_DEVICE_REVISION:
return (off);
switch (off) {
case PMCS_SPC_RESET:
case PMCS_DEVICE_REVISION:
DDI_SUCCESS) {
DDI_SUCCESS) {
int offset;
switch (opcode) {
case PMCOUT_ECHO:
case PMCOUT_SAS_HW_EVENT:
case PMCOUT_GET_DEVICE_HANDLE:
case PMCOUT_SATA_EVENT:
case PMCOUT_SSP_EVENT:
case PMCOUT_GPIO:
case PMCOUT_GPIO_EVENT:
case PMCOUT_GET_TIME_STAMP:
case PMCOUT_SKIP_ENTRIES:
case PMCOUT_GENERAL_EVENT:
case PMCOUT_SSP_COMPLETION:
case PMCOUT_SMP_COMPLETION:
case PMCOUT_SATA_COMPLETION:
case PMCOUT_DEVICE_INFO:
case PMCOUT_FW_FLASH_UPDATE:
case PMCOUT_SSP_ABORT:
case PMCOUT_SATA_ABORT:
case PMCOUT_SMP_ABORT:
case PMCOUT_SET_DEVICE_STATE:
case PMCOUT_GET_DEVICE_STATE:
case PMCOUT_SET_DEVICE_INFO:
case PMCOUT_LOCAL_PHY_CONTROL:
case PMCOUT_SAS_DIAG_EXECUTE:
case PMCOUT_PORT_CONTROL:
case PMCOUT_GET_INFO:
case PMCOUT_GET_VPD:
case PMCOUT_SET_VPD:
case PMCOUT_TWI:
switch (result) {
case SMP_RES_UNKNOWN_FUNCTION:
case SMP_RES_FUNCTION_FAILED:
case SMP_RES_PHY_VACANT:
return (result);
DDI_SUCCESS) {
return (B_FALSE);
return (B_FALSE);
!= DDI_DMA_MAPPED) {
return (B_FALSE);
return (B_FALSE);
return (B_TRUE);
case PMCS_TAG_TYPE_CBACK:
case PMCS_TAG_TYPE_WAIT:
case PMCS_TAG_TYPE_NONE:
#ifdef DEBUG
return (B_FALSE);
return (B_TRUE);
return (B_FALSE);
while (tphyp) {
if (level == 0) {
#ifdef DEBUG
if (level == 0) {
while (phy_next) {
while (phy_next) {
if (level == 0) {
#ifdef DEBUG
while (phyp) {
return (phyp);
DDI_SUCCESS) {
while (tphyp) {
while (tphyp) {
while (phyp) {
static pmcs_phy_t *
return (local);
return (DDI_FAILURE);
return (DDI_FAILURE);
goto check_failed;
for (i = 0; i < PMCS_NIQ; i++) {
if ((pmcs_check_dma_handle(
goto check_failed;
for (i = 0; i < PMCS_NOQ; i++) {
if ((pmcs_check_dma_handle(
goto check_failed;
goto check_failed;
goto check_failed;
!= DDI_SUCCESS))) {
goto check_failed;
while (pchunk) {
!= DDI_SUCCESS) ||
!= DDI_SUCCESS)) {
goto check_failed;
while (ctmp) {
if (ctmp) {
static boolean_t
return (rval);
if (prop_add_val) {