1*4c06356bSdh /* 2*4c06356bSdh * CDDL HEADER START 3*4c06356bSdh * 4*4c06356bSdh * The contents of this file are subject to the terms of the 5*4c06356bSdh * Common Development and Distribution License (the "License"). 6*4c06356bSdh * You may not use this file except in compliance with the License. 7*4c06356bSdh * 8*4c06356bSdh * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*4c06356bSdh * or http://www.opensolaris.org/os/licensing. 10*4c06356bSdh * See the License for the specific language governing permissions 11*4c06356bSdh * and limitations under the License. 12*4c06356bSdh * 13*4c06356bSdh * When distributing Covered Code, include this CDDL HEADER in each 14*4c06356bSdh * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*4c06356bSdh * If applicable, add the following below this CDDL HEADER, with the 16*4c06356bSdh * fields enclosed by brackets "[]" replaced with your own identifying 17*4c06356bSdh * information: Portions Copyright [yyyy] [name of copyright owner] 18*4c06356bSdh * 19*4c06356bSdh * CDDL HEADER END 20*4c06356bSdh * 21*4c06356bSdh * 22*4c06356bSdh * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23*4c06356bSdh * Use is subject to license terms. 24*4c06356bSdh */ 25*4c06356bSdh 26*4c06356bSdh /* 27*4c06356bSdh * This file contains various support routines. 28*4c06356bSdh */ 29*4c06356bSdh 30*4c06356bSdh #include <sys/scsi/adapters/pmcs/pmcs.h> 31*4c06356bSdh 32*4c06356bSdh /* 33*4c06356bSdh * Local static data 34*4c06356bSdh */ 35*4c06356bSdh static int tgtmap_usec = MICROSEC; 36*4c06356bSdh 37*4c06356bSdh /* 38*4c06356bSdh * SAS Topology Configuration 39*4c06356bSdh */ 40*4c06356bSdh static void pmcs_new_tport(pmcs_hw_t *, pmcs_phy_t *); 41*4c06356bSdh static void pmcs_configure_expander(pmcs_hw_t *, pmcs_phy_t *, pmcs_iport_t *); 42*4c06356bSdh 43*4c06356bSdh static boolean_t pmcs_check_expanders(pmcs_hw_t *, pmcs_phy_t *); 44*4c06356bSdh static void pmcs_check_expander(pmcs_hw_t *, pmcs_phy_t *); 45*4c06356bSdh static void pmcs_clear_expander(pmcs_hw_t *, pmcs_phy_t *, int); 46*4c06356bSdh 47*4c06356bSdh static int pmcs_expander_get_nphy(pmcs_hw_t *, pmcs_phy_t *); 48*4c06356bSdh static int pmcs_expander_content_discover(pmcs_hw_t *, pmcs_phy_t *, 49*4c06356bSdh pmcs_phy_t *); 50*4c06356bSdh 51*4c06356bSdh static int pmcs_smp_function_result(pmcs_hw_t *, smp_response_frame_t *); 52*4c06356bSdh static boolean_t pmcs_validate_devid(pmcs_phy_t *, pmcs_phy_t *, uint32_t); 53*4c06356bSdh static void pmcs_clear_phys(pmcs_hw_t *, pmcs_phy_t *); 54*4c06356bSdh static int pmcs_configure_new_devices(pmcs_hw_t *, pmcs_phy_t *); 55*4c06356bSdh static boolean_t pmcs_report_observations(pmcs_hw_t *); 56*4c06356bSdh static boolean_t pmcs_report_iport_observations(pmcs_hw_t *, pmcs_iport_t *, 57*4c06356bSdh pmcs_phy_t *); 58*4c06356bSdh static pmcs_phy_t *pmcs_find_phy_needing_work(pmcs_hw_t *, pmcs_phy_t *); 59*4c06356bSdh static int pmcs_kill_devices(pmcs_hw_t *, pmcs_phy_t *); 60*4c06356bSdh static void pmcs_lock_phy_impl(pmcs_phy_t *, int); 61*4c06356bSdh static void pmcs_unlock_phy_impl(pmcs_phy_t *, int); 62*4c06356bSdh static pmcs_phy_t *pmcs_clone_phy(pmcs_phy_t *); 63*4c06356bSdh static boolean_t pmcs_configure_phy(pmcs_hw_t *, pmcs_phy_t *); 64*4c06356bSdh static void pmcs_reap_dead_phy(pmcs_phy_t *); 65*4c06356bSdh static pmcs_iport_t *pmcs_get_iport_by_ua(pmcs_hw_t *, char *); 66*4c06356bSdh static boolean_t pmcs_phy_target_match(pmcs_phy_t *); 67*4c06356bSdh static void pmcs_handle_ds_recovery_error(pmcs_phy_t *phyp, 68*4c06356bSdh pmcs_xscsi_t *tgt, pmcs_hw_t *pwp, const char *func_name, int line, 69*4c06356bSdh char *reason_string); 70*4c06356bSdh 71*4c06356bSdh /* 72*4c06356bSdh * Often used strings 73*4c06356bSdh */ 74*4c06356bSdh const char pmcs_nowrk[] = "%s: unable to get work structure"; 75*4c06356bSdh const char pmcs_nomsg[] = "%s: unable to get Inbound Message entry"; 76*4c06356bSdh const char pmcs_timeo[] = "!%s: command timed out"; 77*4c06356bSdh 78*4c06356bSdh extern const ddi_dma_attr_t pmcs_dattr; 79*4c06356bSdh 80*4c06356bSdh /* 81*4c06356bSdh * Some Initial setup steps. 82*4c06356bSdh */ 83*4c06356bSdh 84*4c06356bSdh int 85*4c06356bSdh pmcs_setup(pmcs_hw_t *pwp) 86*4c06356bSdh { 87*4c06356bSdh uint32_t barval = pwp->mpibar; 88*4c06356bSdh uint32_t i, scratch, regbar, regoff, barbar, baroff; 89*4c06356bSdh uint32_t new_ioq_depth, ferr = 0; 90*4c06356bSdh 91*4c06356bSdh /* 92*4c06356bSdh * Check current state. If we're not at READY state, 93*4c06356bSdh * we can't go further. 94*4c06356bSdh */ 95*4c06356bSdh scratch = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1); 96*4c06356bSdh if ((scratch & PMCS_MSGU_AAP_STATE_MASK) == PMCS_MSGU_AAP_STATE_ERROR) { 97*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: AAP Error State (0x%x)", 98*4c06356bSdh __func__, pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) & 99*4c06356bSdh PMCS_MSGU_AAP_ERROR_MASK); 100*4c06356bSdh pmcs_fm_ereport(pwp, DDI_FM_DEVICE_INVAL_STATE); 101*4c06356bSdh ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST); 102*4c06356bSdh return (-1); 103*4c06356bSdh } 104*4c06356bSdh if ((scratch & PMCS_MSGU_AAP_STATE_MASK) != PMCS_MSGU_AAP_STATE_READY) { 105*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 106*4c06356bSdh "%s: AAP unit not ready (state 0x%x)", 107*4c06356bSdh __func__, scratch & PMCS_MSGU_AAP_STATE_MASK); 108*4c06356bSdh pmcs_fm_ereport(pwp, DDI_FM_DEVICE_INVAL_STATE); 109*4c06356bSdh ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST); 110*4c06356bSdh return (-1); 111*4c06356bSdh } 112*4c06356bSdh 113*4c06356bSdh /* 114*4c06356bSdh * Read the offset from the Message Unit scratchpad 0 register. 115*4c06356bSdh * This allows us to read the MPI Configuration table. 116*4c06356bSdh * 117*4c06356bSdh * Check its signature for validity. 118*4c06356bSdh */ 119*4c06356bSdh baroff = barval; 120*4c06356bSdh barbar = barval >> PMCS_MSGU_MPI_BAR_SHIFT; 121*4c06356bSdh baroff &= PMCS_MSGU_MPI_OFFSET_MASK; 122*4c06356bSdh 123*4c06356bSdh regoff = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH0); 124*4c06356bSdh regbar = regoff >> PMCS_MSGU_MPI_BAR_SHIFT; 125*4c06356bSdh regoff &= PMCS_MSGU_MPI_OFFSET_MASK; 126*4c06356bSdh 127*4c06356bSdh if (regoff > baroff) { 128*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: bad MPI Table Length " 129*4c06356bSdh "(register offset=0x%08x, passed offset=0x%08x)", __func__, 130*4c06356bSdh regoff, baroff); 131*4c06356bSdh return (-1); 132*4c06356bSdh } 133*4c06356bSdh if (regbar != barbar) { 134*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: bad MPI BAR (register " 135*4c06356bSdh "BAROFF=0x%08x, passed BAROFF=0x%08x)", __func__, 136*4c06356bSdh regbar, barbar); 137*4c06356bSdh return (-1); 138*4c06356bSdh } 139*4c06356bSdh pwp->mpi_offset = regoff; 140*4c06356bSdh if (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_AS) != PMCS_SIGNATURE) { 141*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 142*4c06356bSdh "%s: Bad MPI Configuration Table Signature 0x%x", __func__, 143*4c06356bSdh pmcs_rd_mpi_tbl(pwp, PMCS_MPI_AS)); 144*4c06356bSdh return (-1); 145*4c06356bSdh } 146*4c06356bSdh 147*4c06356bSdh if (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IR) != PMCS_MPI_REVISION1) { 148*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 149*4c06356bSdh "%s: Bad MPI Configuration Revision 0x%x", __func__, 150*4c06356bSdh pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IR)); 151*4c06356bSdh return (-1); 152*4c06356bSdh } 153*4c06356bSdh 154*4c06356bSdh /* 155*4c06356bSdh * Generate offsets for the General System, Inbound Queue Configuration 156*4c06356bSdh * and Outbound Queue configuration tables. This way the macros to 157*4c06356bSdh * access those tables will work correctly. 158*4c06356bSdh */ 159*4c06356bSdh pwp->mpi_gst_offset = 160*4c06356bSdh pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_GSTO); 161*4c06356bSdh pwp->mpi_iqc_offset = 162*4c06356bSdh pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IQCTO); 163*4c06356bSdh pwp->mpi_oqc_offset = 164*4c06356bSdh pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_OQCTO); 165*4c06356bSdh 166*4c06356bSdh pwp->fw = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_FW); 167*4c06356bSdh 168*4c06356bSdh pwp->max_cmd = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_MOIO); 169*4c06356bSdh pwp->max_dev = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO0) >> 16; 170*4c06356bSdh 171*4c06356bSdh pwp->max_iq = PMCS_MNIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1)); 172*4c06356bSdh pwp->max_oq = PMCS_MNOQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1)); 173*4c06356bSdh pwp->nphy = PMCS_NPHY(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1)); 174*4c06356bSdh if (pwp->max_iq <= PMCS_NIQ) { 175*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: not enough Inbound Queues " 176*4c06356bSdh "supported (need %d, max_oq=%d)", __func__, pwp->max_iq, 177*4c06356bSdh PMCS_NIQ); 178*4c06356bSdh return (-1); 179*4c06356bSdh } 180*4c06356bSdh if (pwp->max_oq <= PMCS_NOQ) { 181*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: not enough Outbound Queues " 182*4c06356bSdh "supported (need %d, max_oq=%d)", __func__, pwp->max_oq, 183*4c06356bSdh PMCS_NOQ); 184*4c06356bSdh return (-1); 185*4c06356bSdh } 186*4c06356bSdh if (pwp->nphy == 0) { 187*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: zero phys reported", 188*4c06356bSdh __func__); 189*4c06356bSdh return (-1); 190*4c06356bSdh } 191*4c06356bSdh if (PMCS_HPIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1))) { 192*4c06356bSdh pwp->hipri_queue = (1 << PMCS_IQ_OTHER); 193*4c06356bSdh } 194*4c06356bSdh 195*4c06356bSdh 196*4c06356bSdh for (i = 0; i < pwp->nphy; i++) { 197*4c06356bSdh PMCS_MPI_EVQSET(pwp, PMCS_OQ_EVENTS, i); 198*4c06356bSdh PMCS_MPI_NCQSET(pwp, PMCS_OQ_EVENTS, i); 199*4c06356bSdh } 200*4c06356bSdh 201*4c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_INFO2, 202*4c06356bSdh (PMCS_OQ_EVENTS << GENERAL_EVENT_OQ_SHIFT) | 203*4c06356bSdh (PMCS_OQ_EVENTS << DEVICE_HANDLE_REMOVED_SHIFT)); 204*4c06356bSdh 205*4c06356bSdh /* 206*4c06356bSdh * Verify that ioq_depth is valid (> 0 and not so high that it 207*4c06356bSdh * would cause us to overrun the chip with commands). 208*4c06356bSdh */ 209*4c06356bSdh if (pwp->ioq_depth == 0) { 210*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 211*4c06356bSdh "%s: I/O queue depth set to 0. Setting to %d", 212*4c06356bSdh __func__, PMCS_NQENTRY); 213*4c06356bSdh pwp->ioq_depth = PMCS_NQENTRY; 214*4c06356bSdh } 215*4c06356bSdh 216*4c06356bSdh if (pwp->ioq_depth < PMCS_MIN_NQENTRY) { 217*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 218*4c06356bSdh "%s: I/O queue depth set too low (%d). Setting to %d", 219*4c06356bSdh __func__, pwp->ioq_depth, PMCS_MIN_NQENTRY); 220*4c06356bSdh pwp->ioq_depth = PMCS_MIN_NQENTRY; 221*4c06356bSdh } 222*4c06356bSdh 223*4c06356bSdh if (pwp->ioq_depth > (pwp->max_cmd / (PMCS_IO_IQ_MASK + 1))) { 224*4c06356bSdh new_ioq_depth = pwp->max_cmd / (PMCS_IO_IQ_MASK + 1); 225*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 226*4c06356bSdh "%s: I/O queue depth set too high (%d). Setting to %d", 227*4c06356bSdh __func__, pwp->ioq_depth, new_ioq_depth); 228*4c06356bSdh pwp->ioq_depth = new_ioq_depth; 229*4c06356bSdh } 230*4c06356bSdh 231*4c06356bSdh /* 232*4c06356bSdh * Allocate consistent memory for OQs and IQs. 233*4c06356bSdh */ 234*4c06356bSdh pwp->iqp_dma_attr = pwp->oqp_dma_attr = pmcs_dattr; 235*4c06356bSdh pwp->iqp_dma_attr.dma_attr_align = 236*4c06356bSdh pwp->oqp_dma_attr.dma_attr_align = PMCS_QENTRY_SIZE; 237*4c06356bSdh 238*4c06356bSdh /* 239*4c06356bSdh * The Rev C chip has the ability to do PIO to or from consistent 240*4c06356bSdh * memory anywhere in a 64 bit address space, but the firmware is 241*4c06356bSdh * not presently set up to do so. 242*4c06356bSdh */ 243*4c06356bSdh pwp->iqp_dma_attr.dma_attr_addr_hi = 244*4c06356bSdh pwp->oqp_dma_attr.dma_attr_addr_hi = 0x000000FFFFFFFFFFull; 245*4c06356bSdh 246*4c06356bSdh for (i = 0; i < PMCS_NIQ; i++) { 247*4c06356bSdh if (pmcs_dma_setup(pwp, &pwp->iqp_dma_attr, 248*4c06356bSdh &pwp->iqp_acchdls[i], 249*4c06356bSdh &pwp->iqp_handles[i], PMCS_QENTRY_SIZE * pwp->ioq_depth, 250*4c06356bSdh (caddr_t *)&pwp->iqp[i], &pwp->iqaddr[i]) == B_FALSE) { 251*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 252*4c06356bSdh "Failed to setup DMA for iqp[%d]", i); 253*4c06356bSdh return (-1); 254*4c06356bSdh } 255*4c06356bSdh bzero(pwp->iqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth); 256*4c06356bSdh } 257*4c06356bSdh 258*4c06356bSdh for (i = 0; i < PMCS_NOQ; i++) { 259*4c06356bSdh if (pmcs_dma_setup(pwp, &pwp->oqp_dma_attr, 260*4c06356bSdh &pwp->oqp_acchdls[i], 261*4c06356bSdh &pwp->oqp_handles[i], PMCS_QENTRY_SIZE * pwp->ioq_depth, 262*4c06356bSdh (caddr_t *)&pwp->oqp[i], &pwp->oqaddr[i]) == B_FALSE) { 263*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 264*4c06356bSdh "Failed to setup DMA for oqp[%d]", i); 265*4c06356bSdh return (-1); 266*4c06356bSdh } 267*4c06356bSdh bzero(pwp->oqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth); 268*4c06356bSdh } 269*4c06356bSdh 270*4c06356bSdh /* 271*4c06356bSdh * Install the IQ and OQ addresses (and null out the rest). 272*4c06356bSdh */ 273*4c06356bSdh for (i = 0; i < pwp->max_iq; i++) { 274*4c06356bSdh pwp->iqpi_offset[i] = pmcs_rd_iqc_tbl(pwp, PMCS_IQPIOFFX(i)); 275*4c06356bSdh if (i < PMCS_NIQ) { 276*4c06356bSdh if (i != PMCS_IQ_OTHER) { 277*4c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 278*4c06356bSdh pwp->ioq_depth | (PMCS_QENTRY_SIZE << 16)); 279*4c06356bSdh } else { 280*4c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 281*4c06356bSdh (1 << 30) | pwp->ioq_depth | 282*4c06356bSdh (PMCS_QENTRY_SIZE << 16)); 283*4c06356bSdh } 284*4c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 285*4c06356bSdh DWORD1(pwp->iqaddr[i])); 286*4c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 287*4c06356bSdh DWORD0(pwp->iqaddr[i])); 288*4c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 289*4c06356bSdh DWORD1(pwp->ciaddr+IQ_OFFSET(i))); 290*4c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 291*4c06356bSdh DWORD0(pwp->ciaddr+IQ_OFFSET(i))); 292*4c06356bSdh } else { 293*4c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 0); 294*4c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 0); 295*4c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 0); 296*4c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 0); 297*4c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 0); 298*4c06356bSdh } 299*4c06356bSdh } 300*4c06356bSdh 301*4c06356bSdh for (i = 0; i < pwp->max_oq; i++) { 302*4c06356bSdh pwp->oqci_offset[i] = pmcs_rd_oqc_tbl(pwp, PMCS_OQCIOFFX(i)); 303*4c06356bSdh if (i < PMCS_NOQ) { 304*4c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), pwp->ioq_depth | 305*4c06356bSdh (PMCS_QENTRY_SIZE << 16) | OQIEX); 306*4c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 307*4c06356bSdh DWORD1(pwp->oqaddr[i])); 308*4c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 309*4c06356bSdh DWORD0(pwp->oqaddr[i])); 310*4c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 311*4c06356bSdh DWORD1(pwp->ciaddr+OQ_OFFSET(i))); 312*4c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 313*4c06356bSdh DWORD0(pwp->ciaddr+OQ_OFFSET(i))); 314*4c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 315*4c06356bSdh pwp->oqvec[i] << 24); 316*4c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0); 317*4c06356bSdh } else { 318*4c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), 0); 319*4c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 0); 320*4c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 0); 321*4c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 0); 322*4c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 0); 323*4c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 0); 324*4c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0); 325*4c06356bSdh } 326*4c06356bSdh } 327*4c06356bSdh 328*4c06356bSdh /* 329*4c06356bSdh * Set up logging, if defined. 330*4c06356bSdh */ 331*4c06356bSdh if (pwp->fwlog) { 332*4c06356bSdh uint64_t logdma = pwp->fwaddr; 333*4c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBAH, DWORD1(logdma)); 334*4c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBAL, DWORD0(logdma)); 335*4c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBS, PMCS_FWLOG_SIZE >> 1); 336*4c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELSEV, pwp->fwlog); 337*4c06356bSdh logdma += (PMCS_FWLOG_SIZE >> 1); 338*4c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBAH, DWORD1(logdma)); 339*4c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBAL, DWORD0(logdma)); 340*4c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBS, PMCS_FWLOG_SIZE >> 1); 341*4c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELSEV, pwp->fwlog); 342*4c06356bSdh } 343*4c06356bSdh 344*4c06356bSdh /* 345*4c06356bSdh * Interrupt vectors, outbound queues, and odb_auto_clear 346*4c06356bSdh * 347*4c06356bSdh * MSI/MSI-X: 348*4c06356bSdh * If we got 4 interrupt vectors, we'll assign one to each outbound 349*4c06356bSdh * queue as well as the fatal interrupt, and auto clear can be set 350*4c06356bSdh * for each. 351*4c06356bSdh * 352*4c06356bSdh * If we only got 2 vectors, one will be used for I/O completions 353*4c06356bSdh * and the other for the other two vectors. In this case, auto_ 354*4c06356bSdh * clear can only be set for I/Os, which is fine. The fatal 355*4c06356bSdh * interrupt will be mapped to the PMCS_FATAL_INTERRUPT bit, which 356*4c06356bSdh * is not an interrupt vector. 357*4c06356bSdh * 358*4c06356bSdh * MSI/MSI-X/INT-X: 359*4c06356bSdh * If we only got 1 interrupt vector, auto_clear must be set to 0, 360*4c06356bSdh * and again the fatal interrupt will be mapped to the 361*4c06356bSdh * PMCS_FATAL_INTERRUPT bit (again, not an interrupt vector). 362*4c06356bSdh */ 363*4c06356bSdh 364*4c06356bSdh switch (pwp->int_type) { 365*4c06356bSdh case PMCS_INT_MSIX: 366*4c06356bSdh case PMCS_INT_MSI: 367*4c06356bSdh switch (pwp->intr_cnt) { 368*4c06356bSdh case 1: 369*4c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE | 370*4c06356bSdh (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT)); 371*4c06356bSdh pwp->odb_auto_clear = 0; 372*4c06356bSdh break; 373*4c06356bSdh case 2: 374*4c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE | 375*4c06356bSdh (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT)); 376*4c06356bSdh pwp->odb_auto_clear = (1 << PMCS_FATAL_INTERRUPT) | 377*4c06356bSdh (1 << PMCS_MSIX_IODONE); 378*4c06356bSdh break; 379*4c06356bSdh case 4: 380*4c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE | 381*4c06356bSdh (PMCS_MSIX_FATAL << PMCS_FERIV_SHIFT)); 382*4c06356bSdh pwp->odb_auto_clear = (1 << PMCS_MSIX_FATAL) | 383*4c06356bSdh (1 << PMCS_MSIX_GENERAL) | (1 << PMCS_MSIX_IODONE) | 384*4c06356bSdh (1 << PMCS_MSIX_EVENTS); 385*4c06356bSdh break; 386*4c06356bSdh } 387*4c06356bSdh break; 388*4c06356bSdh 389*4c06356bSdh case PMCS_INT_FIXED: 390*4c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, 391*4c06356bSdh PMCS_FERRIE | (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT)); 392*4c06356bSdh pwp->odb_auto_clear = 0; 393*4c06356bSdh break; 394*4c06356bSdh } 395*4c06356bSdh 396*4c06356bSdh /* 397*4c06356bSdh * Enable Interrupt Reassertion 398*4c06356bSdh * Default Delay 1000us 399*4c06356bSdh */ 400*4c06356bSdh ferr = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_FERR); 401*4c06356bSdh if ((ferr & PMCS_MPI_IRAE) == 0) { 402*4c06356bSdh ferr &= ~(PMCS_MPI_IRAU | PMCS_MPI_IRAD_MASK); 403*4c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, ferr | PMCS_MPI_IRAE); 404*4c06356bSdh } 405*4c06356bSdh 406*4c06356bSdh pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR, pwp->odb_auto_clear); 407*4c06356bSdh pwp->mpi_table_setup = 1; 408*4c06356bSdh return (0); 409*4c06356bSdh } 410*4c06356bSdh 411*4c06356bSdh /* 412*4c06356bSdh * Start the Message Passing protocol with the PMC chip. 413*4c06356bSdh */ 414*4c06356bSdh int 415*4c06356bSdh pmcs_start_mpi(pmcs_hw_t *pwp) 416*4c06356bSdh { 417*4c06356bSdh int i; 418*4c06356bSdh 419*4c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_IBDB, PMCS_MSGU_IBDB_MPIINI); 420*4c06356bSdh for (i = 0; i < 1000; i++) { 421*4c06356bSdh if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & 422*4c06356bSdh PMCS_MSGU_IBDB_MPIINI) == 0) { 423*4c06356bSdh break; 424*4c06356bSdh } 425*4c06356bSdh drv_usecwait(1000); 426*4c06356bSdh } 427*4c06356bSdh if (pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & PMCS_MSGU_IBDB_MPIINI) { 428*4c06356bSdh return (-1); 429*4c06356bSdh } 430*4c06356bSdh drv_usecwait(500000); 431*4c06356bSdh 432*4c06356bSdh /* 433*4c06356bSdh * Check to make sure we got to INIT state. 434*4c06356bSdh */ 435*4c06356bSdh if (PMCS_MPI_S(pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE)) != 436*4c06356bSdh PMCS_MPI_STATE_INIT) { 437*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: MPI launch failed (GST 0x%x " 438*4c06356bSdh "DBCLR 0x%x)", __func__, 439*4c06356bSdh pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE), 440*4c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB_CLEAR)); 441*4c06356bSdh return (-1); 442*4c06356bSdh } 443*4c06356bSdh return (0); 444*4c06356bSdh } 445*4c06356bSdh 446*4c06356bSdh /* 447*4c06356bSdh * Stop the Message Passing protocol with the PMC chip. 448*4c06356bSdh */ 449*4c06356bSdh int 450*4c06356bSdh pmcs_stop_mpi(pmcs_hw_t *pwp) 451*4c06356bSdh { 452*4c06356bSdh int i; 453*4c06356bSdh 454*4c06356bSdh for (i = 0; i < pwp->max_iq; i++) { 455*4c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 0); 456*4c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 0); 457*4c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 0); 458*4c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 0); 459*4c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 0); 460*4c06356bSdh } 461*4c06356bSdh for (i = 0; i < pwp->max_oq; i++) { 462*4c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), 0); 463*4c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 0); 464*4c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 0); 465*4c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 0); 466*4c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 0); 467*4c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 0); 468*4c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0); 469*4c06356bSdh } 470*4c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, 0); 471*4c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_IBDB, PMCS_MSGU_IBDB_MPICTU); 472*4c06356bSdh for (i = 0; i < 2000; i++) { 473*4c06356bSdh if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & 474*4c06356bSdh PMCS_MSGU_IBDB_MPICTU) == 0) { 475*4c06356bSdh break; 476*4c06356bSdh } 477*4c06356bSdh drv_usecwait(1000); 478*4c06356bSdh } 479*4c06356bSdh if (pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & PMCS_MSGU_IBDB_MPICTU) { 480*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: MPI stop failed", __func__); 481*4c06356bSdh return (-1); 482*4c06356bSdh } 483*4c06356bSdh return (0); 484*4c06356bSdh } 485*4c06356bSdh 486*4c06356bSdh /* 487*4c06356bSdh * Do a sequence of ECHO messages to test for MPI functionality, 488*4c06356bSdh * all inbound and outbound queue functionality and interrupts. 489*4c06356bSdh */ 490*4c06356bSdh int 491*4c06356bSdh pmcs_echo_test(pmcs_hw_t *pwp) 492*4c06356bSdh { 493*4c06356bSdh echo_test_t fred; 494*4c06356bSdh struct pmcwork *pwrk; 495*4c06356bSdh uint32_t *msg, count; 496*4c06356bSdh int iqe = 0, iqo = 0, result, rval = 0; 497*4c06356bSdh int iterations; 498*4c06356bSdh hrtime_t echo_start, echo_end, echo_total; 499*4c06356bSdh 500*4c06356bSdh ASSERT(pwp->max_cmd > 0); 501*4c06356bSdh 502*4c06356bSdh /* 503*4c06356bSdh * We want iterations to be max_cmd * 3 to ensure that we run the 504*4c06356bSdh * echo test enough times to iterate through every inbound queue 505*4c06356bSdh * at least twice. 506*4c06356bSdh */ 507*4c06356bSdh iterations = pwp->max_cmd * 3; 508*4c06356bSdh 509*4c06356bSdh echo_total = 0; 510*4c06356bSdh count = 0; 511*4c06356bSdh 512*4c06356bSdh while (count < iterations) { 513*4c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL); 514*4c06356bSdh if (pwrk == NULL) { 515*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__); 516*4c06356bSdh rval = -1; 517*4c06356bSdh break; 518*4c06356bSdh } 519*4c06356bSdh 520*4c06356bSdh mutex_enter(&pwp->iqp_lock[iqe]); 521*4c06356bSdh msg = GET_IQ_ENTRY(pwp, iqe); 522*4c06356bSdh if (msg == NULL) { 523*4c06356bSdh mutex_exit(&pwp->iqp_lock[iqe]); 524*4c06356bSdh pmcs_pwork(pwp, pwrk); 525*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__); 526*4c06356bSdh rval = -1; 527*4c06356bSdh break; 528*4c06356bSdh } 529*4c06356bSdh 530*4c06356bSdh bzero(msg, PMCS_QENTRY_SIZE); 531*4c06356bSdh 532*4c06356bSdh if (iqe == PMCS_IQ_OTHER) { 533*4c06356bSdh /* This is on the high priority queue */ 534*4c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, iqo, PMCIN_ECHO)); 535*4c06356bSdh } else { 536*4c06356bSdh msg[0] = LE_32(PMCS_IOMB_IN_SAS(iqo, PMCIN_ECHO)); 537*4c06356bSdh } 538*4c06356bSdh msg[1] = LE_32(pwrk->htag); 539*4c06356bSdh fred.signature = 0xdeadbeef; 540*4c06356bSdh fred.count = count; 541*4c06356bSdh fred.ptr = &count; 542*4c06356bSdh (void) memcpy(&msg[2], &fred, sizeof (fred)); 543*4c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 544*4c06356bSdh 545*4c06356bSdh INC_IQ_ENTRY(pwp, iqe); 546*4c06356bSdh 547*4c06356bSdh echo_start = gethrtime(); 548*4c06356bSdh DTRACE_PROBE2(pmcs__echo__test__wait__start, 549*4c06356bSdh hrtime_t, echo_start, uint32_t, pwrk->htag); 550*4c06356bSdh 551*4c06356bSdh if (++iqe == PMCS_NIQ) { 552*4c06356bSdh iqe = 0; 553*4c06356bSdh } 554*4c06356bSdh if (++iqo == PMCS_NOQ) { 555*4c06356bSdh iqo = 0; 556*4c06356bSdh } 557*4c06356bSdh 558*4c06356bSdh WAIT_FOR(pwrk, 250, result); 559*4c06356bSdh 560*4c06356bSdh echo_end = gethrtime(); 561*4c06356bSdh DTRACE_PROBE2(pmcs__echo__test__wait__end, 562*4c06356bSdh hrtime_t, echo_end, int, result); 563*4c06356bSdh 564*4c06356bSdh echo_total += (echo_end - echo_start); 565*4c06356bSdh 566*4c06356bSdh pmcs_pwork(pwp, pwrk); 567*4c06356bSdh if (result) { 568*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 569*4c06356bSdh "%s: command timed out on echo test #%d", 570*4c06356bSdh __func__, count); 571*4c06356bSdh rval = -1; 572*4c06356bSdh break; 573*4c06356bSdh } 574*4c06356bSdh } 575*4c06356bSdh 576*4c06356bSdh /* 577*4c06356bSdh * The intr_threshold is adjusted by PMCS_INTR_THRESHOLD in order to 578*4c06356bSdh * remove the overhead of things like the delay in getting signaled 579*4c06356bSdh * for completion. 580*4c06356bSdh */ 581*4c06356bSdh if (echo_total != 0) { 582*4c06356bSdh pwp->io_intr_coal.intr_latency = 583*4c06356bSdh (echo_total / iterations) / 2; 584*4c06356bSdh pwp->io_intr_coal.intr_threshold = 585*4c06356bSdh PMCS_INTR_THRESHOLD(PMCS_QUANTUM_TIME_USECS * 1000 / 586*4c06356bSdh pwp->io_intr_coal.intr_latency); 587*4c06356bSdh } 588*4c06356bSdh 589*4c06356bSdh return (rval); 590*4c06356bSdh } 591*4c06356bSdh 592*4c06356bSdh /* 593*4c06356bSdh * Start the (real) phys 594*4c06356bSdh */ 595*4c06356bSdh int 596*4c06356bSdh pmcs_start_phy(pmcs_hw_t *pwp, int phynum, int linkmode, int speed) 597*4c06356bSdh { 598*4c06356bSdh int result; 599*4c06356bSdh uint32_t *msg; 600*4c06356bSdh struct pmcwork *pwrk; 601*4c06356bSdh pmcs_phy_t *pptr; 602*4c06356bSdh sas_identify_af_t sap; 603*4c06356bSdh 604*4c06356bSdh mutex_enter(&pwp->lock); 605*4c06356bSdh pptr = pwp->root_phys + phynum; 606*4c06356bSdh if (pptr == NULL) { 607*4c06356bSdh mutex_exit(&pwp->lock); 608*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: cannot find port %d", 609*4c06356bSdh __func__, phynum); 610*4c06356bSdh return (0); 611*4c06356bSdh } 612*4c06356bSdh 613*4c06356bSdh pmcs_lock_phy(pptr); 614*4c06356bSdh mutex_exit(&pwp->lock); 615*4c06356bSdh 616*4c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 617*4c06356bSdh if (pwrk == NULL) { 618*4c06356bSdh pmcs_unlock_phy(pptr); 619*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__); 620*4c06356bSdh return (-1); 621*4c06356bSdh } 622*4c06356bSdh 623*4c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 624*4c06356bSdh msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 625*4c06356bSdh 626*4c06356bSdh if (msg == NULL) { 627*4c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 628*4c06356bSdh pmcs_unlock_phy(pptr); 629*4c06356bSdh pmcs_pwork(pwp, pwrk); 630*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__); 631*4c06356bSdh return (-1); 632*4c06356bSdh } 633*4c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_PHY_START)); 634*4c06356bSdh msg[1] = LE_32(pwrk->htag); 635*4c06356bSdh msg[2] = LE_32(linkmode | speed | phynum); 636*4c06356bSdh bzero(&sap, sizeof (sap)); 637*4c06356bSdh sap.device_type = SAS_IF_DTYPE_ENDPOINT; 638*4c06356bSdh sap.ssp_ini_port = 1; 639*4c06356bSdh 640*4c06356bSdh if (pwp->separate_ports) { 641*4c06356bSdh pmcs_wwn2barray(pwp->sas_wwns[phynum], sap.sas_address); 642*4c06356bSdh } else { 643*4c06356bSdh pmcs_wwn2barray(pwp->sas_wwns[0], sap.sas_address); 644*4c06356bSdh } 645*4c06356bSdh 646*4c06356bSdh ASSERT(phynum < SAS2_PHYNUM_MAX); 647*4c06356bSdh sap.phy_identifier = phynum & SAS2_PHYNUM_MASK; 648*4c06356bSdh (void) memcpy(&msg[3], &sap, sizeof (sas_identify_af_t)); 649*4c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 650*4c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 651*4c06356bSdh 652*4c06356bSdh pptr->state.prog_min_rate = (lowbit((ulong_t)speed) - 1); 653*4c06356bSdh pptr->state.prog_max_rate = (highbit((ulong_t)speed) - 1); 654*4c06356bSdh pptr->state.hw_min_rate = PMCS_HW_MIN_LINK_RATE; 655*4c06356bSdh pptr->state.hw_max_rate = PMCS_HW_MAX_LINK_RATE; 656*4c06356bSdh 657*4c06356bSdh pmcs_unlock_phy(pptr); 658*4c06356bSdh WAIT_FOR(pwrk, 1000, result); 659*4c06356bSdh pmcs_pwork(pwp, pwrk); 660*4c06356bSdh 661*4c06356bSdh if (result) { 662*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_timeo, __func__); 663*4c06356bSdh } else { 664*4c06356bSdh mutex_enter(&pwp->lock); 665*4c06356bSdh pwp->phys_started |= (1 << phynum); 666*4c06356bSdh mutex_exit(&pwp->lock); 667*4c06356bSdh } 668*4c06356bSdh 669*4c06356bSdh return (0); 670*4c06356bSdh } 671*4c06356bSdh 672*4c06356bSdh int 673*4c06356bSdh pmcs_start_phys(pmcs_hw_t *pwp) 674*4c06356bSdh { 675*4c06356bSdh int i; 676*4c06356bSdh 677*4c06356bSdh for (i = 0; i < pwp->nphy; i++) { 678*4c06356bSdh if ((pwp->phyid_block_mask & (1 << i)) == 0) { 679*4c06356bSdh if (pmcs_start_phy(pwp, i, 680*4c06356bSdh (pwp->phymode << PHY_MODE_SHIFT), 681*4c06356bSdh pwp->physpeed << PHY_LINK_SHIFT)) { 682*4c06356bSdh return (-1); 683*4c06356bSdh } 684*4c06356bSdh if (pmcs_clear_diag_counters(pwp, i)) { 685*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: failed to " 686*4c06356bSdh "reset counters on PHY (%d)", __func__, i); 687*4c06356bSdh } 688*4c06356bSdh } 689*4c06356bSdh } 690*4c06356bSdh return (0); 691*4c06356bSdh } 692*4c06356bSdh 693*4c06356bSdh /* 694*4c06356bSdh * Called with PHY locked 695*4c06356bSdh */ 696*4c06356bSdh int 697*4c06356bSdh pmcs_reset_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint8_t type) 698*4c06356bSdh { 699*4c06356bSdh uint32_t *msg; 700*4c06356bSdh uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2]; 701*4c06356bSdh const char *mbar; 702*4c06356bSdh uint32_t amt; 703*4c06356bSdh uint32_t pdevid; 704*4c06356bSdh uint32_t stsoff; 705*4c06356bSdh uint32_t status; 706*4c06356bSdh int result, level, phynum; 707*4c06356bSdh struct pmcwork *pwrk; 708*4c06356bSdh uint32_t htag; 709*4c06356bSdh 710*4c06356bSdh ASSERT(mutex_owned(&pptr->phy_lock)); 711*4c06356bSdh 712*4c06356bSdh bzero(iomb, PMCS_QENTRY_SIZE); 713*4c06356bSdh phynum = pptr->phynum; 714*4c06356bSdh level = pptr->level; 715*4c06356bSdh if (level > 0) { 716*4c06356bSdh pdevid = pptr->parent->device_id; 717*4c06356bSdh } 718*4c06356bSdh 719*4c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 720*4c06356bSdh 721*4c06356bSdh if (pwrk == NULL) { 722*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__); 723*4c06356bSdh return (ENOMEM); 724*4c06356bSdh } 725*4c06356bSdh 726*4c06356bSdh pwrk->arg = iomb; 727*4c06356bSdh 728*4c06356bSdh /* 729*4c06356bSdh * If level > 0, we need to issue an SMP_REQUEST with a PHY_CONTROL 730*4c06356bSdh * function to do either a link reset or hard reset. If level == 0, 731*4c06356bSdh * then we do a LOCAL_PHY_CONTROL IOMB to do link/hard reset to the 732*4c06356bSdh * root (local) PHY 733*4c06356bSdh */ 734*4c06356bSdh if (level) { 735*4c06356bSdh stsoff = 2; 736*4c06356bSdh iomb[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 737*4c06356bSdh PMCIN_SMP_REQUEST)); 738*4c06356bSdh iomb[1] = LE_32(pwrk->htag); 739*4c06356bSdh iomb[2] = LE_32(pdevid); 740*4c06356bSdh iomb[3] = LE_32(40 << SMP_REQUEST_LENGTH_SHIFT); 741*4c06356bSdh /* 742*4c06356bSdh * Send SMP PHY CONTROL/HARD or LINK RESET 743*4c06356bSdh */ 744*4c06356bSdh iomb[4] = BE_32(0x40910000); 745*4c06356bSdh iomb[5] = 0; 746*4c06356bSdh 747*4c06356bSdh if (type == PMCS_PHYOP_HARD_RESET) { 748*4c06356bSdh mbar = "SMP PHY CONTROL/HARD RESET"; 749*4c06356bSdh iomb[6] = BE_32((phynum << 24) | 750*4c06356bSdh (PMCS_PHYOP_HARD_RESET << 16)); 751*4c06356bSdh } else { 752*4c06356bSdh mbar = "SMP PHY CONTROL/LINK RESET"; 753*4c06356bSdh iomb[6] = BE_32((phynum << 24) | 754*4c06356bSdh (PMCS_PHYOP_LINK_RESET << 16)); 755*4c06356bSdh } 756*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 757*4c06356bSdh "%s: sending %s to %s for phy 0x%x", 758*4c06356bSdh __func__, mbar, pptr->parent->path, pptr->phynum); 759*4c06356bSdh amt = 7; 760*4c06356bSdh } else { 761*4c06356bSdh /* 762*4c06356bSdh * Unlike most other Outbound messages, status for 763*4c06356bSdh * a local phy operation is in DWORD 3. 764*4c06356bSdh */ 765*4c06356bSdh stsoff = 3; 766*4c06356bSdh iomb[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 767*4c06356bSdh PMCIN_LOCAL_PHY_CONTROL)); 768*4c06356bSdh iomb[1] = LE_32(pwrk->htag); 769*4c06356bSdh if (type == PMCS_PHYOP_LINK_RESET) { 770*4c06356bSdh mbar = "LOCAL PHY LINK RESET"; 771*4c06356bSdh iomb[2] = LE_32((PMCS_PHYOP_LINK_RESET << 8) | phynum); 772*4c06356bSdh } else { 773*4c06356bSdh mbar = "LOCAL PHY HARD RESET"; 774*4c06356bSdh iomb[2] = LE_32((PMCS_PHYOP_HARD_RESET << 8) | phynum); 775*4c06356bSdh } 776*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 777*4c06356bSdh "%s: sending %s to %s", __func__, mbar, pptr->path); 778*4c06356bSdh amt = 3; 779*4c06356bSdh } 780*4c06356bSdh 781*4c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 782*4c06356bSdh msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 783*4c06356bSdh if (msg == NULL) { 784*4c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 785*4c06356bSdh pmcs_pwork(pwp, pwrk); 786*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__); 787*4c06356bSdh return (ENOMEM); 788*4c06356bSdh } 789*4c06356bSdh COPY_MESSAGE(msg, iomb, amt); 790*4c06356bSdh htag = pwrk->htag; 791*4c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 792*4c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 793*4c06356bSdh 794*4c06356bSdh pmcs_unlock_phy(pptr); 795*4c06356bSdh WAIT_FOR(pwrk, 1000, result); 796*4c06356bSdh pmcs_pwork(pwp, pwrk); 797*4c06356bSdh pmcs_lock_phy(pptr); 798*4c06356bSdh 799*4c06356bSdh if (result) { 800*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_timeo, __func__); 801*4c06356bSdh 802*4c06356bSdh if (pmcs_abort(pwp, pptr, htag, 0, 0)) { 803*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 804*4c06356bSdh "%s: Unable to issue SMP abort for htag 0x%08x", 805*4c06356bSdh __func__, htag); 806*4c06356bSdh } else { 807*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 808*4c06356bSdh "%s: Issuing SMP ABORT for htag 0x%08x", 809*4c06356bSdh __func__, htag); 810*4c06356bSdh } 811*4c06356bSdh return (EIO); 812*4c06356bSdh } 813*4c06356bSdh status = LE_32(iomb[stsoff]); 814*4c06356bSdh 815*4c06356bSdh if (status != PMCOUT_STATUS_OK) { 816*4c06356bSdh char buf[32]; 817*4c06356bSdh const char *es = pmcs_status_str(status); 818*4c06356bSdh if (es == NULL) { 819*4c06356bSdh (void) snprintf(buf, sizeof (buf), "Status 0x%x", 820*4c06356bSdh status); 821*4c06356bSdh es = buf; 822*4c06356bSdh } 823*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 824*4c06356bSdh "%s: %s action returned %s for %s", __func__, mbar, es, 825*4c06356bSdh pptr->path); 826*4c06356bSdh return (EIO); 827*4c06356bSdh } 828*4c06356bSdh 829*4c06356bSdh return (0); 830*4c06356bSdh } 831*4c06356bSdh 832*4c06356bSdh /* 833*4c06356bSdh * Stop the (real) phys. No PHY or softstate locks are required as this only 834*4c06356bSdh * happens during detach. 835*4c06356bSdh */ 836*4c06356bSdh void 837*4c06356bSdh pmcs_stop_phy(pmcs_hw_t *pwp, int phynum) 838*4c06356bSdh { 839*4c06356bSdh int result; 840*4c06356bSdh pmcs_phy_t *pptr; 841*4c06356bSdh uint32_t *msg; 842*4c06356bSdh struct pmcwork *pwrk; 843*4c06356bSdh 844*4c06356bSdh pptr = pwp->root_phys + phynum; 845*4c06356bSdh if (pptr == NULL) { 846*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 847*4c06356bSdh "%s: unable to find port %d", __func__, phynum); 848*4c06356bSdh return; 849*4c06356bSdh } 850*4c06356bSdh 851*4c06356bSdh if (pwp->phys_started & (1 << phynum)) { 852*4c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 853*4c06356bSdh 854*4c06356bSdh if (pwrk == NULL) { 855*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__); 856*4c06356bSdh return; 857*4c06356bSdh } 858*4c06356bSdh 859*4c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 860*4c06356bSdh msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 861*4c06356bSdh 862*4c06356bSdh if (msg == NULL) { 863*4c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 864*4c06356bSdh pmcs_pwork(pwp, pwrk); 865*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__); 866*4c06356bSdh return; 867*4c06356bSdh } 868*4c06356bSdh 869*4c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_PHY_STOP)); 870*4c06356bSdh msg[1] = LE_32(pwrk->htag); 871*4c06356bSdh msg[2] = LE_32(phynum); 872*4c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 873*4c06356bSdh /* 874*4c06356bSdh * Make this unconfigured now. 875*4c06356bSdh */ 876*4c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 877*4c06356bSdh WAIT_FOR(pwrk, 1000, result); 878*4c06356bSdh 879*4c06356bSdh pmcs_pwork(pwp, pwrk); 880*4c06356bSdh if (result) { 881*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_timeo, __func__); 882*4c06356bSdh } 883*4c06356bSdh 884*4c06356bSdh pwp->phys_started &= ~(1 << phynum); 885*4c06356bSdh } 886*4c06356bSdh 887*4c06356bSdh pptr->configured = 0; 888*4c06356bSdh } 889*4c06356bSdh 890*4c06356bSdh /* 891*4c06356bSdh * No locks should be required as this is only called during detach 892*4c06356bSdh */ 893*4c06356bSdh void 894*4c06356bSdh pmcs_stop_phys(pmcs_hw_t *pwp) 895*4c06356bSdh { 896*4c06356bSdh int i; 897*4c06356bSdh for (i = 0; i < pwp->nphy; i++) { 898*4c06356bSdh if ((pwp->phyid_block_mask & (1 << i)) == 0) { 899*4c06356bSdh pmcs_stop_phy(pwp, i); 900*4c06356bSdh } 901*4c06356bSdh } 902*4c06356bSdh } 903*4c06356bSdh 904*4c06356bSdh /* 905*4c06356bSdh * Run SAS_DIAG_EXECUTE with cmd and cmd_desc passed. 906*4c06356bSdh * ERR_CNT_RESET: return status of cmd 907*4c06356bSdh * DIAG_REPORT_GET: return value of the counter 908*4c06356bSdh */ 909*4c06356bSdh int 910*4c06356bSdh pmcs_sas_diag_execute(pmcs_hw_t *pwp, uint32_t cmd, uint32_t cmd_desc, 911*4c06356bSdh uint8_t phynum) 912*4c06356bSdh { 913*4c06356bSdh uint32_t htag, *ptr, status, msg[PMCS_MSG_SIZE << 1]; 914*4c06356bSdh int result; 915*4c06356bSdh struct pmcwork *pwrk; 916*4c06356bSdh 917*4c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL); 918*4c06356bSdh if (pwrk == NULL) { 919*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__); 920*4c06356bSdh return (DDI_FAILURE); 921*4c06356bSdh } 922*4c06356bSdh pwrk->arg = msg; 923*4c06356bSdh htag = pwrk->htag; 924*4c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_SAS_DIAG_EXECUTE)); 925*4c06356bSdh msg[1] = LE_32(htag); 926*4c06356bSdh msg[2] = LE_32((cmd << PMCS_DIAG_CMD_SHIFT) | 927*4c06356bSdh (cmd_desc << PMCS_DIAG_CMD_DESC_SHIFT) | phynum); 928*4c06356bSdh 929*4c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 930*4c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 931*4c06356bSdh if (ptr == NULL) { 932*4c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 933*4c06356bSdh pmcs_pwork(pwp, pwrk); 934*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__); 935*4c06356bSdh return (DDI_FAILURE); 936*4c06356bSdh } 937*4c06356bSdh COPY_MESSAGE(ptr, msg, 3); 938*4c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 939*4c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 940*4c06356bSdh 941*4c06356bSdh WAIT_FOR(pwrk, 1000, result); 942*4c06356bSdh 943*4c06356bSdh pmcs_pwork(pwp, pwrk); 944*4c06356bSdh 945*4c06356bSdh if (result) { 946*4c06356bSdh pmcs_timed_out(pwp, htag, __func__); 947*4c06356bSdh return (DDI_FAILURE); 948*4c06356bSdh } 949*4c06356bSdh 950*4c06356bSdh status = LE_32(msg[3]); 951*4c06356bSdh 952*4c06356bSdh /* Return for counter reset */ 953*4c06356bSdh if (cmd == PMCS_ERR_CNT_RESET) 954*4c06356bSdh return (status); 955*4c06356bSdh 956*4c06356bSdh /* Return for counter value */ 957*4c06356bSdh if (status) { 958*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: failed, status (0x%x)", 959*4c06356bSdh __func__, status); 960*4c06356bSdh return (DDI_FAILURE); 961*4c06356bSdh } 962*4c06356bSdh return (LE_32(msg[4])); 963*4c06356bSdh } 964*4c06356bSdh 965*4c06356bSdh /* Get the current value of the counter for desc on phynum and return it. */ 966*4c06356bSdh int 967*4c06356bSdh pmcs_get_diag_report(pmcs_hw_t *pwp, uint32_t desc, uint8_t phynum) 968*4c06356bSdh { 969*4c06356bSdh return (pmcs_sas_diag_execute(pwp, PMCS_DIAG_REPORT_GET, desc, phynum)); 970*4c06356bSdh } 971*4c06356bSdh 972*4c06356bSdh /* Clear all of the counters for phynum. Returns the status of the command. */ 973*4c06356bSdh int 974*4c06356bSdh pmcs_clear_diag_counters(pmcs_hw_t *pwp, uint8_t phynum) 975*4c06356bSdh { 976*4c06356bSdh uint32_t cmd = PMCS_ERR_CNT_RESET; 977*4c06356bSdh uint32_t cmd_desc; 978*4c06356bSdh 979*4c06356bSdh cmd_desc = PMCS_INVALID_DWORD_CNT; 980*4c06356bSdh if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum)) 981*4c06356bSdh return (DDI_FAILURE); 982*4c06356bSdh 983*4c06356bSdh cmd_desc = PMCS_DISPARITY_ERR_CNT; 984*4c06356bSdh if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum)) 985*4c06356bSdh return (DDI_FAILURE); 986*4c06356bSdh 987*4c06356bSdh cmd_desc = PMCS_LOST_DWORD_SYNC_CNT; 988*4c06356bSdh if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum)) 989*4c06356bSdh return (DDI_FAILURE); 990*4c06356bSdh 991*4c06356bSdh cmd_desc = PMCS_RESET_FAILED_CNT; 992*4c06356bSdh if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum)) 993*4c06356bSdh return (DDI_FAILURE); 994*4c06356bSdh 995*4c06356bSdh return (DDI_SUCCESS); 996*4c06356bSdh } 997*4c06356bSdh 998*4c06356bSdh /* 999*4c06356bSdh * Get firmware timestamp 1000*4c06356bSdh */ 1001*4c06356bSdh int 1002*4c06356bSdh pmcs_get_time_stamp(pmcs_hw_t *pwp, uint64_t *ts) 1003*4c06356bSdh { 1004*4c06356bSdh uint32_t htag, *ptr, msg[PMCS_MSG_SIZE << 1]; 1005*4c06356bSdh int result; 1006*4c06356bSdh struct pmcwork *pwrk; 1007*4c06356bSdh 1008*4c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL); 1009*4c06356bSdh if (pwrk == NULL) { 1010*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__); 1011*4c06356bSdh return (-1); 1012*4c06356bSdh } 1013*4c06356bSdh pwrk->arg = msg; 1014*4c06356bSdh htag = pwrk->htag; 1015*4c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_GET_TIME_STAMP)); 1016*4c06356bSdh msg[1] = LE_32(pwrk->htag); 1017*4c06356bSdh 1018*4c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 1019*4c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 1020*4c06356bSdh if (ptr == NULL) { 1021*4c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 1022*4c06356bSdh pmcs_pwork(pwp, pwrk); 1023*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__); 1024*4c06356bSdh return (-1); 1025*4c06356bSdh } 1026*4c06356bSdh COPY_MESSAGE(ptr, msg, 2); 1027*4c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 1028*4c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 1029*4c06356bSdh 1030*4c06356bSdh WAIT_FOR(pwrk, 1000, result); 1031*4c06356bSdh 1032*4c06356bSdh pmcs_pwork(pwp, pwrk); 1033*4c06356bSdh 1034*4c06356bSdh if (result) { 1035*4c06356bSdh pmcs_timed_out(pwp, htag, __func__); 1036*4c06356bSdh return (-1); 1037*4c06356bSdh } 1038*4c06356bSdh *ts = LE_32(msg[2]) | (((uint64_t)LE_32(msg[3])) << 32); 1039*4c06356bSdh return (0); 1040*4c06356bSdh } 1041*4c06356bSdh 1042*4c06356bSdh /* 1043*4c06356bSdh * Dump all pertinent registers 1044*4c06356bSdh */ 1045*4c06356bSdh 1046*4c06356bSdh void 1047*4c06356bSdh pmcs_register_dump(pmcs_hw_t *pwp) 1048*4c06356bSdh { 1049*4c06356bSdh int i; 1050*4c06356bSdh uint32_t val; 1051*4c06356bSdh 1052*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, "pmcs%d: Register dump start", 1053*4c06356bSdh ddi_get_instance(pwp->dip)); 1054*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, 1055*4c06356bSdh "OBDB (intr): 0x%08x (mask): 0x%08x (clear): 0x%08x", 1056*4c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB), 1057*4c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB_MASK), 1058*4c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR)); 1059*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, "SCRATCH0: 0x%08x", 1060*4c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH0)); 1061*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, "SCRATCH1: 0x%08x", 1062*4c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1)); 1063*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, "SCRATCH2: 0x%08x", 1064*4c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2)); 1065*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, "SCRATCH3: 0x%08x", 1066*4c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH3)); 1067*4c06356bSdh for (i = 0; i < PMCS_NIQ; i++) { 1068*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, "IQ %d: CI %u PI %u", 1069*4c06356bSdh i, pmcs_rd_iqci(pwp, i), pmcs_rd_iqpi(pwp, i)); 1070*4c06356bSdh } 1071*4c06356bSdh for (i = 0; i < PMCS_NOQ; i++) { 1072*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, "OQ %d: CI %u PI %u", 1073*4c06356bSdh i, pmcs_rd_oqci(pwp, i), pmcs_rd_oqpi(pwp, i)); 1074*4c06356bSdh } 1075*4c06356bSdh val = pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE); 1076*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, 1077*4c06356bSdh "GST TABLE BASE: 0x%08x (STATE=0x%x QF=%d GSTLEN=%d HMI_ERR=0x%x)", 1078*4c06356bSdh val, PMCS_MPI_S(val), PMCS_QF(val), PMCS_GSTLEN(val) * 4, 1079*4c06356bSdh PMCS_HMI_ERR(val)); 1080*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, "GST TABLE IQFRZ0: 0x%08x", 1081*4c06356bSdh pmcs_rd_gst_tbl(pwp, PMCS_GST_IQFRZ0)); 1082*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, "GST TABLE IQFRZ1: 0x%08x", 1083*4c06356bSdh pmcs_rd_gst_tbl(pwp, PMCS_GST_IQFRZ1)); 1084*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, "GST TABLE MSGU TICK: 0x%08x", 1085*4c06356bSdh pmcs_rd_gst_tbl(pwp, PMCS_GST_MSGU_TICK)); 1086*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, "GST TABLE IOP TICK: 0x%08x", 1087*4c06356bSdh pmcs_rd_gst_tbl(pwp, PMCS_GST_IOP_TICK)); 1088*4c06356bSdh for (i = 0; i < pwp->nphy; i++) { 1089*4c06356bSdh uint32_t rerrf, pinfo, started = 0, link = 0; 1090*4c06356bSdh pinfo = pmcs_rd_gst_tbl(pwp, PMCS_GST_PHY_INFO(i)); 1091*4c06356bSdh if (pinfo & 1) { 1092*4c06356bSdh started = 1; 1093*4c06356bSdh link = pinfo & 2; 1094*4c06356bSdh } 1095*4c06356bSdh rerrf = pmcs_rd_gst_tbl(pwp, PMCS_GST_RERR_INFO(i)); 1096*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, 1097*4c06356bSdh "GST TABLE PHY%d STARTED=%d LINK=%d RERR=0x%08x", 1098*4c06356bSdh i, started, link, rerrf); 1099*4c06356bSdh } 1100*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, "pmcs%d: Register dump end", 1101*4c06356bSdh ddi_get_instance(pwp->dip)); 1102*4c06356bSdh } 1103*4c06356bSdh 1104*4c06356bSdh /* 1105*4c06356bSdh * Handle SATA Abort and other error processing 1106*4c06356bSdh */ 1107*4c06356bSdh int 1108*4c06356bSdh pmcs_abort_handler(pmcs_hw_t *pwp) 1109*4c06356bSdh { 1110*4c06356bSdh pmcs_phy_t *pptr, *pnext, *pnext_uplevel[PMCS_MAX_XPND]; 1111*4c06356bSdh int r, level = 0; 1112*4c06356bSdh 1113*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s", __func__); 1114*4c06356bSdh 1115*4c06356bSdh mutex_enter(&pwp->lock); 1116*4c06356bSdh pptr = pwp->root_phys; 1117*4c06356bSdh mutex_exit(&pwp->lock); 1118*4c06356bSdh 1119*4c06356bSdh while (pptr) { 1120*4c06356bSdh /* 1121*4c06356bSdh * XXX: Need to make sure this doesn't happen 1122*4c06356bSdh * XXX: when non-NCQ commands are running. 1123*4c06356bSdh */ 1124*4c06356bSdh pmcs_lock_phy(pptr); 1125*4c06356bSdh if (pptr->need_rl_ext) { 1126*4c06356bSdh ASSERT(pptr->dtype == SATA); 1127*4c06356bSdh if (pmcs_acquire_scratch(pwp, B_FALSE)) { 1128*4c06356bSdh goto next_phy; 1129*4c06356bSdh } 1130*4c06356bSdh r = pmcs_sata_abort_ncq(pwp, pptr); 1131*4c06356bSdh pmcs_release_scratch(pwp); 1132*4c06356bSdh if (r == ENOMEM) { 1133*4c06356bSdh goto next_phy; 1134*4c06356bSdh } 1135*4c06356bSdh if (r) { 1136*4c06356bSdh r = pmcs_reset_phy(pwp, pptr, 1137*4c06356bSdh PMCS_PHYOP_LINK_RESET); 1138*4c06356bSdh if (r == ENOMEM) { 1139*4c06356bSdh goto next_phy; 1140*4c06356bSdh } 1141*4c06356bSdh /* what if other failures happened? */ 1142*4c06356bSdh pptr->abort_pending = 1; 1143*4c06356bSdh pptr->abort_sent = 0; 1144*4c06356bSdh } 1145*4c06356bSdh } 1146*4c06356bSdh if (pptr->abort_pending == 0 || pptr->abort_sent) { 1147*4c06356bSdh goto next_phy; 1148*4c06356bSdh } 1149*4c06356bSdh pptr->abort_pending = 0; 1150*4c06356bSdh if (pmcs_abort(pwp, pptr, pptr->device_id, 1, 1) == ENOMEM) { 1151*4c06356bSdh pptr->abort_pending = 1; 1152*4c06356bSdh goto next_phy; 1153*4c06356bSdh } 1154*4c06356bSdh pptr->abort_sent = 1; 1155*4c06356bSdh 1156*4c06356bSdh next_phy: 1157*4c06356bSdh if (pptr->children) { 1158*4c06356bSdh pnext = pptr->children; 1159*4c06356bSdh pnext_uplevel[level++] = pptr->sibling; 1160*4c06356bSdh } else { 1161*4c06356bSdh pnext = pptr->sibling; 1162*4c06356bSdh while ((pnext == NULL) && (level > 0)) { 1163*4c06356bSdh pnext = pnext_uplevel[--level]; 1164*4c06356bSdh } 1165*4c06356bSdh } 1166*4c06356bSdh 1167*4c06356bSdh pmcs_unlock_phy(pptr); 1168*4c06356bSdh pptr = pnext; 1169*4c06356bSdh } 1170*4c06356bSdh 1171*4c06356bSdh return (0); 1172*4c06356bSdh } 1173*4c06356bSdh 1174*4c06356bSdh /* 1175*4c06356bSdh * Register a device (get a device handle for it). 1176*4c06356bSdh * Called with PHY lock held. 1177*4c06356bSdh */ 1178*4c06356bSdh int 1179*4c06356bSdh pmcs_register_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 1180*4c06356bSdh { 1181*4c06356bSdh struct pmcwork *pwrk; 1182*4c06356bSdh int result = 0; 1183*4c06356bSdh uint32_t *msg; 1184*4c06356bSdh uint32_t tmp, status; 1185*4c06356bSdh uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2]; 1186*4c06356bSdh 1187*4c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 1188*4c06356bSdh msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 1189*4c06356bSdh 1190*4c06356bSdh if (msg == NULL || 1191*4c06356bSdh (pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr)) == NULL) { 1192*4c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 1193*4c06356bSdh result = ENOMEM; 1194*4c06356bSdh goto out; 1195*4c06356bSdh } 1196*4c06356bSdh 1197*4c06356bSdh pwrk->arg = iomb; 1198*4c06356bSdh pwrk->dtype = pptr->dtype; 1199*4c06356bSdh 1200*4c06356bSdh msg[1] = LE_32(pwrk->htag); 1201*4c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_REGISTER_DEVICE)); 1202*4c06356bSdh tmp = PMCS_DEVREG_TLR | 1203*4c06356bSdh (pptr->link_rate << PMCS_DEVREG_LINK_RATE_SHIFT); 1204*4c06356bSdh if (IS_ROOT_PHY(pptr)) { 1205*4c06356bSdh msg[2] = LE_32(pptr->portid | 1206*4c06356bSdh (pptr->phynum << PMCS_PHYID_SHIFT)); 1207*4c06356bSdh } else { 1208*4c06356bSdh msg[2] = LE_32(pptr->portid); 1209*4c06356bSdh } 1210*4c06356bSdh if (pptr->dtype == SATA) { 1211*4c06356bSdh if (IS_ROOT_PHY(pptr)) { 1212*4c06356bSdh tmp |= PMCS_DEVREG_TYPE_SATA_DIRECT; 1213*4c06356bSdh } else { 1214*4c06356bSdh tmp |= PMCS_DEVREG_TYPE_SATA; 1215*4c06356bSdh } 1216*4c06356bSdh } else { 1217*4c06356bSdh tmp |= PMCS_DEVREG_TYPE_SAS; 1218*4c06356bSdh } 1219*4c06356bSdh msg[3] = LE_32(tmp); 1220*4c06356bSdh msg[4] = LE_32(PMCS_DEVREG_IT_NEXUS_TIMEOUT); 1221*4c06356bSdh (void) memcpy(&msg[5], pptr->sas_address, 8); 1222*4c06356bSdh 1223*4c06356bSdh CLEAN_MESSAGE(msg, 7); 1224*4c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 1225*4c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 1226*4c06356bSdh 1227*4c06356bSdh pmcs_unlock_phy(pptr); 1228*4c06356bSdh WAIT_FOR(pwrk, 250, result); 1229*4c06356bSdh pmcs_lock_phy(pptr); 1230*4c06356bSdh pmcs_pwork(pwp, pwrk); 1231*4c06356bSdh 1232*4c06356bSdh if (result) { 1233*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_timeo, __func__); 1234*4c06356bSdh result = ETIMEDOUT; 1235*4c06356bSdh goto out; 1236*4c06356bSdh } 1237*4c06356bSdh status = LE_32(iomb[2]); 1238*4c06356bSdh tmp = LE_32(iomb[3]); 1239*4c06356bSdh switch (status) { 1240*4c06356bSdh case PMCS_DEVREG_OK: 1241*4c06356bSdh case PMCS_DEVREG_DEVICE_ALREADY_REGISTERED: 1242*4c06356bSdh case PMCS_DEVREG_PHY_ALREADY_REGISTERED: 1243*4c06356bSdh if (pmcs_validate_devid(pwp->root_phys, pptr, tmp) == B_FALSE) { 1244*4c06356bSdh result = EEXIST; 1245*4c06356bSdh goto out; 1246*4c06356bSdh } else if (status != PMCS_DEVREG_OK) { 1247*4c06356bSdh if (tmp == 0xffffffff) { /* F/W bug */ 1248*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, 1249*4c06356bSdh "%s: phy %s already has bogus devid 0x%x", 1250*4c06356bSdh __func__, pptr->path, tmp); 1251*4c06356bSdh result = EIO; 1252*4c06356bSdh goto out; 1253*4c06356bSdh } else { 1254*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, 1255*4c06356bSdh "%s: phy %s already has a device id 0x%x", 1256*4c06356bSdh __func__, pptr->path, tmp); 1257*4c06356bSdh } 1258*4c06356bSdh } 1259*4c06356bSdh break; 1260*4c06356bSdh default: 1261*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: status 0x%x when trying to " 1262*4c06356bSdh "register device %s", __func__, status, pptr->path); 1263*4c06356bSdh result = EIO; 1264*4c06356bSdh goto out; 1265*4c06356bSdh } 1266*4c06356bSdh pptr->device_id = tmp; 1267*4c06356bSdh pptr->valid_device_id = 1; 1268*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "Phy %s/" SAS_ADDR_FMT 1269*4c06356bSdh " registered with device_id 0x%x (portid %d)", pptr->path, 1270*4c06356bSdh SAS_ADDR_PRT(pptr->sas_address), tmp, pptr->portid); 1271*4c06356bSdh out: 1272*4c06356bSdh return (result); 1273*4c06356bSdh } 1274*4c06356bSdh 1275*4c06356bSdh /* 1276*4c06356bSdh * Deregister a device (remove a device handle). 1277*4c06356bSdh * Called with PHY locked. 1278*4c06356bSdh */ 1279*4c06356bSdh void 1280*4c06356bSdh pmcs_deregister_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 1281*4c06356bSdh { 1282*4c06356bSdh struct pmcwork *pwrk; 1283*4c06356bSdh uint32_t msg[PMCS_MSG_SIZE], *ptr, status; 1284*4c06356bSdh uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2]; 1285*4c06356bSdh int result; 1286*4c06356bSdh 1287*4c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 1288*4c06356bSdh if (pwrk == NULL) { 1289*4c06356bSdh return; 1290*4c06356bSdh } 1291*4c06356bSdh 1292*4c06356bSdh pwrk->arg = iomb; 1293*4c06356bSdh pwrk->dtype = pptr->dtype; 1294*4c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 1295*4c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 1296*4c06356bSdh if (ptr == NULL) { 1297*4c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 1298*4c06356bSdh pmcs_pwork(pwp, pwrk); 1299*4c06356bSdh return; 1300*4c06356bSdh } 1301*4c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 1302*4c06356bSdh PMCIN_DEREGISTER_DEVICE_HANDLE)); 1303*4c06356bSdh msg[1] = LE_32(pwrk->htag); 1304*4c06356bSdh msg[2] = LE_32(pptr->device_id); 1305*4c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 1306*4c06356bSdh COPY_MESSAGE(ptr, msg, 3); 1307*4c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 1308*4c06356bSdh 1309*4c06356bSdh pmcs_unlock_phy(pptr); 1310*4c06356bSdh WAIT_FOR(pwrk, 250, result); 1311*4c06356bSdh pmcs_pwork(pwp, pwrk); 1312*4c06356bSdh pmcs_lock_phy(pptr); 1313*4c06356bSdh 1314*4c06356bSdh if (result) { 1315*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_timeo, __func__); 1316*4c06356bSdh return; 1317*4c06356bSdh } 1318*4c06356bSdh status = LE_32(iomb[2]); 1319*4c06356bSdh if (status != PMCOUT_STATUS_OK) { 1320*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: status 0x%x when trying to " 1321*4c06356bSdh "deregister device %s", __func__, status, pptr->path); 1322*4c06356bSdh } else { 1323*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: device %s deregistered", 1324*4c06356bSdh __func__, pptr->path); 1325*4c06356bSdh pptr->valid_device_id = 0; 1326*4c06356bSdh pptr->device_id = PMCS_INVALID_DEVICE_ID; 1327*4c06356bSdh } 1328*4c06356bSdh } 1329*4c06356bSdh 1330*4c06356bSdh /* 1331*4c06356bSdh * Deregister all registered devices. 1332*4c06356bSdh */ 1333*4c06356bSdh void 1334*4c06356bSdh pmcs_deregister_devices(pmcs_hw_t *pwp, pmcs_phy_t *phyp) 1335*4c06356bSdh { 1336*4c06356bSdh /* 1337*4c06356bSdh * Start at the maximum level and walk back to level 0. This only 1338*4c06356bSdh * gets done during detach after all threads and timers have been 1339*4c06356bSdh * destroyed, so there's no need to hold the softstate or PHY lock. 1340*4c06356bSdh */ 1341*4c06356bSdh while (phyp) { 1342*4c06356bSdh if (phyp->children) { 1343*4c06356bSdh pmcs_deregister_devices(pwp, phyp->children); 1344*4c06356bSdh } 1345*4c06356bSdh if (phyp->valid_device_id) { 1346*4c06356bSdh pmcs_deregister_device(pwp, phyp); 1347*4c06356bSdh } 1348*4c06356bSdh phyp = phyp->sibling; 1349*4c06356bSdh } 1350*4c06356bSdh } 1351*4c06356bSdh 1352*4c06356bSdh /* 1353*4c06356bSdh * Perform a 'soft' reset on the PMC chip 1354*4c06356bSdh */ 1355*4c06356bSdh int 1356*4c06356bSdh pmcs_soft_reset(pmcs_hw_t *pwp, boolean_t no_restart) 1357*4c06356bSdh { 1358*4c06356bSdh uint32_t s2, sfrbits, gsm, rapchk, wapchk, wdpchk, spc, tsmode; 1359*4c06356bSdh pmcs_phy_t *pptr; 1360*4c06356bSdh char *msg = NULL; 1361*4c06356bSdh int i; 1362*4c06356bSdh 1363*4c06356bSdh /* 1364*4c06356bSdh * Disable interrupts 1365*4c06356bSdh */ 1366*4c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff); 1367*4c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff); 1368*4c06356bSdh 1369*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, "%s", __func__); 1370*4c06356bSdh 1371*4c06356bSdh if (pwp->locks_initted) { 1372*4c06356bSdh mutex_enter(&pwp->lock); 1373*4c06356bSdh } 1374*4c06356bSdh pwp->blocked = 1; 1375*4c06356bSdh 1376*4c06356bSdh /* 1377*4c06356bSdh * Step 1 1378*4c06356bSdh */ 1379*4c06356bSdh s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2); 1380*4c06356bSdh if ((s2 & PMCS_MSGU_HOST_SOFT_RESET_READY) == 0) { 1381*4c06356bSdh pmcs_wr_gsm_reg(pwp, RB6_ACCESS, RB6_NMI_SIGNATURE); 1382*4c06356bSdh pmcs_wr_gsm_reg(pwp, RB6_ACCESS, RB6_NMI_SIGNATURE); 1383*4c06356bSdh for (i = 0; i < 100; i++) { 1384*4c06356bSdh s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) & 1385*4c06356bSdh PMCS_MSGU_HOST_SOFT_RESET_READY; 1386*4c06356bSdh if (s2) { 1387*4c06356bSdh break; 1388*4c06356bSdh } 1389*4c06356bSdh drv_usecwait(10000); 1390*4c06356bSdh } 1391*4c06356bSdh s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) & 1392*4c06356bSdh PMCS_MSGU_HOST_SOFT_RESET_READY; 1393*4c06356bSdh if (s2 == 0) { 1394*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: PMCS_MSGU_HOST_" 1395*4c06356bSdh "SOFT_RESET_READY never came ready", __func__); 1396*4c06356bSdh pmcs_register_dump(pwp); 1397*4c06356bSdh if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) & 1398*4c06356bSdh PMCS_MSGU_CPU_SOFT_RESET_READY) == 0 || 1399*4c06356bSdh (pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) & 1400*4c06356bSdh PMCS_MSGU_CPU_SOFT_RESET_READY) == 0) { 1401*4c06356bSdh pwp->state = STATE_DEAD; 1402*4c06356bSdh pwp->blocked = 0; 1403*4c06356bSdh if (pwp->locks_initted) { 1404*4c06356bSdh mutex_exit(&pwp->lock); 1405*4c06356bSdh } 1406*4c06356bSdh return (-1); 1407*4c06356bSdh } 1408*4c06356bSdh } 1409*4c06356bSdh } 1410*4c06356bSdh 1411*4c06356bSdh /* 1412*4c06356bSdh * Step 2 1413*4c06356bSdh */ 1414*4c06356bSdh pmcs_wr_gsm_reg(pwp, NMI_EN_VPE0_IOP, 0); 1415*4c06356bSdh drv_usecwait(10); 1416*4c06356bSdh pmcs_wr_gsm_reg(pwp, NMI_EN_VPE0_AAP1, 0); 1417*4c06356bSdh drv_usecwait(10); 1418*4c06356bSdh pmcs_wr_topunit(pwp, PMCS_EVENT_INT_ENABLE, 0); 1419*4c06356bSdh drv_usecwait(10); 1420*4c06356bSdh pmcs_wr_topunit(pwp, PMCS_EVENT_INT_STAT, 1421*4c06356bSdh pmcs_rd_topunit(pwp, PMCS_EVENT_INT_STAT)); 1422*4c06356bSdh drv_usecwait(10); 1423*4c06356bSdh pmcs_wr_topunit(pwp, PMCS_ERROR_INT_ENABLE, 0); 1424*4c06356bSdh drv_usecwait(10); 1425*4c06356bSdh pmcs_wr_topunit(pwp, PMCS_ERROR_INT_STAT, 1426*4c06356bSdh pmcs_rd_topunit(pwp, PMCS_ERROR_INT_STAT)); 1427*4c06356bSdh drv_usecwait(10); 1428*4c06356bSdh 1429*4c06356bSdh sfrbits = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) & 1430*4c06356bSdh PMCS_MSGU_AAP_SFR_PROGRESS; 1431*4c06356bSdh sfrbits ^= PMCS_MSGU_AAP_SFR_PROGRESS; 1432*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, "PMCS_MSGU_HOST_SCRATCH0 %08x -> %08x", 1433*4c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH0), HST_SFT_RESET_SIG); 1434*4c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH0, HST_SFT_RESET_SIG); 1435*4c06356bSdh 1436*4c06356bSdh /* 1437*4c06356bSdh * Step 3 1438*4c06356bSdh */ 1439*4c06356bSdh gsm = pmcs_rd_gsm_reg(pwp, GSM_CFG_AND_RESET); 1440*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, "GSM %08x -> %08x", gsm, 1441*4c06356bSdh gsm & ~PMCS_SOFT_RESET_BITS); 1442*4c06356bSdh pmcs_wr_gsm_reg(pwp, GSM_CFG_AND_RESET, gsm & ~PMCS_SOFT_RESET_BITS); 1443*4c06356bSdh 1444*4c06356bSdh /* 1445*4c06356bSdh * Step 4 1446*4c06356bSdh */ 1447*4c06356bSdh rapchk = pmcs_rd_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN); 1448*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, "READ_ADR_PARITY_CHK_EN %08x -> %08x", 1449*4c06356bSdh rapchk, 0); 1450*4c06356bSdh pmcs_wr_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN, 0); 1451*4c06356bSdh wapchk = pmcs_rd_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN); 1452*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, "WRITE_ADR_PARITY_CHK_EN %08x -> %08x", 1453*4c06356bSdh wapchk, 0); 1454*4c06356bSdh pmcs_wr_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN, 0); 1455*4c06356bSdh wdpchk = pmcs_rd_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN); 1456*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, "WRITE_DATA_PARITY_CHK_EN %08x -> %08x", 1457*4c06356bSdh wdpchk, 0); 1458*4c06356bSdh pmcs_wr_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN, 0); 1459*4c06356bSdh 1460*4c06356bSdh /* 1461*4c06356bSdh * Step 5 1462*4c06356bSdh */ 1463*4c06356bSdh drv_usecwait(100); 1464*4c06356bSdh 1465*4c06356bSdh /* 1466*4c06356bSdh * Step 5.5 (Temporary workaround for 1.07.xx Beta) 1467*4c06356bSdh */ 1468*4c06356bSdh tsmode = pmcs_rd_gsm_reg(pwp, PMCS_GPIO_TRISTATE_MODE_ADDR); 1469*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, "GPIO TSMODE %08x -> %08x", tsmode, 1470*4c06356bSdh tsmode & ~(PMCS_GPIO_TSMODE_BIT0|PMCS_GPIO_TSMODE_BIT1)); 1471*4c06356bSdh pmcs_wr_gsm_reg(pwp, PMCS_GPIO_TRISTATE_MODE_ADDR, 1472*4c06356bSdh tsmode & ~(PMCS_GPIO_TSMODE_BIT0|PMCS_GPIO_TSMODE_BIT1)); 1473*4c06356bSdh drv_usecwait(10); 1474*4c06356bSdh 1475*4c06356bSdh /* 1476*4c06356bSdh * Step 6 1477*4c06356bSdh */ 1478*4c06356bSdh spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET); 1479*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, "SPC_RESET %08x -> %08x", spc, 1480*4c06356bSdh spc & ~(PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB)); 1481*4c06356bSdh pmcs_wr_topunit(pwp, PMCS_SPC_RESET, 1482*4c06356bSdh spc & ~(PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB)); 1483*4c06356bSdh drv_usecwait(10); 1484*4c06356bSdh 1485*4c06356bSdh /* 1486*4c06356bSdh * Step 7 1487*4c06356bSdh */ 1488*4c06356bSdh spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET); 1489*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, "SPC_RESET %08x -> %08x", spc, 1490*4c06356bSdh spc & ~(BDMA_CORE_RSTB|OSSP_RSTB)); 1491*4c06356bSdh pmcs_wr_topunit(pwp, PMCS_SPC_RESET, spc & ~(BDMA_CORE_RSTB|OSSP_RSTB)); 1492*4c06356bSdh 1493*4c06356bSdh /* 1494*4c06356bSdh * Step 8 1495*4c06356bSdh */ 1496*4c06356bSdh drv_usecwait(100); 1497*4c06356bSdh 1498*4c06356bSdh /* 1499*4c06356bSdh * Step 9 1500*4c06356bSdh */ 1501*4c06356bSdh spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET); 1502*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, "SPC_RESET %08x -> %08x", spc, 1503*4c06356bSdh spc | (BDMA_CORE_RSTB|OSSP_RSTB)); 1504*4c06356bSdh pmcs_wr_topunit(pwp, PMCS_SPC_RESET, spc | (BDMA_CORE_RSTB|OSSP_RSTB)); 1505*4c06356bSdh 1506*4c06356bSdh /* 1507*4c06356bSdh * Step 10 1508*4c06356bSdh */ 1509*4c06356bSdh drv_usecwait(100); 1510*4c06356bSdh 1511*4c06356bSdh /* 1512*4c06356bSdh * Step 11 1513*4c06356bSdh */ 1514*4c06356bSdh gsm = pmcs_rd_gsm_reg(pwp, GSM_CFG_AND_RESET); 1515*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, "GSM %08x -> %08x", gsm, 1516*4c06356bSdh gsm | PMCS_SOFT_RESET_BITS); 1517*4c06356bSdh pmcs_wr_gsm_reg(pwp, GSM_CFG_AND_RESET, gsm | PMCS_SOFT_RESET_BITS); 1518*4c06356bSdh drv_usecwait(10); 1519*4c06356bSdh 1520*4c06356bSdh /* 1521*4c06356bSdh * Step 12 1522*4c06356bSdh */ 1523*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, "READ_ADR_PARITY_CHK_EN %08x -> %08x", 1524*4c06356bSdh pmcs_rd_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN), rapchk); 1525*4c06356bSdh pmcs_wr_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN, rapchk); 1526*4c06356bSdh drv_usecwait(10); 1527*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, "WRITE_ADR_PARITY_CHK_EN %08x -> %08x", 1528*4c06356bSdh pmcs_rd_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN), wapchk); 1529*4c06356bSdh pmcs_wr_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN, wapchk); 1530*4c06356bSdh drv_usecwait(10); 1531*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, "WRITE_DATA_PARITY_CHK_EN %08x -> %08x", 1532*4c06356bSdh pmcs_rd_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN), wapchk); 1533*4c06356bSdh pmcs_wr_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN, wdpchk); 1534*4c06356bSdh drv_usecwait(10); 1535*4c06356bSdh 1536*4c06356bSdh /* 1537*4c06356bSdh * Step 13 1538*4c06356bSdh */ 1539*4c06356bSdh spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET); 1540*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, "SPC_RESET %08x -> %08x", spc, 1541*4c06356bSdh spc | (PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB)); 1542*4c06356bSdh pmcs_wr_topunit(pwp, PMCS_SPC_RESET, 1543*4c06356bSdh spc | (PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB)); 1544*4c06356bSdh 1545*4c06356bSdh /* 1546*4c06356bSdh * Step 14 1547*4c06356bSdh */ 1548*4c06356bSdh drv_usecwait(100); 1549*4c06356bSdh 1550*4c06356bSdh /* 1551*4c06356bSdh * Step 15 1552*4c06356bSdh */ 1553*4c06356bSdh for (spc = 0, i = 0; i < 1000; i++) { 1554*4c06356bSdh drv_usecwait(1000); 1555*4c06356bSdh spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1); 1556*4c06356bSdh if ((spc & PMCS_MSGU_AAP_SFR_PROGRESS) == sfrbits) { 1557*4c06356bSdh break; 1558*4c06356bSdh } 1559*4c06356bSdh } 1560*4c06356bSdh 1561*4c06356bSdh if ((spc & PMCS_MSGU_AAP_SFR_PROGRESS) != sfrbits) { 1562*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 1563*4c06356bSdh "SFR didn't toggle (sfr 0x%x)", spc); 1564*4c06356bSdh pwp->state = STATE_DEAD; 1565*4c06356bSdh pwp->blocked = 0; 1566*4c06356bSdh if (pwp->locks_initted) { 1567*4c06356bSdh mutex_exit(&pwp->lock); 1568*4c06356bSdh } 1569*4c06356bSdh return (-1); 1570*4c06356bSdh } 1571*4c06356bSdh 1572*4c06356bSdh /* 1573*4c06356bSdh * Step 16 1574*4c06356bSdh */ 1575*4c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff); 1576*4c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff); 1577*4c06356bSdh 1578*4c06356bSdh /* 1579*4c06356bSdh * Wait for up to 5 seconds for AAP state to come either ready or error. 1580*4c06356bSdh */ 1581*4c06356bSdh for (i = 0; i < 50; i++) { 1582*4c06356bSdh spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) & 1583*4c06356bSdh PMCS_MSGU_AAP_STATE_MASK; 1584*4c06356bSdh if (spc == PMCS_MSGU_AAP_STATE_ERROR || 1585*4c06356bSdh spc == PMCS_MSGU_AAP_STATE_READY) { 1586*4c06356bSdh break; 1587*4c06356bSdh } 1588*4c06356bSdh drv_usecwait(100000); 1589*4c06356bSdh } 1590*4c06356bSdh spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1); 1591*4c06356bSdh if ((spc & PMCS_MSGU_AAP_STATE_MASK) != PMCS_MSGU_AAP_STATE_READY) { 1592*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 1593*4c06356bSdh "soft reset failed (state 0x%x)", spc); 1594*4c06356bSdh pwp->state = STATE_DEAD; 1595*4c06356bSdh pwp->blocked = 0; 1596*4c06356bSdh if (pwp->locks_initted) { 1597*4c06356bSdh mutex_exit(&pwp->lock); 1598*4c06356bSdh } 1599*4c06356bSdh return (-1); 1600*4c06356bSdh } 1601*4c06356bSdh 1602*4c06356bSdh 1603*4c06356bSdh if (pwp->state == STATE_DEAD || pwp->state == STATE_UNPROBING || 1604*4c06356bSdh pwp->state == STATE_PROBING || pwp->locks_initted == 0) { 1605*4c06356bSdh pwp->blocked = 0; 1606*4c06356bSdh if (pwp->locks_initted) { 1607*4c06356bSdh mutex_exit(&pwp->lock); 1608*4c06356bSdh } 1609*4c06356bSdh return (0); 1610*4c06356bSdh } 1611*4c06356bSdh 1612*4c06356bSdh /* 1613*4c06356bSdh * Return at this point if we dont need to startup. 1614*4c06356bSdh */ 1615*4c06356bSdh if (no_restart) { 1616*4c06356bSdh return (0); 1617*4c06356bSdh } 1618*4c06356bSdh 1619*4c06356bSdh ASSERT(pwp->locks_initted != 0); 1620*4c06356bSdh 1621*4c06356bSdh /* 1622*4c06356bSdh * Clean up various soft state. 1623*4c06356bSdh */ 1624*4c06356bSdh bzero(pwp->ports, sizeof (pwp->ports)); 1625*4c06356bSdh 1626*4c06356bSdh pmcs_free_all_phys(pwp, pwp->root_phys); 1627*4c06356bSdh 1628*4c06356bSdh for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) { 1629*4c06356bSdh pmcs_lock_phy(pptr); 1630*4c06356bSdh pmcs_clear_phy(pwp, pptr); 1631*4c06356bSdh pmcs_unlock_phy(pptr); 1632*4c06356bSdh } 1633*4c06356bSdh 1634*4c06356bSdh if (pwp->targets) { 1635*4c06356bSdh for (i = 0; i < pwp->max_dev; i++) { 1636*4c06356bSdh pmcs_xscsi_t *xp = pwp->targets[i]; 1637*4c06356bSdh 1638*4c06356bSdh if (xp == NULL) { 1639*4c06356bSdh continue; 1640*4c06356bSdh } 1641*4c06356bSdh mutex_enter(&xp->statlock); 1642*4c06356bSdh if (xp->assigned == 0 && xp->dying == 0) { 1643*4c06356bSdh if (xp->new) { 1644*4c06356bSdh xp->new = 0; 1645*4c06356bSdh xp->ca = 0; 1646*4c06356bSdh xp->qdepth = 0; 1647*4c06356bSdh xp->phy = NULL; 1648*4c06356bSdh } 1649*4c06356bSdh mutex_exit(&xp->statlock); 1650*4c06356bSdh continue; 1651*4c06356bSdh } 1652*4c06356bSdh xp->tagmap = 0; 1653*4c06356bSdh xp->dying = 1; 1654*4c06356bSdh xp->assigned = 0; 1655*4c06356bSdh mutex_exit(&xp->statlock); 1656*4c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_REM_DEVICES); 1657*4c06356bSdh } 1658*4c06356bSdh } 1659*4c06356bSdh 1660*4c06356bSdh bzero(pwp->shadow_iqpi, sizeof (pwp->shadow_iqpi)); 1661*4c06356bSdh for (i = 0; i < PMCS_NIQ; i++) { 1662*4c06356bSdh if (pwp->iqp[i]) { 1663*4c06356bSdh bzero(pwp->iqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth); 1664*4c06356bSdh pmcs_wr_iqpi(pwp, i, 0); 1665*4c06356bSdh pmcs_wr_iqci(pwp, i, 0); 1666*4c06356bSdh } 1667*4c06356bSdh } 1668*4c06356bSdh for (i = 0; i < PMCS_NOQ; i++) { 1669*4c06356bSdh if (pwp->oqp[i]) { 1670*4c06356bSdh bzero(pwp->oqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth); 1671*4c06356bSdh pmcs_wr_oqpi(pwp, i, 0); 1672*4c06356bSdh pmcs_wr_oqci(pwp, i, 0); 1673*4c06356bSdh } 1674*4c06356bSdh 1675*4c06356bSdh } 1676*4c06356bSdh if (pwp->fwlogp) { 1677*4c06356bSdh bzero(pwp->fwlogp, PMCS_FWLOG_SIZE); 1678*4c06356bSdh } 1679*4c06356bSdh STAILQ_INIT(&pwp->wf); 1680*4c06356bSdh bzero(pwp->work, sizeof (pmcwork_t) * pwp->max_cmd); 1681*4c06356bSdh for (i = 0; i < pwp->max_cmd - 1; i++) { 1682*4c06356bSdh pmcwork_t *pwrk = &pwp->work[i]; 1683*4c06356bSdh STAILQ_INSERT_TAIL(&pwp->wf, pwrk, next); 1684*4c06356bSdh } 1685*4c06356bSdh 1686*4c06356bSdh /* 1687*4c06356bSdh * Clear out any leftover commands sitting in the work list 1688*4c06356bSdh */ 1689*4c06356bSdh for (i = 0; i < pwp->max_cmd; i++) { 1690*4c06356bSdh pmcwork_t *pwrk = &pwp->work[i]; 1691*4c06356bSdh mutex_enter(&pwrk->lock); 1692*4c06356bSdh if (pwrk->state == PMCS_WORK_STATE_ONCHIP) { 1693*4c06356bSdh switch (PMCS_TAG_TYPE(pwrk->htag)) { 1694*4c06356bSdh case PMCS_TAG_TYPE_WAIT: 1695*4c06356bSdh mutex_exit(&pwrk->lock); 1696*4c06356bSdh break; 1697*4c06356bSdh case PMCS_TAG_TYPE_CBACK: 1698*4c06356bSdh case PMCS_TAG_TYPE_NONE: 1699*4c06356bSdh pmcs_pwork(pwp, pwrk); 1700*4c06356bSdh break; 1701*4c06356bSdh default: 1702*4c06356bSdh break; 1703*4c06356bSdh } 1704*4c06356bSdh } else if (pwrk->state == PMCS_WORK_STATE_IOCOMPQ) { 1705*4c06356bSdh pwrk->dead = 1; 1706*4c06356bSdh mutex_exit(&pwrk->lock); 1707*4c06356bSdh } else { 1708*4c06356bSdh /* 1709*4c06356bSdh * The other states of NIL, READY and INTR 1710*4c06356bSdh * should not be visible outside of a lock being held. 1711*4c06356bSdh */ 1712*4c06356bSdh pmcs_pwork(pwp, pwrk); 1713*4c06356bSdh } 1714*4c06356bSdh } 1715*4c06356bSdh 1716*4c06356bSdh /* 1717*4c06356bSdh * Restore Interrupt Mask 1718*4c06356bSdh */ 1719*4c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, pwp->intr_mask); 1720*4c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff); 1721*4c06356bSdh 1722*4c06356bSdh pwp->blocked = 0; 1723*4c06356bSdh pwp->mpi_table_setup = 0; 1724*4c06356bSdh mutex_exit(&pwp->lock); 1725*4c06356bSdh 1726*4c06356bSdh /* 1727*4c06356bSdh * Set up MPI again. 1728*4c06356bSdh */ 1729*4c06356bSdh if (pmcs_setup(pwp)) { 1730*4c06356bSdh msg = "unable to setup MPI tables again"; 1731*4c06356bSdh goto fail_restart; 1732*4c06356bSdh } 1733*4c06356bSdh pmcs_report_fwversion(pwp); 1734*4c06356bSdh 1735*4c06356bSdh /* 1736*4c06356bSdh * Restart MPI 1737*4c06356bSdh */ 1738*4c06356bSdh if (pmcs_start_mpi(pwp)) { 1739*4c06356bSdh msg = "unable to restart MPI again"; 1740*4c06356bSdh goto fail_restart; 1741*4c06356bSdh } 1742*4c06356bSdh 1743*4c06356bSdh mutex_enter(&pwp->lock); 1744*4c06356bSdh pwp->blocked = 0; 1745*4c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 1746*4c06356bSdh mutex_exit(&pwp->lock); 1747*4c06356bSdh 1748*4c06356bSdh /* 1749*4c06356bSdh * Run any completions 1750*4c06356bSdh */ 1751*4c06356bSdh PMCS_CQ_RUN(pwp); 1752*4c06356bSdh 1753*4c06356bSdh /* 1754*4c06356bSdh * Delay 1755*4c06356bSdh */ 1756*4c06356bSdh drv_usecwait(1000000); 1757*4c06356bSdh return (0); 1758*4c06356bSdh 1759*4c06356bSdh fail_restart: 1760*4c06356bSdh mutex_enter(&pwp->lock); 1761*4c06356bSdh pwp->state = STATE_DEAD; 1762*4c06356bSdh mutex_exit(&pwp->lock); 1763*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, "%s: Failed: %s", __func__, msg); 1764*4c06356bSdh return (-1); 1765*4c06356bSdh } 1766*4c06356bSdh 1767*4c06356bSdh /* 1768*4c06356bSdh * Reset a device or a logical unit. 1769*4c06356bSdh */ 1770*4c06356bSdh int 1771*4c06356bSdh pmcs_reset_dev(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint64_t lun) 1772*4c06356bSdh { 1773*4c06356bSdh int rval = 0; 1774*4c06356bSdh 1775*4c06356bSdh if (pptr == NULL) { 1776*4c06356bSdh return (ENXIO); 1777*4c06356bSdh } 1778*4c06356bSdh 1779*4c06356bSdh pmcs_lock_phy(pptr); 1780*4c06356bSdh if (pptr->dtype == SAS) { 1781*4c06356bSdh /* 1782*4c06356bSdh * Some devices do not support SAS_I_T_NEXUS_RESET as 1783*4c06356bSdh * it is not a mandatory (in SAM4) task management 1784*4c06356bSdh * function, while LOGIC_UNIT_RESET is mandatory. 1785*4c06356bSdh * 1786*4c06356bSdh * The problem here is that we need to iterate over 1787*4c06356bSdh * all known LUNs to emulate the semantics of 1788*4c06356bSdh * "RESET_TARGET". 1789*4c06356bSdh * 1790*4c06356bSdh * XXX: FIX ME 1791*4c06356bSdh */ 1792*4c06356bSdh if (lun == (uint64_t)-1) { 1793*4c06356bSdh lun = 0; 1794*4c06356bSdh } 1795*4c06356bSdh rval = pmcs_ssp_tmf(pwp, pptr, SAS_LOGICAL_UNIT_RESET, 0, lun, 1796*4c06356bSdh NULL); 1797*4c06356bSdh } else if (pptr->dtype == SATA) { 1798*4c06356bSdh if (lun != 0ull) { 1799*4c06356bSdh pmcs_unlock_phy(pptr); 1800*4c06356bSdh return (EINVAL); 1801*4c06356bSdh } 1802*4c06356bSdh rval = pmcs_reset_phy(pwp, pptr, PMCS_PHYOP_LINK_RESET); 1803*4c06356bSdh } else { 1804*4c06356bSdh pmcs_unlock_phy(pptr); 1805*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 1806*4c06356bSdh "%s: cannot reset a SMP device yet (%s)", 1807*4c06356bSdh __func__, pptr->path); 1808*4c06356bSdh return (EINVAL); 1809*4c06356bSdh } 1810*4c06356bSdh 1811*4c06356bSdh /* 1812*4c06356bSdh * Now harvest any commands killed by this action 1813*4c06356bSdh * by issuing an ABORT for all commands on this device. 1814*4c06356bSdh * 1815*4c06356bSdh * We do this even if the the tmf or reset fails (in case there 1816*4c06356bSdh * are any dead commands around to be harvested *anyway*). 1817*4c06356bSdh * We don't have to await for the abort to complete. 1818*4c06356bSdh */ 1819*4c06356bSdh if (pmcs_abort(pwp, pptr, 0, 1, 0)) { 1820*4c06356bSdh pptr->abort_pending = 1; 1821*4c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE); 1822*4c06356bSdh } 1823*4c06356bSdh 1824*4c06356bSdh pmcs_unlock_phy(pptr); 1825*4c06356bSdh return (rval); 1826*4c06356bSdh } 1827*4c06356bSdh 1828*4c06356bSdh /* 1829*4c06356bSdh * Called with PHY locked. 1830*4c06356bSdh */ 1831*4c06356bSdh static int 1832*4c06356bSdh pmcs_get_device_handle(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 1833*4c06356bSdh { 1834*4c06356bSdh if (pptr->valid_device_id == 0) { 1835*4c06356bSdh int result = pmcs_register_device(pwp, pptr); 1836*4c06356bSdh 1837*4c06356bSdh /* 1838*4c06356bSdh * If we changed while registering, punt 1839*4c06356bSdh */ 1840*4c06356bSdh if (pptr->changed) { 1841*4c06356bSdh RESTART_DISCOVERY(pwp); 1842*4c06356bSdh return (-1); 1843*4c06356bSdh } 1844*4c06356bSdh 1845*4c06356bSdh /* 1846*4c06356bSdh * If we had a failure to register, check against errors. 1847*4c06356bSdh * An ENOMEM error means we just retry (temp resource shortage). 1848*4c06356bSdh */ 1849*4c06356bSdh if (result == ENOMEM) { 1850*4c06356bSdh PHY_CHANGED(pwp, pptr); 1851*4c06356bSdh RESTART_DISCOVERY(pwp); 1852*4c06356bSdh return (-1); 1853*4c06356bSdh } 1854*4c06356bSdh 1855*4c06356bSdh /* 1856*4c06356bSdh * An ETIMEDOUT error means we retry (if our counter isn't 1857*4c06356bSdh * exhausted) 1858*4c06356bSdh */ 1859*4c06356bSdh if (result == ETIMEDOUT) { 1860*4c06356bSdh if (ddi_get_lbolt() < pptr->config_stop) { 1861*4c06356bSdh PHY_CHANGED(pwp, pptr); 1862*4c06356bSdh RESTART_DISCOVERY(pwp); 1863*4c06356bSdh } else { 1864*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 1865*4c06356bSdh "%s: Retries exhausted for %s, killing", 1866*4c06356bSdh __func__, pptr->path); 1867*4c06356bSdh pptr->config_stop = 0; 1868*4c06356bSdh pmcs_kill_changed(pwp, pptr, 0); 1869*4c06356bSdh } 1870*4c06356bSdh return (-1); 1871*4c06356bSdh } 1872*4c06356bSdh /* 1873*4c06356bSdh * Other errors or no valid device id is fatal, but don't 1874*4c06356bSdh * preclude a future action. 1875*4c06356bSdh */ 1876*4c06356bSdh if (result || pptr->valid_device_id == 0) { 1877*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: %s could not " 1878*4c06356bSdh "be registered", __func__, pptr->path); 1879*4c06356bSdh return (-1); 1880*4c06356bSdh } 1881*4c06356bSdh } 1882*4c06356bSdh return (0); 1883*4c06356bSdh } 1884*4c06356bSdh 1885*4c06356bSdh int 1886*4c06356bSdh pmcs_iport_tgtmap_create(pmcs_iport_t *iport) 1887*4c06356bSdh { 1888*4c06356bSdh ASSERT(iport); 1889*4c06356bSdh if (iport == NULL) 1890*4c06356bSdh return (B_FALSE); 1891*4c06356bSdh 1892*4c06356bSdh pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_MAP, "%s", __func__); 1893*4c06356bSdh 1894*4c06356bSdh /* create target map */ 1895*4c06356bSdh if (scsi_hba_tgtmap_create(iport->dip, SCSI_TM_FULLSET, tgtmap_usec, 1896*4c06356bSdh 2048, NULL, NULL, NULL, &iport->iss_tgtmap) != DDI_SUCCESS) { 1897*4c06356bSdh pmcs_prt(iport->pwp, PMCS_PRT_DEBUG, 1898*4c06356bSdh "%s: failed to create tgtmap", __func__); 1899*4c06356bSdh return (B_FALSE); 1900*4c06356bSdh } 1901*4c06356bSdh return (B_TRUE); 1902*4c06356bSdh } 1903*4c06356bSdh 1904*4c06356bSdh int 1905*4c06356bSdh pmcs_iport_tgtmap_destroy(pmcs_iport_t *iport) 1906*4c06356bSdh { 1907*4c06356bSdh ASSERT(iport && iport->iss_tgtmap); 1908*4c06356bSdh if ((iport == NULL) || (iport->iss_tgtmap == NULL)) 1909*4c06356bSdh return (B_FALSE); 1910*4c06356bSdh 1911*4c06356bSdh pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_MAP, "%s", __func__); 1912*4c06356bSdh 1913*4c06356bSdh /* destroy target map */ 1914*4c06356bSdh scsi_hba_tgtmap_destroy(iport->iss_tgtmap); 1915*4c06356bSdh return (B_TRUE); 1916*4c06356bSdh } 1917*4c06356bSdh 1918*4c06356bSdh /* 1919*4c06356bSdh * Query the phymap and populate the iport handle passed in. 1920*4c06356bSdh * Called with iport lock held. 1921*4c06356bSdh */ 1922*4c06356bSdh int 1923*4c06356bSdh pmcs_iport_configure_phys(pmcs_iport_t *iport) 1924*4c06356bSdh { 1925*4c06356bSdh pmcs_hw_t *pwp; 1926*4c06356bSdh pmcs_phy_t *pptr; 1927*4c06356bSdh sas_phymap_phys_t *phys; 1928*4c06356bSdh int phynum; 1929*4c06356bSdh int inst; 1930*4c06356bSdh 1931*4c06356bSdh ASSERT(iport); 1932*4c06356bSdh ASSERT(mutex_owned(&iport->lock)); 1933*4c06356bSdh pwp = iport->pwp; 1934*4c06356bSdh ASSERT(pwp); 1935*4c06356bSdh inst = ddi_get_instance(iport->dip); 1936*4c06356bSdh 1937*4c06356bSdh mutex_enter(&pwp->lock); 1938*4c06356bSdh ASSERT(pwp->root_phys != NULL); 1939*4c06356bSdh 1940*4c06356bSdh /* 1941*4c06356bSdh * Query the phymap regarding the phys in this iport and populate 1942*4c06356bSdh * the iport's phys list. Hereafter this list is maintained via 1943*4c06356bSdh * port up and down events in pmcs_intr.c 1944*4c06356bSdh */ 1945*4c06356bSdh ASSERT(list_is_empty(&iport->phys)); 1946*4c06356bSdh phys = sas_phymap_ua2phys(pwp->hss_phymap, iport->ua); 1947*4c06356bSdh while ((phynum = sas_phymap_phys_next(phys)) != -1) { 1948*4c06356bSdh /* Grab the phy pointer from root_phys */ 1949*4c06356bSdh pptr = pwp->root_phys + phynum; 1950*4c06356bSdh ASSERT(pptr); 1951*4c06356bSdh pmcs_lock_phy(pptr); 1952*4c06356bSdh ASSERT(pptr->phynum == phynum); 1953*4c06356bSdh 1954*4c06356bSdh /* 1955*4c06356bSdh * Set a back pointer in the phy to this iport. 1956*4c06356bSdh */ 1957*4c06356bSdh pptr->iport = iport; 1958*4c06356bSdh 1959*4c06356bSdh /* 1960*4c06356bSdh * If this phy is the primary, set a pointer to it on our 1961*4c06356bSdh * iport handle, and set our portid from it. 1962*4c06356bSdh */ 1963*4c06356bSdh if (!pptr->subsidiary) { 1964*4c06356bSdh iport->pptr = pptr; 1965*4c06356bSdh iport->portid = pptr->portid; 1966*4c06356bSdh } 1967*4c06356bSdh 1968*4c06356bSdh /* 1969*4c06356bSdh * Finally, insert the phy into our list 1970*4c06356bSdh */ 1971*4c06356bSdh pmcs_add_phy_to_iport(iport, pptr); 1972*4c06356bSdh pmcs_unlock_phy(pptr); 1973*4c06356bSdh 1974*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: found phy %d [0x%p] " 1975*4c06356bSdh "on iport%d, refcnt(%d)", __func__, phynum, 1976*4c06356bSdh (void *)pptr, inst, iport->refcnt); 1977*4c06356bSdh } 1978*4c06356bSdh mutex_exit(&pwp->lock); 1979*4c06356bSdh sas_phymap_phys_free(phys); 1980*4c06356bSdh RESTART_DISCOVERY(pwp); 1981*4c06356bSdh return (DDI_SUCCESS); 1982*4c06356bSdh } 1983*4c06356bSdh 1984*4c06356bSdh /* 1985*4c06356bSdh * Return the iport that ua is associated with, or NULL. If an iport is 1986*4c06356bSdh * returned, it will be held and the caller must release the hold. 1987*4c06356bSdh */ 1988*4c06356bSdh static pmcs_iport_t * 1989*4c06356bSdh pmcs_get_iport_by_ua(pmcs_hw_t *pwp, char *ua) 1990*4c06356bSdh { 1991*4c06356bSdh pmcs_iport_t *iport = NULL; 1992*4c06356bSdh 1993*4c06356bSdh rw_enter(&pwp->iports_lock, RW_READER); 1994*4c06356bSdh for (iport = list_head(&pwp->iports); 1995*4c06356bSdh iport != NULL; 1996*4c06356bSdh iport = list_next(&pwp->iports, iport)) { 1997*4c06356bSdh mutex_enter(&iport->lock); 1998*4c06356bSdh if (strcmp(iport->ua, ua) == 0) { 1999*4c06356bSdh mutex_exit(&iport->lock); 2000*4c06356bSdh mutex_enter(&iport->refcnt_lock); 2001*4c06356bSdh iport->refcnt++; 2002*4c06356bSdh mutex_exit(&iport->refcnt_lock); 2003*4c06356bSdh break; 2004*4c06356bSdh } 2005*4c06356bSdh mutex_exit(&iport->lock); 2006*4c06356bSdh } 2007*4c06356bSdh rw_exit(&pwp->iports_lock); 2008*4c06356bSdh 2009*4c06356bSdh return (iport); 2010*4c06356bSdh } 2011*4c06356bSdh 2012*4c06356bSdh /* 2013*4c06356bSdh * Return the iport that pptr is associated with, or NULL. 2014*4c06356bSdh * If an iport is returned, there is a hold that the caller must release. 2015*4c06356bSdh */ 2016*4c06356bSdh pmcs_iport_t * 2017*4c06356bSdh pmcs_get_iport_by_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 2018*4c06356bSdh { 2019*4c06356bSdh pmcs_iport_t *iport = NULL; 2020*4c06356bSdh char *ua; 2021*4c06356bSdh 2022*4c06356bSdh ua = sas_phymap_lookup_ua(pwp->hss_phymap, pwp->sas_wwns[0], 2023*4c06356bSdh pmcs_barray2wwn(pptr->sas_address)); 2024*4c06356bSdh if (ua) { 2025*4c06356bSdh iport = pmcs_get_iport_by_ua(pwp, ua); 2026*4c06356bSdh if (iport) { 2027*4c06356bSdh mutex_enter(&iport->lock); 2028*4c06356bSdh iport->ua_state = UA_ACTIVE; 2029*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: " 2030*4c06356bSdh "found iport [0x%p] on ua (%s) for phy [0x%p], " 2031*4c06356bSdh "refcnt (%d)", __func__, (void *)iport, ua, 2032*4c06356bSdh (void *)pptr, iport->refcnt); 2033*4c06356bSdh mutex_exit(&iport->lock); 2034*4c06356bSdh } 2035*4c06356bSdh } 2036*4c06356bSdh 2037*4c06356bSdh return (iport); 2038*4c06356bSdh } 2039*4c06356bSdh 2040*4c06356bSdh void 2041*4c06356bSdh pmcs_rele_iport(pmcs_iport_t *iport) 2042*4c06356bSdh { 2043*4c06356bSdh /* 2044*4c06356bSdh * Release a refcnt on this iport. If this is the last reference, 2045*4c06356bSdh * signal the potential waiter in pmcs_iport_unattach(). 2046*4c06356bSdh */ 2047*4c06356bSdh ASSERT(iport->refcnt > 0); 2048*4c06356bSdh mutex_enter(&iport->refcnt_lock); 2049*4c06356bSdh iport->refcnt--; 2050*4c06356bSdh mutex_exit(&iport->refcnt_lock); 2051*4c06356bSdh if (iport->refcnt == 0) { 2052*4c06356bSdh cv_signal(&iport->refcnt_cv); 2053*4c06356bSdh } 2054*4c06356bSdh pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, "%s: iport [0x%p] " 2055*4c06356bSdh "refcnt (%d)", __func__, (void *)iport, iport->refcnt); 2056*4c06356bSdh } 2057*4c06356bSdh 2058*4c06356bSdh void 2059*4c06356bSdh pmcs_phymap_activate(void *arg, char *ua, void **privp) 2060*4c06356bSdh { 2061*4c06356bSdh _NOTE(ARGUNUSED(privp)); 2062*4c06356bSdh pmcs_hw_t *pwp = arg; 2063*4c06356bSdh pmcs_iport_t *iport = NULL; 2064*4c06356bSdh 2065*4c06356bSdh mutex_enter(&pwp->lock); 2066*4c06356bSdh if ((pwp->state == STATE_UNPROBING) || (pwp->state == STATE_DEAD)) { 2067*4c06356bSdh mutex_exit(&pwp->lock); 2068*4c06356bSdh return; 2069*4c06356bSdh } 2070*4c06356bSdh pwp->phymap_active++; 2071*4c06356bSdh mutex_exit(&pwp->lock); 2072*4c06356bSdh 2073*4c06356bSdh if (scsi_hba_iportmap_iport_add(pwp->hss_iportmap, ua, NULL) != 2074*4c06356bSdh DDI_SUCCESS) { 2075*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, "%s: failed to add " 2076*4c06356bSdh "iport handle on unit address [%s]", __func__, ua); 2077*4c06356bSdh } else { 2078*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, "%s: phymap_active count " 2079*4c06356bSdh "(%d), added iport handle on unit address [%s]", __func__, 2080*4c06356bSdh pwp->phymap_active, ua); 2081*4c06356bSdh } 2082*4c06356bSdh 2083*4c06356bSdh /* Set the HBA softstate as our private data for this unit address */ 2084*4c06356bSdh *privp = (void *)pwp; 2085*4c06356bSdh 2086*4c06356bSdh /* 2087*4c06356bSdh * We are waiting on attach for this iport node, unless it is still 2088*4c06356bSdh * attached. This can happen if a consumer has an outstanding open 2089*4c06356bSdh * on our iport node, but the port is down. If this is the case, we 2090*4c06356bSdh * need to configure our iport here for reuse. 2091*4c06356bSdh */ 2092*4c06356bSdh iport = pmcs_get_iport_by_ua(pwp, ua); 2093*4c06356bSdh if (iport) { 2094*4c06356bSdh mutex_enter(&iport->lock); 2095*4c06356bSdh if (pmcs_iport_configure_phys(iport) != DDI_SUCCESS) { 2096*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: " 2097*4c06356bSdh "failed to configure phys on iport [0x%p] at " 2098*4c06356bSdh "unit address (%s)", __func__, (void *)iport, ua); 2099*4c06356bSdh } 2100*4c06356bSdh iport->ua_state = UA_ACTIVE; 2101*4c06356bSdh pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS, 2102*4c06356bSdh &iport->nphy); 2103*4c06356bSdh mutex_exit(&iport->lock); 2104*4c06356bSdh pmcs_rele_iport(iport); 2105*4c06356bSdh } 2106*4c06356bSdh 2107*4c06356bSdh } 2108*4c06356bSdh 2109*4c06356bSdh void 2110*4c06356bSdh pmcs_phymap_deactivate(void *arg, char *ua, void *privp) 2111*4c06356bSdh { 2112*4c06356bSdh _NOTE(ARGUNUSED(privp)); 2113*4c06356bSdh pmcs_hw_t *pwp = arg; 2114*4c06356bSdh pmcs_iport_t *iport; 2115*4c06356bSdh 2116*4c06356bSdh mutex_enter(&pwp->lock); 2117*4c06356bSdh pwp->phymap_active--; 2118*4c06356bSdh mutex_exit(&pwp->lock); 2119*4c06356bSdh 2120*4c06356bSdh if (scsi_hba_iportmap_iport_remove(pwp->hss_iportmap, ua) != 2121*4c06356bSdh DDI_SUCCESS) { 2122*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, "%s: failed to remove " 2123*4c06356bSdh "iport handle on unit address [%s]", __func__, ua); 2124*4c06356bSdh } else { 2125*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, "%s: phymap_active " 2126*4c06356bSdh "count (%d), removed iport handle on unit address [%s]", 2127*4c06356bSdh __func__, pwp->phymap_active, ua); 2128*4c06356bSdh } 2129*4c06356bSdh 2130*4c06356bSdh iport = pmcs_get_iport_by_ua(pwp, ua); 2131*4c06356bSdh 2132*4c06356bSdh if (iport == NULL) { 2133*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: failed lookup of " 2134*4c06356bSdh "iport handle on unit address (%s)", __func__, ua); 2135*4c06356bSdh return; 2136*4c06356bSdh } 2137*4c06356bSdh 2138*4c06356bSdh mutex_enter(&iport->lock); 2139*4c06356bSdh iport->ua_state = UA_INACTIVE; 2140*4c06356bSdh iport->portid = PMCS_IPORT_INVALID_PORT_ID; 2141*4c06356bSdh pmcs_remove_phy_from_iport(iport, NULL); 2142*4c06356bSdh mutex_exit(&iport->lock); 2143*4c06356bSdh pmcs_rele_iport(iport); 2144*4c06356bSdh } 2145*4c06356bSdh 2146*4c06356bSdh /* 2147*4c06356bSdh * Top-level discovery function 2148*4c06356bSdh */ 2149*4c06356bSdh void 2150*4c06356bSdh pmcs_discover(pmcs_hw_t *pwp) 2151*4c06356bSdh { 2152*4c06356bSdh pmcs_phy_t *pptr; 2153*4c06356bSdh pmcs_phy_t *root_phy; 2154*4c06356bSdh 2155*4c06356bSdh DTRACE_PROBE2(pmcs__discover__entry, ulong_t, pwp->work_flags, 2156*4c06356bSdh boolean_t, pwp->config_changed); 2157*4c06356bSdh 2158*4c06356bSdh mutex_enter(&pwp->lock); 2159*4c06356bSdh 2160*4c06356bSdh if (pwp->state != STATE_RUNNING) { 2161*4c06356bSdh mutex_exit(&pwp->lock); 2162*4c06356bSdh return; 2163*4c06356bSdh } 2164*4c06356bSdh 2165*4c06356bSdh /* Ensure we have at least one phymap active */ 2166*4c06356bSdh if (pwp->phymap_active == 0) { 2167*4c06356bSdh mutex_exit(&pwp->lock); 2168*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 2169*4c06356bSdh "%s: phymap inactive, exiting", __func__); 2170*4c06356bSdh return; 2171*4c06356bSdh } 2172*4c06356bSdh 2173*4c06356bSdh mutex_exit(&pwp->lock); 2174*4c06356bSdh 2175*4c06356bSdh /* 2176*4c06356bSdh * If no iports have attached, but we have PHYs that are up, we 2177*4c06356bSdh * are waiting for iport attach to complete. Restart discovery. 2178*4c06356bSdh */ 2179*4c06356bSdh rw_enter(&pwp->iports_lock, RW_READER); 2180*4c06356bSdh if (!pwp->iports_attached) { 2181*4c06356bSdh rw_exit(&pwp->iports_lock); 2182*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 2183*4c06356bSdh "%s: no iports attached, retry discovery", __func__); 2184*4c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER); 2185*4c06356bSdh return; 2186*4c06356bSdh } 2187*4c06356bSdh rw_exit(&pwp->iports_lock); 2188*4c06356bSdh 2189*4c06356bSdh mutex_enter(&pwp->config_lock); 2190*4c06356bSdh if (pwp->configuring) { 2191*4c06356bSdh mutex_exit(&pwp->config_lock); 2192*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 2193*4c06356bSdh "%s: configuration already in progress", __func__); 2194*4c06356bSdh return; 2195*4c06356bSdh } 2196*4c06356bSdh 2197*4c06356bSdh if (pmcs_acquire_scratch(pwp, B_FALSE)) { 2198*4c06356bSdh mutex_exit(&pwp->config_lock); 2199*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 2200*4c06356bSdh "%s: cannot allocate scratch", __func__); 2201*4c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER); 2202*4c06356bSdh return; 2203*4c06356bSdh } 2204*4c06356bSdh 2205*4c06356bSdh pwp->configuring = 1; 2206*4c06356bSdh pwp->config_changed = B_FALSE; 2207*4c06356bSdh mutex_exit(&pwp->config_lock); 2208*4c06356bSdh 2209*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "Discovery begin"); 2210*4c06356bSdh 2211*4c06356bSdh /* 2212*4c06356bSdh * The order of the following traversals is important. 2213*4c06356bSdh * 2214*4c06356bSdh * The first one checks for changed expanders. 2215*4c06356bSdh * 2216*4c06356bSdh * The second one aborts commands for dead devices and deregisters them. 2217*4c06356bSdh * 2218*4c06356bSdh * The third one clears the contents of dead expanders from the tree 2219*4c06356bSdh * 2220*4c06356bSdh * The fourth one clears now dead devices in expanders that remain. 2221*4c06356bSdh */ 2222*4c06356bSdh 2223*4c06356bSdh /* 2224*4c06356bSdh * 1. Check expanders marked changed (but not dead) to see if they still 2225*4c06356bSdh * have the same number of phys and the same SAS address. Mark them, 2226*4c06356bSdh * their subsidiary phys (if wide) and their descendents dead if 2227*4c06356bSdh * anything has changed. Check the devices they contain to see if 2228*4c06356bSdh * *they* have changed. If they've changed from type NOTHING we leave 2229*4c06356bSdh * them marked changed to be configured later (picking up a new SAS 2230*4c06356bSdh * address and link rate if possible). Otherwise, any change in type, 2231*4c06356bSdh * SAS address or removal of target role will cause us to mark them 2232*4c06356bSdh * (and their descendents) as dead (and cause any pending commands 2233*4c06356bSdh * and associated devices to be removed). 2234*4c06356bSdh */ 2235*4c06356bSdh root_phy = pwp->root_phys; 2236*4c06356bSdh if (pmcs_check_expanders(pwp, root_phy) == B_TRUE) { 2237*4c06356bSdh goto out; 2238*4c06356bSdh } 2239*4c06356bSdh 2240*4c06356bSdh /* 2241*4c06356bSdh * 2. Descend the tree looking for dead devices and kill them 2242*4c06356bSdh * by aborting all active commands and then deregistering them. 2243*4c06356bSdh */ 2244*4c06356bSdh if (pmcs_kill_devices(pwp, root_phy)) { 2245*4c06356bSdh goto out; 2246*4c06356bSdh } 2247*4c06356bSdh 2248*4c06356bSdh /* 2249*4c06356bSdh * 3. Check for dead expanders and remove their children from the tree. 2250*4c06356bSdh * By the time we get here, the devices and commands for them have 2251*4c06356bSdh * already been terminated and removed. 2252*4c06356bSdh * 2253*4c06356bSdh * We do this independent of the configuration count changing so we can 2254*4c06356bSdh * free any dead device PHYs that were discovered while checking 2255*4c06356bSdh * expanders. We ignore any subsidiary phys as pmcs_clear_expander 2256*4c06356bSdh * will take care of those. 2257*4c06356bSdh * 2258*4c06356bSdh * NOTE: pmcs_clear_expander requires softstate lock 2259*4c06356bSdh */ 2260*4c06356bSdh mutex_enter(&pwp->lock); 2261*4c06356bSdh for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) { 2262*4c06356bSdh /* 2263*4c06356bSdh * Call pmcs_clear_expander for every root PHY. It will 2264*4c06356bSdh * recurse and determine which (if any) expanders actually 2265*4c06356bSdh * need to be cleared. 2266*4c06356bSdh */ 2267*4c06356bSdh pmcs_lock_phy(pptr); 2268*4c06356bSdh pmcs_clear_expander(pwp, pptr, 0); 2269*4c06356bSdh pmcs_unlock_phy(pptr); 2270*4c06356bSdh } 2271*4c06356bSdh mutex_exit(&pwp->lock); 2272*4c06356bSdh 2273*4c06356bSdh /* 2274*4c06356bSdh * 4. Check for dead devices and nullify them. By the time we get here, 2275*4c06356bSdh * the devices and commands for them have already been terminated 2276*4c06356bSdh * and removed. This is different from step 2 in that this just nulls 2277*4c06356bSdh * phys that are part of expanders that are still here but used to 2278*4c06356bSdh * be something but are no longer something (e.g., after a pulled 2279*4c06356bSdh * disk drive). Note that dead expanders had their contained phys 2280*4c06356bSdh * removed from the tree- here, the expanders themselves are 2281*4c06356bSdh * nullified (unless they were removed by being contained in another 2282*4c06356bSdh * expander phy). 2283*4c06356bSdh */ 2284*4c06356bSdh pmcs_clear_phys(pwp, root_phy); 2285*4c06356bSdh 2286*4c06356bSdh /* 2287*4c06356bSdh * 5. Now check for and configure new devices. 2288*4c06356bSdh */ 2289*4c06356bSdh if (pmcs_configure_new_devices(pwp, root_phy)) { 2290*4c06356bSdh goto restart; 2291*4c06356bSdh } 2292*4c06356bSdh 2293*4c06356bSdh out: 2294*4c06356bSdh DTRACE_PROBE2(pmcs__discover__exit, ulong_t, pwp->work_flags, 2295*4c06356bSdh boolean_t, pwp->config_changed); 2296*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "Discovery end"); 2297*4c06356bSdh 2298*4c06356bSdh mutex_enter(&pwp->config_lock); 2299*4c06356bSdh 2300*4c06356bSdh if (pwp->config_changed == B_FALSE) { 2301*4c06356bSdh /* 2302*4c06356bSdh * Observation is stable, report what we currently see to 2303*4c06356bSdh * the tgtmaps for delta processing. Start by setting 2304*4c06356bSdh * BEGIN on all tgtmaps. 2305*4c06356bSdh */ 2306*4c06356bSdh mutex_exit(&pwp->config_lock); 2307*4c06356bSdh if (pmcs_report_observations(pwp) == B_FALSE) { 2308*4c06356bSdh goto restart; 2309*4c06356bSdh } 2310*4c06356bSdh mutex_enter(&pwp->config_lock); 2311*4c06356bSdh } else { 2312*4c06356bSdh /* 2313*4c06356bSdh * If config_changed is TRUE, we need to reschedule 2314*4c06356bSdh * discovery now. 2315*4c06356bSdh */ 2316*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 2317*4c06356bSdh "%s: Config has changed, will re-run discovery", __func__); 2318*4c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER); 2319*4c06356bSdh } 2320*4c06356bSdh 2321*4c06356bSdh pmcs_release_scratch(pwp); 2322*4c06356bSdh pwp->configuring = 0; 2323*4c06356bSdh mutex_exit(&pwp->config_lock); 2324*4c06356bSdh 2325*4c06356bSdh #ifdef DEBUG 2326*4c06356bSdh pptr = pmcs_find_phy_needing_work(pwp, pwp->root_phys); 2327*4c06356bSdh if (pptr != NULL) { 2328*4c06356bSdh if (!WORK_IS_SCHEDULED(pwp, PMCS_WORK_DISCOVER)) { 2329*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 2330*4c06356bSdh "PHY %s dead=%d changed=%d configured=%d " 2331*4c06356bSdh "but no work scheduled", pptr->path, pptr->dead, 2332*4c06356bSdh pptr->changed, pptr->configured); 2333*4c06356bSdh } 2334*4c06356bSdh pmcs_unlock_phy(pptr); 2335*4c06356bSdh } 2336*4c06356bSdh #endif 2337*4c06356bSdh 2338*4c06356bSdh return; 2339*4c06356bSdh 2340*4c06356bSdh restart: 2341*4c06356bSdh /* Clean up and restart discovery */ 2342*4c06356bSdh pmcs_release_scratch(pwp); 2343*4c06356bSdh mutex_enter(&pwp->config_lock); 2344*4c06356bSdh pwp->configuring = 0; 2345*4c06356bSdh RESTART_DISCOVERY_LOCKED(pwp); 2346*4c06356bSdh mutex_exit(&pwp->config_lock); 2347*4c06356bSdh } 2348*4c06356bSdh 2349*4c06356bSdh /* 2350*4c06356bSdh * Return any PHY that needs to have scheduled work done. The PHY is returned 2351*4c06356bSdh * locked. 2352*4c06356bSdh */ 2353*4c06356bSdh static pmcs_phy_t * 2354*4c06356bSdh pmcs_find_phy_needing_work(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 2355*4c06356bSdh { 2356*4c06356bSdh pmcs_phy_t *cphyp, *pnext; 2357*4c06356bSdh 2358*4c06356bSdh while (pptr) { 2359*4c06356bSdh pmcs_lock_phy(pptr); 2360*4c06356bSdh 2361*4c06356bSdh if (pptr->changed || (pptr->dead && pptr->valid_device_id)) { 2362*4c06356bSdh return (pptr); 2363*4c06356bSdh } 2364*4c06356bSdh 2365*4c06356bSdh pnext = pptr->sibling; 2366*4c06356bSdh 2367*4c06356bSdh if (pptr->children) { 2368*4c06356bSdh cphyp = pptr->children; 2369*4c06356bSdh pmcs_unlock_phy(pptr); 2370*4c06356bSdh cphyp = pmcs_find_phy_needing_work(pwp, cphyp); 2371*4c06356bSdh if (cphyp) { 2372*4c06356bSdh return (cphyp); 2373*4c06356bSdh } 2374*4c06356bSdh } else { 2375*4c06356bSdh pmcs_unlock_phy(pptr); 2376*4c06356bSdh } 2377*4c06356bSdh 2378*4c06356bSdh pptr = pnext; 2379*4c06356bSdh } 2380*4c06356bSdh 2381*4c06356bSdh return (NULL); 2382*4c06356bSdh } 2383*4c06356bSdh 2384*4c06356bSdh /* 2385*4c06356bSdh * Report current observations to SCSA. 2386*4c06356bSdh */ 2387*4c06356bSdh static boolean_t 2388*4c06356bSdh pmcs_report_observations(pmcs_hw_t *pwp) 2389*4c06356bSdh { 2390*4c06356bSdh pmcs_iport_t *iport; 2391*4c06356bSdh scsi_hba_tgtmap_t *tgtmap; 2392*4c06356bSdh char *ap; 2393*4c06356bSdh pmcs_phy_t *pptr; 2394*4c06356bSdh uint64_t wwn; 2395*4c06356bSdh 2396*4c06356bSdh /* 2397*4c06356bSdh * Observation is stable, report what we currently see to the tgtmaps 2398*4c06356bSdh * for delta processing. Start by setting BEGIN on all tgtmaps. 2399*4c06356bSdh */ 2400*4c06356bSdh rw_enter(&pwp->iports_lock, RW_READER); 2401*4c06356bSdh for (iport = list_head(&pwp->iports); iport != NULL; 2402*4c06356bSdh iport = list_next(&pwp->iports, iport)) { 2403*4c06356bSdh /* 2404*4c06356bSdh * Unless we have at least one phy up, skip this iport. 2405*4c06356bSdh * Note we don't need to lock the iport for report_skip 2406*4c06356bSdh * since it is only used here. We are doing the skip so that 2407*4c06356bSdh * the phymap and iportmap stabilization times are honored - 2408*4c06356bSdh * giving us the ability to recover port operation within the 2409*4c06356bSdh * stabilization time without unconfiguring targets using the 2410*4c06356bSdh * port. 2411*4c06356bSdh */ 2412*4c06356bSdh if (!sas_phymap_uahasphys(pwp->hss_phymap, iport->ua)) { 2413*4c06356bSdh iport->report_skip = 1; 2414*4c06356bSdh continue; /* skip set_begin */ 2415*4c06356bSdh } 2416*4c06356bSdh iport->report_skip = 0; 2417*4c06356bSdh 2418*4c06356bSdh tgtmap = iport->iss_tgtmap; 2419*4c06356bSdh ASSERT(tgtmap); 2420*4c06356bSdh if (scsi_hba_tgtmap_set_begin(tgtmap) != DDI_SUCCESS) { 2421*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, 2422*4c06356bSdh "%s: cannot set_begin tgtmap ", __func__); 2423*4c06356bSdh rw_exit(&pwp->iports_lock); 2424*4c06356bSdh return (B_FALSE); 2425*4c06356bSdh } 2426*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, 2427*4c06356bSdh "%s: set begin on tgtmap [0x%p]", __func__, 2428*4c06356bSdh (void *)tgtmap); 2429*4c06356bSdh } 2430*4c06356bSdh rw_exit(&pwp->iports_lock); 2431*4c06356bSdh 2432*4c06356bSdh /* 2433*4c06356bSdh * Now, cycle through all levels of all phys and report 2434*4c06356bSdh * observations into their respective tgtmaps. 2435*4c06356bSdh */ 2436*4c06356bSdh pptr = pwp->root_phys; 2437*4c06356bSdh 2438*4c06356bSdh while (pptr) { 2439*4c06356bSdh pmcs_lock_phy(pptr); 2440*4c06356bSdh 2441*4c06356bSdh /* 2442*4c06356bSdh * Skip PHYs that have nothing attached or are dead. 2443*4c06356bSdh */ 2444*4c06356bSdh if ((pptr->dtype == NOTHING) || pptr->dead) { 2445*4c06356bSdh pmcs_unlock_phy(pptr); 2446*4c06356bSdh pptr = pptr->sibling; 2447*4c06356bSdh continue; 2448*4c06356bSdh } 2449*4c06356bSdh 2450*4c06356bSdh if (pptr->changed) { 2451*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 2452*4c06356bSdh "%s: oops, PHY %s changed; restart discovery", 2453*4c06356bSdh __func__, pptr->path); 2454*4c06356bSdh pmcs_unlock_phy(pptr); 2455*4c06356bSdh return (B_FALSE); 2456*4c06356bSdh } 2457*4c06356bSdh 2458*4c06356bSdh /* 2459*4c06356bSdh * Get the iport for this root PHY, then call the helper 2460*4c06356bSdh * to report observations for this iport's targets 2461*4c06356bSdh */ 2462*4c06356bSdh iport = pmcs_get_iport_by_phy(pwp, pptr); 2463*4c06356bSdh if (iport == NULL) { 2464*4c06356bSdh /* No iport for this tgt */ 2465*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 2466*4c06356bSdh "%s: no iport for this target", 2467*4c06356bSdh __func__); 2468*4c06356bSdh pmcs_unlock_phy(pptr); 2469*4c06356bSdh pptr = pptr->sibling; 2470*4c06356bSdh continue; 2471*4c06356bSdh } 2472*4c06356bSdh 2473*4c06356bSdh if (!iport->report_skip) { 2474*4c06356bSdh if (pmcs_report_iport_observations( 2475*4c06356bSdh pwp, iport, pptr) == B_FALSE) { 2476*4c06356bSdh pmcs_rele_iport(iport); 2477*4c06356bSdh pmcs_unlock_phy(pptr); 2478*4c06356bSdh return (B_FALSE); 2479*4c06356bSdh } 2480*4c06356bSdh } 2481*4c06356bSdh pmcs_rele_iport(iport); 2482*4c06356bSdh pmcs_unlock_phy(pptr); 2483*4c06356bSdh pptr = pptr->sibling; 2484*4c06356bSdh } 2485*4c06356bSdh 2486*4c06356bSdh /* 2487*4c06356bSdh * The observation is complete, end sets. Note we will skip any 2488*4c06356bSdh * iports that are active, but have no PHYs in them (i.e. awaiting 2489*4c06356bSdh * unconfigure). Set to restart discovery if we find this. 2490*4c06356bSdh */ 2491*4c06356bSdh rw_enter(&pwp->iports_lock, RW_READER); 2492*4c06356bSdh for (iport = list_head(&pwp->iports); 2493*4c06356bSdh iport != NULL; 2494*4c06356bSdh iport = list_next(&pwp->iports, iport)) { 2495*4c06356bSdh 2496*4c06356bSdh if (iport->report_skip) 2497*4c06356bSdh continue; /* skip set_end */ 2498*4c06356bSdh 2499*4c06356bSdh tgtmap = iport->iss_tgtmap; 2500*4c06356bSdh ASSERT(tgtmap); 2501*4c06356bSdh if (scsi_hba_tgtmap_set_end(tgtmap, 0) != DDI_SUCCESS) { 2502*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, 2503*4c06356bSdh "%s: cannot set_end tgtmap ", __func__); 2504*4c06356bSdh rw_exit(&pwp->iports_lock); 2505*4c06356bSdh return (B_FALSE); 2506*4c06356bSdh } 2507*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, 2508*4c06356bSdh "%s: set end on tgtmap [0x%p]", __func__, 2509*4c06356bSdh (void *)tgtmap); 2510*4c06356bSdh } 2511*4c06356bSdh 2512*4c06356bSdh /* 2513*4c06356bSdh * Now that discovery is complete, set up the necessary 2514*4c06356bSdh * DDI properties on each iport node. 2515*4c06356bSdh */ 2516*4c06356bSdh for (iport = list_head(&pwp->iports); iport != NULL; 2517*4c06356bSdh iport = list_next(&pwp->iports, iport)) { 2518*4c06356bSdh /* Set up the DDI properties on each phy */ 2519*4c06356bSdh pmcs_smhba_set_phy_props(iport); 2520*4c06356bSdh 2521*4c06356bSdh /* Set up the 'attached-port' property on the iport */ 2522*4c06356bSdh ap = kmem_zalloc(PMCS_MAX_UA_SIZE, KM_SLEEP); 2523*4c06356bSdh mutex_enter(&iport->lock); 2524*4c06356bSdh pptr = iport->pptr; 2525*4c06356bSdh mutex_exit(&iport->lock); 2526*4c06356bSdh if (pptr == NULL) { 2527*4c06356bSdh /* 2528*4c06356bSdh * This iport is down, but has not been 2529*4c06356bSdh * removed from our list (unconfigured). 2530*4c06356bSdh * Set our value to '0'. 2531*4c06356bSdh */ 2532*4c06356bSdh (void) snprintf(ap, 1, "%s", "0"); 2533*4c06356bSdh } else { 2534*4c06356bSdh /* Otherwise, set it to remote phy's wwn */ 2535*4c06356bSdh pmcs_lock_phy(pptr); 2536*4c06356bSdh wwn = pmcs_barray2wwn(pptr->sas_address); 2537*4c06356bSdh (void) scsi_wwn_to_wwnstr(wwn, 1, ap); 2538*4c06356bSdh pmcs_unlock_phy(pptr); 2539*4c06356bSdh } 2540*4c06356bSdh if (ndi_prop_update_string(DDI_DEV_T_NONE, iport->dip, 2541*4c06356bSdh SCSI_ADDR_PROP_ATTACHED_PORT, ap) != DDI_SUCCESS) { 2542*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Failed to " 2543*4c06356bSdh "set prop ("SCSI_ADDR_PROP_ATTACHED_PORT")", 2544*4c06356bSdh __func__); 2545*4c06356bSdh } 2546*4c06356bSdh kmem_free(ap, PMCS_MAX_UA_SIZE); 2547*4c06356bSdh } 2548*4c06356bSdh rw_exit(&pwp->iports_lock); 2549*4c06356bSdh 2550*4c06356bSdh return (B_TRUE); 2551*4c06356bSdh } 2552*4c06356bSdh 2553*4c06356bSdh /* 2554*4c06356bSdh * Report observations into a particular iport's target map 2555*4c06356bSdh * 2556*4c06356bSdh * Called with phyp (and all descendents) locked 2557*4c06356bSdh */ 2558*4c06356bSdh static boolean_t 2559*4c06356bSdh pmcs_report_iport_observations(pmcs_hw_t *pwp, pmcs_iport_t *iport, 2560*4c06356bSdh pmcs_phy_t *phyp) 2561*4c06356bSdh { 2562*4c06356bSdh pmcs_phy_t *lphyp; 2563*4c06356bSdh scsi_hba_tgtmap_t *tgtmap; 2564*4c06356bSdh scsi_tgtmap_tgt_type_t tgt_type; 2565*4c06356bSdh char *ua; 2566*4c06356bSdh uint64_t wwn; 2567*4c06356bSdh 2568*4c06356bSdh tgtmap = iport->iss_tgtmap; 2569*4c06356bSdh ASSERT(tgtmap); 2570*4c06356bSdh 2571*4c06356bSdh lphyp = phyp; 2572*4c06356bSdh while (lphyp) { 2573*4c06356bSdh switch (lphyp->dtype) { 2574*4c06356bSdh default: /* Skip unknown PHYs. */ 2575*4c06356bSdh /* for non-root phys, skip to sibling */ 2576*4c06356bSdh goto next_phy; 2577*4c06356bSdh 2578*4c06356bSdh case SATA: 2579*4c06356bSdh case SAS: 2580*4c06356bSdh tgt_type = SCSI_TGT_SCSI_DEVICE; 2581*4c06356bSdh break; 2582*4c06356bSdh 2583*4c06356bSdh case EXPANDER: 2584*4c06356bSdh tgt_type = SCSI_TGT_SMP_DEVICE; 2585*4c06356bSdh break; 2586*4c06356bSdh } 2587*4c06356bSdh 2588*4c06356bSdh if (lphyp->dead) { 2589*4c06356bSdh goto next_phy; 2590*4c06356bSdh } 2591*4c06356bSdh 2592*4c06356bSdh wwn = pmcs_barray2wwn(lphyp->sas_address); 2593*4c06356bSdh ua = scsi_wwn_to_wwnstr(wwn, 1, NULL); 2594*4c06356bSdh 2595*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, 2596*4c06356bSdh "iport_observation: adding %s on tgtmap [0x%p] phy [0x%p]", 2597*4c06356bSdh ua, (void *)tgtmap, (void*)lphyp); 2598*4c06356bSdh 2599*4c06356bSdh if (scsi_hba_tgtmap_set_add(tgtmap, tgt_type, ua, NULL) != 2600*4c06356bSdh DDI_SUCCESS) { 2601*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, 2602*4c06356bSdh "%s: failed to add address %s", __func__, ua); 2603*4c06356bSdh scsi_free_wwnstr(ua); 2604*4c06356bSdh return (B_FALSE); 2605*4c06356bSdh } 2606*4c06356bSdh scsi_free_wwnstr(ua); 2607*4c06356bSdh 2608*4c06356bSdh if (lphyp->children) { 2609*4c06356bSdh if (pmcs_report_iport_observations(pwp, iport, 2610*4c06356bSdh lphyp->children) == B_FALSE) { 2611*4c06356bSdh return (B_FALSE); 2612*4c06356bSdh } 2613*4c06356bSdh } 2614*4c06356bSdh 2615*4c06356bSdh /* for non-root phys, report siblings too */ 2616*4c06356bSdh next_phy: 2617*4c06356bSdh if (IS_ROOT_PHY(lphyp)) { 2618*4c06356bSdh lphyp = NULL; 2619*4c06356bSdh } else { 2620*4c06356bSdh lphyp = lphyp->sibling; 2621*4c06356bSdh } 2622*4c06356bSdh } 2623*4c06356bSdh 2624*4c06356bSdh return (B_TRUE); 2625*4c06356bSdh } 2626*4c06356bSdh 2627*4c06356bSdh /* 2628*4c06356bSdh * Check for and configure new devices. 2629*4c06356bSdh * 2630*4c06356bSdh * If the changed device is a SATA device, add a SATA device. 2631*4c06356bSdh * 2632*4c06356bSdh * If the changed device is a SAS device, add a SAS device. 2633*4c06356bSdh * 2634*4c06356bSdh * If the changed device is an EXPANDER device, do a REPORT 2635*4c06356bSdh * GENERAL SMP command to find out the number of contained phys. 2636*4c06356bSdh * 2637*4c06356bSdh * For each number of contained phys, allocate a phy, do a 2638*4c06356bSdh * DISCOVERY SMP command to find out what kind of device it 2639*4c06356bSdh * is and add it to the linked list of phys on the *next* level. 2640*4c06356bSdh * 2641*4c06356bSdh * NOTE: pptr passed in by the caller will be a root PHY 2642*4c06356bSdh */ 2643*4c06356bSdh static int 2644*4c06356bSdh pmcs_configure_new_devices(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 2645*4c06356bSdh { 2646*4c06356bSdh int rval = 0; 2647*4c06356bSdh pmcs_iport_t *iport; 2648*4c06356bSdh pmcs_phy_t *pnext, *orig_pptr = pptr, *root_phy, *pchild; 2649*4c06356bSdh 2650*4c06356bSdh /* 2651*4c06356bSdh * First, walk through each PHY at this level 2652*4c06356bSdh */ 2653*4c06356bSdh while (pptr) { 2654*4c06356bSdh pmcs_lock_phy(pptr); 2655*4c06356bSdh pnext = pptr->sibling; 2656*4c06356bSdh 2657*4c06356bSdh /* 2658*4c06356bSdh * Set the new dtype if it has changed 2659*4c06356bSdh */ 2660*4c06356bSdh if ((pptr->pend_dtype != NEW) && 2661*4c06356bSdh (pptr->pend_dtype != pptr->dtype)) { 2662*4c06356bSdh pptr->dtype = pptr->pend_dtype; 2663*4c06356bSdh } 2664*4c06356bSdh 2665*4c06356bSdh if (pptr->changed == 0 || pptr->dead || pptr->configured) { 2666*4c06356bSdh goto next_phy; 2667*4c06356bSdh } 2668*4c06356bSdh 2669*4c06356bSdh /* 2670*4c06356bSdh * Confirm that this target's iport is configured 2671*4c06356bSdh */ 2672*4c06356bSdh root_phy = pmcs_get_root_phy(pptr); 2673*4c06356bSdh iport = pmcs_get_iport_by_phy(pwp, root_phy); 2674*4c06356bSdh if (iport == NULL) { 2675*4c06356bSdh /* No iport for this tgt, restart */ 2676*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 2677*4c06356bSdh "%s: iport not yet configured, " 2678*4c06356bSdh "retry discovery", __func__); 2679*4c06356bSdh pnext = NULL; 2680*4c06356bSdh rval = -1; 2681*4c06356bSdh goto next_phy; 2682*4c06356bSdh } 2683*4c06356bSdh 2684*4c06356bSdh switch (pptr->dtype) { 2685*4c06356bSdh case NOTHING: 2686*4c06356bSdh pptr->changed = 0; 2687*4c06356bSdh break; 2688*4c06356bSdh case SATA: 2689*4c06356bSdh case SAS: 2690*4c06356bSdh pptr->iport = iport; 2691*4c06356bSdh pmcs_new_tport(pwp, pptr); 2692*4c06356bSdh break; 2693*4c06356bSdh case EXPANDER: 2694*4c06356bSdh pmcs_configure_expander(pwp, pptr, iport); 2695*4c06356bSdh break; 2696*4c06356bSdh } 2697*4c06356bSdh pmcs_rele_iport(iport); 2698*4c06356bSdh 2699*4c06356bSdh mutex_enter(&pwp->config_lock); 2700*4c06356bSdh if (pwp->config_changed) { 2701*4c06356bSdh mutex_exit(&pwp->config_lock); 2702*4c06356bSdh pnext = NULL; 2703*4c06356bSdh goto next_phy; 2704*4c06356bSdh } 2705*4c06356bSdh mutex_exit(&pwp->config_lock); 2706*4c06356bSdh 2707*4c06356bSdh next_phy: 2708*4c06356bSdh pmcs_unlock_phy(pptr); 2709*4c06356bSdh pptr = pnext; 2710*4c06356bSdh } 2711*4c06356bSdh 2712*4c06356bSdh if (rval != 0) { 2713*4c06356bSdh return (rval); 2714*4c06356bSdh } 2715*4c06356bSdh 2716*4c06356bSdh /* 2717*4c06356bSdh * Now walk through each PHY again, recalling ourselves if they 2718*4c06356bSdh * have children 2719*4c06356bSdh */ 2720*4c06356bSdh pptr = orig_pptr; 2721*4c06356bSdh while (pptr) { 2722*4c06356bSdh pmcs_lock_phy(pptr); 2723*4c06356bSdh pnext = pptr->sibling; 2724*4c06356bSdh pchild = pptr->children; 2725*4c06356bSdh pmcs_unlock_phy(pptr); 2726*4c06356bSdh 2727*4c06356bSdh if (pchild) { 2728*4c06356bSdh rval = pmcs_configure_new_devices(pwp, pchild); 2729*4c06356bSdh if (rval != 0) { 2730*4c06356bSdh break; 2731*4c06356bSdh } 2732*4c06356bSdh } 2733*4c06356bSdh 2734*4c06356bSdh pptr = pnext; 2735*4c06356bSdh } 2736*4c06356bSdh 2737*4c06356bSdh return (rval); 2738*4c06356bSdh } 2739*4c06356bSdh 2740*4c06356bSdh /* 2741*4c06356bSdh * Set all phys and descendent phys as changed if changed == B_TRUE, otherwise 2742*4c06356bSdh * mark them all as not changed. 2743*4c06356bSdh * 2744*4c06356bSdh * Called with parent PHY locked. 2745*4c06356bSdh */ 2746*4c06356bSdh void 2747*4c06356bSdh pmcs_set_changed(pmcs_hw_t *pwp, pmcs_phy_t *parent, boolean_t changed, 2748*4c06356bSdh int level) 2749*4c06356bSdh { 2750*4c06356bSdh pmcs_phy_t *pptr; 2751*4c06356bSdh 2752*4c06356bSdh if (level == 0) { 2753*4c06356bSdh if (changed) { 2754*4c06356bSdh PHY_CHANGED(pwp, parent); 2755*4c06356bSdh } else { 2756*4c06356bSdh parent->changed = 0; 2757*4c06356bSdh } 2758*4c06356bSdh if (parent->dtype == EXPANDER && parent->level) { 2759*4c06356bSdh parent->width = 1; 2760*4c06356bSdh } 2761*4c06356bSdh if (parent->children) { 2762*4c06356bSdh pmcs_set_changed(pwp, parent->children, changed, 2763*4c06356bSdh level + 1); 2764*4c06356bSdh } 2765*4c06356bSdh } else { 2766*4c06356bSdh pptr = parent; 2767*4c06356bSdh while (pptr) { 2768*4c06356bSdh if (changed) { 2769*4c06356bSdh PHY_CHANGED(pwp, pptr); 2770*4c06356bSdh } else { 2771*4c06356bSdh pptr->changed = 0; 2772*4c06356bSdh } 2773*4c06356bSdh if (pptr->dtype == EXPANDER && pptr->level) { 2774*4c06356bSdh pptr->width = 1; 2775*4c06356bSdh } 2776*4c06356bSdh if (pptr->children) { 2777*4c06356bSdh pmcs_set_changed(pwp, pptr->children, changed, 2778*4c06356bSdh level + 1); 2779*4c06356bSdh } 2780*4c06356bSdh pptr = pptr->sibling; 2781*4c06356bSdh } 2782*4c06356bSdh } 2783*4c06356bSdh } 2784*4c06356bSdh 2785*4c06356bSdh /* 2786*4c06356bSdh * Take the passed phy mark it and its descendants as dead. 2787*4c06356bSdh * Fire up reconfiguration to abort commands and bury it. 2788*4c06356bSdh * 2789*4c06356bSdh * Called with the parent PHY locked. 2790*4c06356bSdh */ 2791*4c06356bSdh void 2792*4c06356bSdh pmcs_kill_changed(pmcs_hw_t *pwp, pmcs_phy_t *parent, int level) 2793*4c06356bSdh { 2794*4c06356bSdh pmcs_phy_t *pptr = parent; 2795*4c06356bSdh 2796*4c06356bSdh while (pptr) { 2797*4c06356bSdh pptr->link_rate = 0; 2798*4c06356bSdh pptr->abort_sent = 0; 2799*4c06356bSdh pptr->abort_pending = 1; 2800*4c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE); 2801*4c06356bSdh pptr->need_rl_ext = 0; 2802*4c06356bSdh 2803*4c06356bSdh if (pptr->dead == 0) { 2804*4c06356bSdh PHY_CHANGED(pwp, pptr); 2805*4c06356bSdh RESTART_DISCOVERY(pwp); 2806*4c06356bSdh } 2807*4c06356bSdh 2808*4c06356bSdh pptr->dead = 1; 2809*4c06356bSdh 2810*4c06356bSdh if (pptr->children) { 2811*4c06356bSdh pmcs_kill_changed(pwp, pptr->children, level + 1); 2812*4c06356bSdh } 2813*4c06356bSdh 2814*4c06356bSdh /* 2815*4c06356bSdh * Only kill siblings at level > 0 2816*4c06356bSdh */ 2817*4c06356bSdh if (level == 0) { 2818*4c06356bSdh return; 2819*4c06356bSdh } 2820*4c06356bSdh 2821*4c06356bSdh pptr = pptr->sibling; 2822*4c06356bSdh } 2823*4c06356bSdh } 2824*4c06356bSdh 2825*4c06356bSdh /* 2826*4c06356bSdh * Go through every PHY and clear any that are dead (unless they're expanders) 2827*4c06356bSdh */ 2828*4c06356bSdh static void 2829*4c06356bSdh pmcs_clear_phys(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 2830*4c06356bSdh { 2831*4c06356bSdh pmcs_phy_t *pnext, *phyp; 2832*4c06356bSdh 2833*4c06356bSdh phyp = pptr; 2834*4c06356bSdh while (phyp) { 2835*4c06356bSdh if (IS_ROOT_PHY(phyp)) { 2836*4c06356bSdh pmcs_lock_phy(phyp); 2837*4c06356bSdh } 2838*4c06356bSdh 2839*4c06356bSdh if ((phyp->dtype != EXPANDER) && phyp->dead) { 2840*4c06356bSdh pmcs_clear_phy(pwp, phyp); 2841*4c06356bSdh } 2842*4c06356bSdh 2843*4c06356bSdh if (phyp->children) { 2844*4c06356bSdh pmcs_clear_phys(pwp, phyp->children); 2845*4c06356bSdh } 2846*4c06356bSdh 2847*4c06356bSdh pnext = phyp->sibling; 2848*4c06356bSdh 2849*4c06356bSdh if (IS_ROOT_PHY(phyp)) { 2850*4c06356bSdh pmcs_unlock_phy(phyp); 2851*4c06356bSdh } 2852*4c06356bSdh 2853*4c06356bSdh phyp = pnext; 2854*4c06356bSdh } 2855*4c06356bSdh } 2856*4c06356bSdh 2857*4c06356bSdh /* 2858*4c06356bSdh * Clear volatile parts of a phy. Called with PHY locked. 2859*4c06356bSdh */ 2860*4c06356bSdh void 2861*4c06356bSdh pmcs_clear_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 2862*4c06356bSdh { 2863*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: %s", __func__, pptr->path); 2864*4c06356bSdh ASSERT(mutex_owned(&pptr->phy_lock)); 2865*4c06356bSdh /* keep sibling */ 2866*4c06356bSdh /* keep children */ 2867*4c06356bSdh /* keep parent */ 2868*4c06356bSdh pptr->device_id = PMCS_INVALID_DEVICE_ID; 2869*4c06356bSdh /* keep hw_event_ack */ 2870*4c06356bSdh pptr->ncphy = 0; 2871*4c06356bSdh /* keep phynum */ 2872*4c06356bSdh pptr->width = 0; 2873*4c06356bSdh pptr->ds_recovery_retries = 0; 2874*4c06356bSdh /* keep dtype */ 2875*4c06356bSdh pptr->config_stop = 0; 2876*4c06356bSdh pptr->spinup_hold = 0; 2877*4c06356bSdh pptr->atdt = 0; 2878*4c06356bSdh /* keep portid */ 2879*4c06356bSdh pptr->link_rate = 0; 2880*4c06356bSdh pptr->valid_device_id = 0; 2881*4c06356bSdh pptr->abort_sent = 0; 2882*4c06356bSdh pptr->abort_pending = 0; 2883*4c06356bSdh pptr->need_rl_ext = 0; 2884*4c06356bSdh pptr->subsidiary = 0; 2885*4c06356bSdh pptr->configured = 0; 2886*4c06356bSdh /* Only mark dead if it's not a root PHY and its dtype isn't NOTHING */ 2887*4c06356bSdh /* XXX: What about directly attached disks? */ 2888*4c06356bSdh if (!IS_ROOT_PHY(pptr) && (pptr->dtype != NOTHING)) 2889*4c06356bSdh pptr->dead = 1; 2890*4c06356bSdh pptr->changed = 0; 2891*4c06356bSdh /* keep SAS address */ 2892*4c06356bSdh /* keep path */ 2893*4c06356bSdh /* keep ref_count */ 2894*4c06356bSdh /* Don't clear iport on root PHYs - they are handled in pmcs_intr.c */ 2895*4c06356bSdh if (!IS_ROOT_PHY(pptr)) { 2896*4c06356bSdh pptr->iport = NULL; 2897*4c06356bSdh } 2898*4c06356bSdh } 2899*4c06356bSdh 2900*4c06356bSdh /* 2901*4c06356bSdh * Allocate softstate for this target if there isn't already one. If there 2902*4c06356bSdh * is, just redo our internal configuration. If it is actually "new", we'll 2903*4c06356bSdh * soon get a tran_tgt_init for it. 2904*4c06356bSdh * 2905*4c06356bSdh * Called with PHY locked. 2906*4c06356bSdh */ 2907*4c06356bSdh static void 2908*4c06356bSdh pmcs_new_tport(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 2909*4c06356bSdh { 2910*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: phy 0x%p @ %s", __func__, 2911*4c06356bSdh (void *)pptr, pptr->path); 2912*4c06356bSdh 2913*4c06356bSdh if (pmcs_configure_phy(pwp, pptr) == B_FALSE) { 2914*4c06356bSdh /* 2915*4c06356bSdh * If the config failed, mark the PHY as changed. 2916*4c06356bSdh */ 2917*4c06356bSdh PHY_CHANGED(pwp, pptr); 2918*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 2919*4c06356bSdh "%s: pmcs_configure_phy failed for phy 0x%p", __func__, 2920*4c06356bSdh (void *)pptr); 2921*4c06356bSdh return; 2922*4c06356bSdh } 2923*4c06356bSdh 2924*4c06356bSdh /* Mark PHY as no longer changed */ 2925*4c06356bSdh pptr->changed = 0; 2926*4c06356bSdh 2927*4c06356bSdh /* 2928*4c06356bSdh * If the PHY has no target pointer, see if there's a dead PHY that 2929*4c06356bSdh * matches. 2930*4c06356bSdh */ 2931*4c06356bSdh if (pptr->target == NULL) { 2932*4c06356bSdh pmcs_reap_dead_phy(pptr); 2933*4c06356bSdh } 2934*4c06356bSdh 2935*4c06356bSdh /* 2936*4c06356bSdh * Only assign the device if there is a target for this PHY with a 2937*4c06356bSdh * matching SAS address. If an iport is disconnected from one piece 2938*4c06356bSdh * of storage and connected to another within the iport stabilization 2939*4c06356bSdh * time, we can get the PHY/target mismatch situation. 2940*4c06356bSdh * 2941*4c06356bSdh * Otherwise, it'll get done in tran_tgt_init. 2942*4c06356bSdh */ 2943*4c06356bSdh if (pptr->target) { 2944*4c06356bSdh mutex_enter(&pptr->target->statlock); 2945*4c06356bSdh if (pmcs_phy_target_match(pptr) == B_FALSE) { 2946*4c06356bSdh mutex_exit(&pptr->target->statlock); 2947*4c06356bSdh if (!IS_ROOT_PHY(pptr)) { 2948*4c06356bSdh pmcs_dec_phy_ref_count(pptr); 2949*4c06356bSdh } 2950*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 2951*4c06356bSdh "%s: Not assigning existing tgt %p for PHY %p " 2952*4c06356bSdh "(WWN mismatch)", __func__, (void *)pptr->target, 2953*4c06356bSdh (void *)pptr); 2954*4c06356bSdh pptr->target = NULL; 2955*4c06356bSdh return; 2956*4c06356bSdh } 2957*4c06356bSdh 2958*4c06356bSdh if (!pmcs_assign_device(pwp, pptr->target)) { 2959*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 2960*4c06356bSdh "%s: pmcs_assign_device failed for target 0x%p", 2961*4c06356bSdh __func__, (void *)pptr->target); 2962*4c06356bSdh } 2963*4c06356bSdh mutex_exit(&pptr->target->statlock); 2964*4c06356bSdh } 2965*4c06356bSdh } 2966*4c06356bSdh 2967*4c06356bSdh /* 2968*4c06356bSdh * Called with PHY lock held. 2969*4c06356bSdh */ 2970*4c06356bSdh static boolean_t 2971*4c06356bSdh pmcs_configure_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 2972*4c06356bSdh { 2973*4c06356bSdh char *dtype; 2974*4c06356bSdh 2975*4c06356bSdh ASSERT(mutex_owned(&pptr->phy_lock)); 2976*4c06356bSdh 2977*4c06356bSdh /* 2978*4c06356bSdh * Mark this device as no longer changed. 2979*4c06356bSdh */ 2980*4c06356bSdh pptr->changed = 0; 2981*4c06356bSdh 2982*4c06356bSdh /* 2983*4c06356bSdh * If we don't have a device handle, get one. 2984*4c06356bSdh */ 2985*4c06356bSdh if (pmcs_get_device_handle(pwp, pptr)) { 2986*4c06356bSdh return (B_FALSE); 2987*4c06356bSdh } 2988*4c06356bSdh 2989*4c06356bSdh pptr->configured = 1; 2990*4c06356bSdh 2991*4c06356bSdh switch (pptr->dtype) { 2992*4c06356bSdh case SAS: 2993*4c06356bSdh dtype = "SAS"; 2994*4c06356bSdh break; 2995*4c06356bSdh case SATA: 2996*4c06356bSdh dtype = "SATA"; 2997*4c06356bSdh break; 2998*4c06356bSdh case EXPANDER: 2999*4c06356bSdh dtype = "SMP"; 3000*4c06356bSdh break; 3001*4c06356bSdh default: 3002*4c06356bSdh dtype = "???"; 3003*4c06356bSdh } 3004*4c06356bSdh 3005*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "config_dev: %s dev %s " 3006*4c06356bSdh SAS_ADDR_FMT " dev id 0x%x lr 0x%x", dtype, pptr->path, 3007*4c06356bSdh SAS_ADDR_PRT(pptr->sas_address), pptr->device_id, pptr->link_rate); 3008*4c06356bSdh 3009*4c06356bSdh return (B_TRUE); 3010*4c06356bSdh } 3011*4c06356bSdh 3012*4c06356bSdh /* 3013*4c06356bSdh * Called with PHY locked 3014*4c06356bSdh */ 3015*4c06356bSdh static void 3016*4c06356bSdh pmcs_configure_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr, pmcs_iport_t *iport) 3017*4c06356bSdh { 3018*4c06356bSdh pmcs_phy_t *ctmp, *clist = NULL, *cnext; 3019*4c06356bSdh int result, i, nphy = 0; 3020*4c06356bSdh boolean_t root_phy = B_FALSE; 3021*4c06356bSdh 3022*4c06356bSdh ASSERT(iport); 3023*4c06356bSdh 3024*4c06356bSdh /* 3025*4c06356bSdh * Step 1- clear our "changed" bit. If we need to retry/restart due 3026*4c06356bSdh * to resource shortages, we'll set it again. While we're doing 3027*4c06356bSdh * configuration, other events may set it again as well. If the PHY 3028*4c06356bSdh * is a root PHY and is currently marked as having changed, reset the 3029*4c06356bSdh * config_stop timer as well. 3030*4c06356bSdh */ 3031*4c06356bSdh if (IS_ROOT_PHY(pptr) && pptr->changed) { 3032*4c06356bSdh pptr->config_stop = ddi_get_lbolt() + 3033*4c06356bSdh drv_usectohz(PMCS_MAX_CONFIG_TIME); 3034*4c06356bSdh } 3035*4c06356bSdh pptr->changed = 0; 3036*4c06356bSdh 3037*4c06356bSdh /* 3038*4c06356bSdh * Step 2- make sure we don't overflow 3039*4c06356bSdh */ 3040*4c06356bSdh if (pptr->level == PMCS_MAX_XPND-1) { 3041*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_WARN, 3042*4c06356bSdh "%s: SAS expansion tree too deep", __func__); 3043*4c06356bSdh return; 3044*4c06356bSdh } 3045*4c06356bSdh 3046*4c06356bSdh /* 3047*4c06356bSdh * Step 3- Check if this expander is part of a wide phy that has 3048*4c06356bSdh * already been configured. 3049*4c06356bSdh * 3050*4c06356bSdh * This is known by checking this level for another EXPANDER device 3051*4c06356bSdh * with the same SAS address and isn't already marked as a subsidiary 3052*4c06356bSdh * phy and a parent whose SAS address is the same as our SAS address 3053*4c06356bSdh * (if there are parents). 3054*4c06356bSdh */ 3055*4c06356bSdh if (!IS_ROOT_PHY(pptr)) { 3056*4c06356bSdh /* 3057*4c06356bSdh * No need to lock the parent here because we're in discovery 3058*4c06356bSdh * and the only time a PHY's children pointer can change is 3059*4c06356bSdh * in discovery; either in pmcs_clear_expander (which has 3060*4c06356bSdh * already been called) or here, down below. Plus, trying to 3061*4c06356bSdh * grab the parent's lock here can cause deadlock. 3062*4c06356bSdh */ 3063*4c06356bSdh ctmp = pptr->parent->children; 3064*4c06356bSdh } else { 3065*4c06356bSdh ctmp = pwp->root_phys; 3066*4c06356bSdh root_phy = B_TRUE; 3067*4c06356bSdh } 3068*4c06356bSdh 3069*4c06356bSdh while (ctmp) { 3070*4c06356bSdh /* 3071*4c06356bSdh * If we've checked all PHYs up to pptr, we stop. Otherwise, 3072*4c06356bSdh * we'll be checking for a primary PHY with a higher PHY 3073*4c06356bSdh * number than pptr, which will never happen. The primary 3074*4c06356bSdh * PHY on non-root expanders will ALWAYS be the lowest 3075*4c06356bSdh * numbered PHY. 3076*4c06356bSdh */ 3077*4c06356bSdh if (ctmp == pptr) { 3078*4c06356bSdh break; 3079*4c06356bSdh } 3080*4c06356bSdh 3081*4c06356bSdh /* 3082*4c06356bSdh * If pptr and ctmp are root PHYs, just grab the mutex on 3083*4c06356bSdh * ctmp. No need to lock the entire tree. If they are not 3084*4c06356bSdh * root PHYs, there is no need to lock since a non-root PHY's 3085*4c06356bSdh * SAS address and other characteristics can only change in 3086*4c06356bSdh * discovery anyway. 3087*4c06356bSdh */ 3088*4c06356bSdh if (root_phy) { 3089*4c06356bSdh mutex_enter(&ctmp->phy_lock); 3090*4c06356bSdh } 3091*4c06356bSdh 3092*4c06356bSdh if (ctmp->dtype == EXPANDER && ctmp->width && 3093*4c06356bSdh memcmp(ctmp->sas_address, pptr->sas_address, 8) == 0) { 3094*4c06356bSdh int widephy = 0; 3095*4c06356bSdh /* 3096*4c06356bSdh * If these phys are not root PHYs, compare their SAS 3097*4c06356bSdh * addresses too. 3098*4c06356bSdh */ 3099*4c06356bSdh if (!root_phy) { 3100*4c06356bSdh if (memcmp(ctmp->parent->sas_address, 3101*4c06356bSdh pptr->parent->sas_address, 8) == 0) { 3102*4c06356bSdh widephy = 1; 3103*4c06356bSdh } 3104*4c06356bSdh } else { 3105*4c06356bSdh widephy = 1; 3106*4c06356bSdh } 3107*4c06356bSdh if (widephy) { 3108*4c06356bSdh ctmp->width++; 3109*4c06356bSdh pptr->subsidiary = 1; 3110*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: PHY " 3111*4c06356bSdh "%s part of wide PHY %s (now %d wide)", 3112*4c06356bSdh __func__, pptr->path, ctmp->path, 3113*4c06356bSdh ctmp->width); 3114*4c06356bSdh if (root_phy) { 3115*4c06356bSdh mutex_exit(&ctmp->phy_lock); 3116*4c06356bSdh } 3117*4c06356bSdh return; 3118*4c06356bSdh } 3119*4c06356bSdh } 3120*4c06356bSdh 3121*4c06356bSdh cnext = ctmp->sibling; 3122*4c06356bSdh if (root_phy) { 3123*4c06356bSdh mutex_exit(&ctmp->phy_lock); 3124*4c06356bSdh } 3125*4c06356bSdh ctmp = cnext; 3126*4c06356bSdh } 3127*4c06356bSdh 3128*4c06356bSdh /* 3129*4c06356bSdh * Step 4- If we don't have a device handle, get one. Since this 3130*4c06356bSdh * is the primary PHY, make sure subsidiary is cleared. 3131*4c06356bSdh */ 3132*4c06356bSdh pptr->subsidiary = 0; 3133*4c06356bSdh if (pmcs_get_device_handle(pwp, pptr)) { 3134*4c06356bSdh goto out; 3135*4c06356bSdh } 3136*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "Config expander %s " 3137*4c06356bSdh SAS_ADDR_FMT " dev id 0x%x lr 0x%x", pptr->path, 3138*4c06356bSdh SAS_ADDR_PRT(pptr->sas_address), pptr->device_id, pptr->link_rate); 3139*4c06356bSdh 3140*4c06356bSdh /* 3141*4c06356bSdh * Step 5- figure out how many phys are in this expander. 3142*4c06356bSdh */ 3143*4c06356bSdh nphy = pmcs_expander_get_nphy(pwp, pptr); 3144*4c06356bSdh if (nphy <= 0) { 3145*4c06356bSdh if (nphy == 0 && ddi_get_lbolt() < pptr->config_stop) { 3146*4c06356bSdh PHY_CHANGED(pwp, pptr); 3147*4c06356bSdh RESTART_DISCOVERY(pwp); 3148*4c06356bSdh } else { 3149*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 3150*4c06356bSdh "%s: Retries exhausted for %s, killing", __func__, 3151*4c06356bSdh pptr->path); 3152*4c06356bSdh pptr->config_stop = 0; 3153*4c06356bSdh pmcs_kill_changed(pwp, pptr, 0); 3154*4c06356bSdh } 3155*4c06356bSdh goto out; 3156*4c06356bSdh } 3157*4c06356bSdh 3158*4c06356bSdh /* 3159*4c06356bSdh * Step 6- Allocate a list of phys for this expander and figure out 3160*4c06356bSdh * what each one is. 3161*4c06356bSdh */ 3162*4c06356bSdh for (i = 0; i < nphy; i++) { 3163*4c06356bSdh ctmp = kmem_cache_alloc(pwp->phy_cache, KM_SLEEP); 3164*4c06356bSdh bzero(ctmp, sizeof (pmcs_phy_t)); 3165*4c06356bSdh ctmp->device_id = PMCS_INVALID_DEVICE_ID; 3166*4c06356bSdh ctmp->sibling = clist; 3167*4c06356bSdh ctmp->pend_dtype = NEW; /* Init pending dtype */ 3168*4c06356bSdh ctmp->config_stop = ddi_get_lbolt() + 3169*4c06356bSdh drv_usectohz(PMCS_MAX_CONFIG_TIME); 3170*4c06356bSdh clist = ctmp; 3171*4c06356bSdh } 3172*4c06356bSdh 3173*4c06356bSdh mutex_enter(&pwp->config_lock); 3174*4c06356bSdh if (pwp->config_changed) { 3175*4c06356bSdh RESTART_DISCOVERY_LOCKED(pwp); 3176*4c06356bSdh mutex_exit(&pwp->config_lock); 3177*4c06356bSdh /* 3178*4c06356bSdh * Clean up the newly allocated PHYs and return 3179*4c06356bSdh */ 3180*4c06356bSdh while (clist) { 3181*4c06356bSdh ctmp = clist->sibling; 3182*4c06356bSdh kmem_cache_free(pwp->phy_cache, clist); 3183*4c06356bSdh clist = ctmp; 3184*4c06356bSdh } 3185*4c06356bSdh return; 3186*4c06356bSdh } 3187*4c06356bSdh mutex_exit(&pwp->config_lock); 3188*4c06356bSdh 3189*4c06356bSdh /* 3190*4c06356bSdh * Step 7- Now fill in the rest of the static portions of the phy. 3191*4c06356bSdh */ 3192*4c06356bSdh for (i = 0, ctmp = clist; ctmp; ctmp = ctmp->sibling, i++) { 3193*4c06356bSdh ctmp->parent = pptr; 3194*4c06356bSdh ctmp->pwp = pwp; 3195*4c06356bSdh ctmp->level = pptr->level+1; 3196*4c06356bSdh ctmp->portid = pptr->portid; 3197*4c06356bSdh if (ctmp->tolerates_sas2) { 3198*4c06356bSdh ASSERT(i < SAS2_PHYNUM_MAX); 3199*4c06356bSdh ctmp->phynum = i & SAS2_PHYNUM_MASK; 3200*4c06356bSdh } else { 3201*4c06356bSdh ASSERT(i < SAS_PHYNUM_MAX); 3202*4c06356bSdh ctmp->phynum = i & SAS_PHYNUM_MASK; 3203*4c06356bSdh } 3204*4c06356bSdh pmcs_phy_name(pwp, ctmp, ctmp->path, sizeof (ctmp->path)); 3205*4c06356bSdh pmcs_lock_phy(ctmp); 3206*4c06356bSdh } 3207*4c06356bSdh 3208*4c06356bSdh /* 3209*4c06356bSdh * Step 8- Discover things about each phy in the expander. 3210*4c06356bSdh */ 3211*4c06356bSdh for (i = 0, ctmp = clist; ctmp; ctmp = ctmp->sibling, i++) { 3212*4c06356bSdh result = pmcs_expander_content_discover(pwp, pptr, ctmp); 3213*4c06356bSdh if (result <= 0) { 3214*4c06356bSdh if (ddi_get_lbolt() < pptr->config_stop) { 3215*4c06356bSdh PHY_CHANGED(pwp, pptr); 3216*4c06356bSdh RESTART_DISCOVERY(pwp); 3217*4c06356bSdh } else { 3218*4c06356bSdh pptr->config_stop = 0; 3219*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 3220*4c06356bSdh "%s: Retries exhausted for %s, killing", 3221*4c06356bSdh __func__, pptr->path); 3222*4c06356bSdh pmcs_kill_changed(pwp, pptr, 0); 3223*4c06356bSdh } 3224*4c06356bSdh goto out; 3225*4c06356bSdh } 3226*4c06356bSdh 3227*4c06356bSdh /* Set pend_dtype to dtype for 1st time initialization */ 3228*4c06356bSdh ctmp->pend_dtype = ctmp->dtype; 3229*4c06356bSdh } 3230*4c06356bSdh 3231*4c06356bSdh /* 3232*4c06356bSdh * Step 9- Install the new list on the next level. There should be 3233*4c06356bSdh * no children pointer on this PHY. If there is, we'd need to know 3234*4c06356bSdh * how it happened (The expander suddenly got more PHYs?). 3235*4c06356bSdh */ 3236*4c06356bSdh ASSERT(pptr->children == NULL); 3237*4c06356bSdh if (pptr->children != NULL) { 3238*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Already child PHYs attached " 3239*4c06356bSdh " to PHY %s: This should never happen", __func__, 3240*4c06356bSdh pptr->path); 3241*4c06356bSdh goto out; 3242*4c06356bSdh } else { 3243*4c06356bSdh pptr->children = clist; 3244*4c06356bSdh } 3245*4c06356bSdh 3246*4c06356bSdh clist = NULL; 3247*4c06356bSdh pptr->ncphy = nphy; 3248*4c06356bSdh pptr->configured = 1; 3249*4c06356bSdh 3250*4c06356bSdh /* 3251*4c06356bSdh * We only set width if we're greater than level 0. 3252*4c06356bSdh */ 3253*4c06356bSdh if (pptr->level) { 3254*4c06356bSdh pptr->width = 1; 3255*4c06356bSdh } 3256*4c06356bSdh 3257*4c06356bSdh /* 3258*4c06356bSdh * Now tell the rest of the world about us, as an SMP node. 3259*4c06356bSdh */ 3260*4c06356bSdh pptr->iport = iport; 3261*4c06356bSdh pmcs_new_tport(pwp, pptr); 3262*4c06356bSdh 3263*4c06356bSdh out: 3264*4c06356bSdh while (clist) { 3265*4c06356bSdh ctmp = clist->sibling; 3266*4c06356bSdh pmcs_unlock_phy(clist); 3267*4c06356bSdh kmem_cache_free(pwp->phy_cache, clist); 3268*4c06356bSdh clist = ctmp; 3269*4c06356bSdh } 3270*4c06356bSdh } 3271*4c06356bSdh 3272*4c06356bSdh /* 3273*4c06356bSdh * 2. Check expanders marked changed (but not dead) to see if they still have 3274*4c06356bSdh * the same number of phys and the same SAS address. Mark them, their subsidiary 3275*4c06356bSdh * phys (if wide) and their descendents dead if anything has changed. Check the 3276*4c06356bSdh * the devices they contain to see if *they* have changed. If they've changed 3277*4c06356bSdh * from type NOTHING we leave them marked changed to be configured later 3278*4c06356bSdh * (picking up a new SAS address and link rate if possible). Otherwise, any 3279*4c06356bSdh * change in type, SAS address or removal of target role will cause us to 3280*4c06356bSdh * mark them (and their descendents) as dead and cause any pending commands 3281*4c06356bSdh * and associated devices to be removed. 3282*4c06356bSdh * 3283*4c06356bSdh * Called with PHY (pptr) locked. 3284*4c06356bSdh */ 3285*4c06356bSdh 3286*4c06356bSdh static void 3287*4c06356bSdh pmcs_check_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 3288*4c06356bSdh { 3289*4c06356bSdh int nphy, result; 3290*4c06356bSdh pmcs_phy_t *ctmp, *local, *local_list = NULL, *local_tail = NULL; 3291*4c06356bSdh boolean_t kill_changed, changed; 3292*4c06356bSdh 3293*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 3294*4c06356bSdh "%s: check %s", __func__, pptr->path); 3295*4c06356bSdh 3296*4c06356bSdh /* 3297*4c06356bSdh * Step 1: Mark phy as not changed. We will mark it changed if we need 3298*4c06356bSdh * to retry. 3299*4c06356bSdh */ 3300*4c06356bSdh pptr->changed = 0; 3301*4c06356bSdh 3302*4c06356bSdh /* 3303*4c06356bSdh * Reset the config_stop time. Although we're not actually configuring 3304*4c06356bSdh * anything here, we do want some indication of when to give up trying 3305*4c06356bSdh * if we can't communicate with the expander. 3306*4c06356bSdh */ 3307*4c06356bSdh pptr->config_stop = ddi_get_lbolt() + 3308*4c06356bSdh drv_usectohz(PMCS_MAX_CONFIG_TIME); 3309*4c06356bSdh 3310*4c06356bSdh /* 3311*4c06356bSdh * Step 2: Figure out how many phys are in this expander. If 3312*4c06356bSdh * pmcs_expander_get_nphy returns 0 we ran out of resources, 3313*4c06356bSdh * so reschedule and try later. If it returns another error, 3314*4c06356bSdh * just return. 3315*4c06356bSdh */ 3316*4c06356bSdh nphy = pmcs_expander_get_nphy(pwp, pptr); 3317*4c06356bSdh if (nphy <= 0) { 3318*4c06356bSdh if ((nphy == 0) && (ddi_get_lbolt() < pptr->config_stop)) { 3319*4c06356bSdh PHY_CHANGED(pwp, pptr); 3320*4c06356bSdh RESTART_DISCOVERY(pwp); 3321*4c06356bSdh } else { 3322*4c06356bSdh pptr->config_stop = 0; 3323*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 3324*4c06356bSdh "%s: Retries exhausted for %s, killing", __func__, 3325*4c06356bSdh pptr->path); 3326*4c06356bSdh pmcs_kill_changed(pwp, pptr, 0); 3327*4c06356bSdh } 3328*4c06356bSdh return; 3329*4c06356bSdh } 3330*4c06356bSdh 3331*4c06356bSdh /* 3332*4c06356bSdh * Step 3: If the number of phys don't agree, kill the old sub-tree. 3333*4c06356bSdh */ 3334*4c06356bSdh if (nphy != pptr->ncphy) { 3335*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 3336*4c06356bSdh "%s: number of contained phys for %s changed from %d to %d", 3337*4c06356bSdh __func__, pptr->path, pptr->ncphy, nphy); 3338*4c06356bSdh /* 3339*4c06356bSdh * Force a rescan of this expander after dead contents 3340*4c06356bSdh * are cleared and removed. 3341*4c06356bSdh */ 3342*4c06356bSdh pmcs_kill_changed(pwp, pptr, 0); 3343*4c06356bSdh return; 3344*4c06356bSdh } 3345*4c06356bSdh 3346*4c06356bSdh /* 3347*4c06356bSdh * Step 4: if we're at the bottom of the stack, we're done 3348*4c06356bSdh * (we can't have any levels below us) 3349*4c06356bSdh */ 3350*4c06356bSdh if (pptr->level == PMCS_MAX_XPND-1) { 3351*4c06356bSdh return; 3352*4c06356bSdh } 3353*4c06356bSdh 3354*4c06356bSdh /* 3355*4c06356bSdh * Step 5: Discover things about each phy in this expander. We do 3356*4c06356bSdh * this by walking the current list of contained phys and doing a 3357*4c06356bSdh * content discovery for it to a local phy. 3358*4c06356bSdh */ 3359*4c06356bSdh ctmp = pptr->children; 3360*4c06356bSdh ASSERT(ctmp); 3361*4c06356bSdh if (ctmp == NULL) { 3362*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 3363*4c06356bSdh "%s: No children attached to expander @ %s?", __func__, 3364*4c06356bSdh pptr->path); 3365*4c06356bSdh return; 3366*4c06356bSdh } 3367*4c06356bSdh 3368*4c06356bSdh while (ctmp) { 3369*4c06356bSdh /* 3370*4c06356bSdh * Allocate a local PHY to contain the proposed new contents 3371*4c06356bSdh * and link it to the rest of the local PHYs so that they 3372*4c06356bSdh * can all be freed later. 3373*4c06356bSdh */ 3374*4c06356bSdh local = pmcs_clone_phy(ctmp); 3375*4c06356bSdh 3376*4c06356bSdh if (local_list == NULL) { 3377*4c06356bSdh local_list = local; 3378*4c06356bSdh local_tail = local; 3379*4c06356bSdh } else { 3380*4c06356bSdh local_tail->sibling = local; 3381*4c06356bSdh local_tail = local; 3382*4c06356bSdh } 3383*4c06356bSdh 3384*4c06356bSdh /* 3385*4c06356bSdh * Need to lock the local PHY since pmcs_expander_content_ 3386*4c06356bSdh * discovery may call pmcs_clear_phy on it, which expects 3387*4c06356bSdh * the PHY to be locked. 3388*4c06356bSdh */ 3389*4c06356bSdh pmcs_lock_phy(local); 3390*4c06356bSdh result = pmcs_expander_content_discover(pwp, pptr, local); 3391*4c06356bSdh pmcs_unlock_phy(local); 3392*4c06356bSdh if (result <= 0) { 3393*4c06356bSdh if (ddi_get_lbolt() < pptr->config_stop) { 3394*4c06356bSdh PHY_CHANGED(pwp, pptr); 3395*4c06356bSdh RESTART_DISCOVERY(pwp); 3396*4c06356bSdh } else { 3397*4c06356bSdh pptr->config_stop = 0; 3398*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 3399*4c06356bSdh "%s: Retries exhausted for %s, killing", 3400*4c06356bSdh __func__, pptr->path); 3401*4c06356bSdh pmcs_kill_changed(pwp, pptr, 0); 3402*4c06356bSdh } 3403*4c06356bSdh 3404*4c06356bSdh /* 3405*4c06356bSdh * Release all the local PHYs that we allocated. 3406*4c06356bSdh */ 3407*4c06356bSdh pmcs_free_phys(pwp, local_list); 3408*4c06356bSdh return; 3409*4c06356bSdh } 3410*4c06356bSdh 3411*4c06356bSdh ctmp = ctmp->sibling; 3412*4c06356bSdh } 3413*4c06356bSdh 3414*4c06356bSdh /* 3415*4c06356bSdh * Step 6: Compare the local PHY's contents to our current PHY. If 3416*4c06356bSdh * there are changes, take the appropriate action. 3417*4c06356bSdh * This is done in two steps (step 5 above, and 6 here) so that if we 3418*4c06356bSdh * have to bail during this process (e.g. pmcs_expander_content_discover 3419*4c06356bSdh * fails), we haven't actually changed the state of any of the real 3420*4c06356bSdh * PHYs. Next time we come through here, we'll be starting over from 3421*4c06356bSdh * scratch. This keeps us from marking a changed PHY as no longer 3422*4c06356bSdh * changed, but then having to bail only to come back next time and 3423*4c06356bSdh * think that the PHY hadn't changed. If this were to happen, we 3424*4c06356bSdh * would fail to properly configure the device behind this PHY. 3425*4c06356bSdh */ 3426*4c06356bSdh local = local_list; 3427*4c06356bSdh ctmp = pptr->children; 3428*4c06356bSdh 3429*4c06356bSdh while (ctmp) { 3430*4c06356bSdh changed = B_FALSE; 3431*4c06356bSdh kill_changed = B_FALSE; 3432*4c06356bSdh 3433*4c06356bSdh /* 3434*4c06356bSdh * We set local to local_list prior to this loop so that we 3435*4c06356bSdh * can simply walk the local_list while we walk this list. The 3436*4c06356bSdh * two lists should be completely in sync. 3437*4c06356bSdh * 3438*4c06356bSdh * Clear the changed flag here. 3439*4c06356bSdh */ 3440*4c06356bSdh ctmp->changed = 0; 3441*4c06356bSdh 3442*4c06356bSdh if (ctmp->dtype != local->dtype) { 3443*4c06356bSdh if (ctmp->dtype != NOTHING) { 3444*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: %s " 3445*4c06356bSdh "type changed from %s to %s (killing)", 3446*4c06356bSdh __func__, ctmp->path, PHY_TYPE(ctmp), 3447*4c06356bSdh PHY_TYPE(local)); 3448*4c06356bSdh /* 3449*4c06356bSdh * Force a rescan of this expander after dead 3450*4c06356bSdh * contents are cleared and removed. 3451*4c06356bSdh */ 3452*4c06356bSdh changed = B_TRUE; 3453*4c06356bSdh kill_changed = B_TRUE; 3454*4c06356bSdh } else { 3455*4c06356bSdh changed = B_TRUE; 3456*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 3457*4c06356bSdh "%s: %s type changed from NOTHING to %s", 3458*4c06356bSdh __func__, ctmp->path, PHY_TYPE(local)); 3459*4c06356bSdh } 3460*4c06356bSdh 3461*4c06356bSdh } else if (ctmp->atdt != local->atdt) { 3462*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: %s attached " 3463*4c06356bSdh "device type changed from %d to %d (killing)", 3464*4c06356bSdh __func__, ctmp->path, ctmp->atdt, local->atdt); 3465*4c06356bSdh /* 3466*4c06356bSdh * Force a rescan of this expander after dead 3467*4c06356bSdh * contents are cleared and removed. 3468*4c06356bSdh */ 3469*4c06356bSdh changed = B_TRUE; 3470*4c06356bSdh 3471*4c06356bSdh if (local->atdt == 0) { 3472*4c06356bSdh kill_changed = B_TRUE; 3473*4c06356bSdh } 3474*4c06356bSdh } else if (ctmp->link_rate != local->link_rate) { 3475*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, "%s: %s changed speed from" 3476*4c06356bSdh " %s to %s", __func__, ctmp->path, 3477*4c06356bSdh pmcs_get_rate(ctmp->link_rate), 3478*4c06356bSdh pmcs_get_rate(local->link_rate)); 3479*4c06356bSdh /* If the speed changed from invalid, force rescan */ 3480*4c06356bSdh if (!PMCS_VALID_LINK_RATE(ctmp->link_rate)) { 3481*4c06356bSdh changed = B_TRUE; 3482*4c06356bSdh RESTART_DISCOVERY(pwp); 3483*4c06356bSdh } else { 3484*4c06356bSdh /* Just update to the new link rate */ 3485*4c06356bSdh ctmp->link_rate = local->link_rate; 3486*4c06356bSdh } 3487*4c06356bSdh 3488*4c06356bSdh if (!PMCS_VALID_LINK_RATE(local->link_rate)) { 3489*4c06356bSdh kill_changed = B_TRUE; 3490*4c06356bSdh } 3491*4c06356bSdh } else if (memcmp(ctmp->sas_address, local->sas_address, 3492*4c06356bSdh sizeof (ctmp->sas_address)) != 0) { 3493*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: SASAddr " 3494*4c06356bSdh "for %s changed from " SAS_ADDR_FMT " to " 3495*4c06356bSdh SAS_ADDR_FMT " (kill old tree)", __func__, 3496*4c06356bSdh ctmp->path, SAS_ADDR_PRT(ctmp->sas_address), 3497*4c06356bSdh SAS_ADDR_PRT(local->sas_address)); 3498*4c06356bSdh /* 3499*4c06356bSdh * Force a rescan of this expander after dead 3500*4c06356bSdh * contents are cleared and removed. 3501*4c06356bSdh */ 3502*4c06356bSdh changed = B_TRUE; 3503*4c06356bSdh } else { 3504*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 3505*4c06356bSdh "%s: %s looks the same (type %s)", 3506*4c06356bSdh __func__, ctmp->path, PHY_TYPE(ctmp)); 3507*4c06356bSdh /* 3508*4c06356bSdh * If EXPANDER, still mark it changed so we 3509*4c06356bSdh * re-evaluate its contents. If it's not an expander, 3510*4c06356bSdh * but it hasn't been configured, also mark it as 3511*4c06356bSdh * changed so that it will undergo configuration. 3512*4c06356bSdh */ 3513*4c06356bSdh if (ctmp->dtype == EXPANDER) { 3514*4c06356bSdh changed = B_TRUE; 3515*4c06356bSdh } else if ((ctmp->dtype != NOTHING) && 3516*4c06356bSdh !ctmp->configured) { 3517*4c06356bSdh ctmp->changed = 1; 3518*4c06356bSdh } else { 3519*4c06356bSdh /* It simply hasn't changed */ 3520*4c06356bSdh ctmp->changed = 0; 3521*4c06356bSdh } 3522*4c06356bSdh } 3523*4c06356bSdh 3524*4c06356bSdh /* 3525*4c06356bSdh * If the PHY changed, call pmcs_kill_changed if indicated, 3526*4c06356bSdh * update its contents to reflect its current state and mark it 3527*4c06356bSdh * as changed. 3528*4c06356bSdh */ 3529*4c06356bSdh if (changed) { 3530*4c06356bSdh /* 3531*4c06356bSdh * pmcs_kill_changed will mark the PHY as changed, so 3532*4c06356bSdh * only do PHY_CHANGED if we did not do kill_changed. 3533*4c06356bSdh */ 3534*4c06356bSdh if (kill_changed) { 3535*4c06356bSdh pmcs_kill_changed(pwp, ctmp, 0); 3536*4c06356bSdh } else { 3537*4c06356bSdh /* 3538*4c06356bSdh * If we're not killing the device, it's not 3539*4c06356bSdh * dead. Mark the PHY as changed. 3540*4c06356bSdh */ 3541*4c06356bSdh PHY_CHANGED(pwp, ctmp); 3542*4c06356bSdh 3543*4c06356bSdh if (ctmp->dead) { 3544*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 3545*4c06356bSdh "%s: Unmarking PHY %s dead, " 3546*4c06356bSdh "restarting discovery", 3547*4c06356bSdh __func__, ctmp->path); 3548*4c06356bSdh ctmp->dead = 0; 3549*4c06356bSdh RESTART_DISCOVERY(pwp); 3550*4c06356bSdh } 3551*4c06356bSdh } 3552*4c06356bSdh 3553*4c06356bSdh /* 3554*4c06356bSdh * If the dtype of this PHY is now NOTHING, mark it as 3555*4c06356bSdh * unconfigured. Set pend_dtype to what the new dtype 3556*4c06356bSdh * is. It'll get updated at the end of the discovery 3557*4c06356bSdh * process. 3558*4c06356bSdh */ 3559*4c06356bSdh if (local->dtype == NOTHING) { 3560*4c06356bSdh bzero(ctmp->sas_address, 3561*4c06356bSdh sizeof (local->sas_address)); 3562*4c06356bSdh ctmp->atdt = 0; 3563*4c06356bSdh ctmp->link_rate = 0; 3564*4c06356bSdh ctmp->pend_dtype = NOTHING; 3565*4c06356bSdh ctmp->configured = 0; 3566*4c06356bSdh } else { 3567*4c06356bSdh (void) memcpy(ctmp->sas_address, 3568*4c06356bSdh local->sas_address, 3569*4c06356bSdh sizeof (local->sas_address)); 3570*4c06356bSdh ctmp->atdt = local->atdt; 3571*4c06356bSdh ctmp->link_rate = local->link_rate; 3572*4c06356bSdh ctmp->pend_dtype = local->dtype; 3573*4c06356bSdh } 3574*4c06356bSdh } 3575*4c06356bSdh 3576*4c06356bSdh local = local->sibling; 3577*4c06356bSdh ctmp = ctmp->sibling; 3578*4c06356bSdh } 3579*4c06356bSdh 3580*4c06356bSdh /* 3581*4c06356bSdh * If we got to here, that means we were able to see all the PHYs 3582*4c06356bSdh * and we can now update all of the real PHYs with the information 3583*4c06356bSdh * we got on the local PHYs. Once that's done, free all the local 3584*4c06356bSdh * PHYs. 3585*4c06356bSdh */ 3586*4c06356bSdh 3587*4c06356bSdh pmcs_free_phys(pwp, local_list); 3588*4c06356bSdh } 3589*4c06356bSdh 3590*4c06356bSdh /* 3591*4c06356bSdh * Top level routine to check expanders. We call pmcs_check_expander for 3592*4c06356bSdh * each expander. Since we're not doing any configuration right now, it 3593*4c06356bSdh * doesn't matter if this is breadth-first. 3594*4c06356bSdh */ 3595*4c06356bSdh static boolean_t 3596*4c06356bSdh pmcs_check_expanders(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 3597*4c06356bSdh { 3598*4c06356bSdh pmcs_phy_t *phyp, *pnext, *pchild; 3599*4c06356bSdh boolean_t config_changed = B_FALSE; 3600*4c06356bSdh 3601*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: %s", __func__, pptr->path); 3602*4c06356bSdh 3603*4c06356bSdh /* 3604*4c06356bSdh * Check each expander at this level 3605*4c06356bSdh */ 3606*4c06356bSdh phyp = pptr; 3607*4c06356bSdh while (phyp && !config_changed) { 3608*4c06356bSdh pmcs_lock_phy(phyp); 3609*4c06356bSdh 3610*4c06356bSdh if ((phyp->dtype == EXPANDER) && phyp->changed && 3611*4c06356bSdh !phyp->dead && !phyp->subsidiary && 3612*4c06356bSdh phyp->configured) { 3613*4c06356bSdh pmcs_check_expander(pwp, phyp); 3614*4c06356bSdh } 3615*4c06356bSdh 3616*4c06356bSdh pnext = phyp->sibling; 3617*4c06356bSdh pmcs_unlock_phy(phyp); 3618*4c06356bSdh 3619*4c06356bSdh mutex_enter(&pwp->config_lock); 3620*4c06356bSdh config_changed = pwp->config_changed; 3621*4c06356bSdh mutex_exit(&pwp->config_lock); 3622*4c06356bSdh 3623*4c06356bSdh phyp = pnext; 3624*4c06356bSdh } 3625*4c06356bSdh 3626*4c06356bSdh if (config_changed) { 3627*4c06356bSdh return (config_changed); 3628*4c06356bSdh } 3629*4c06356bSdh 3630*4c06356bSdh /* 3631*4c06356bSdh * Now check the children 3632*4c06356bSdh */ 3633*4c06356bSdh phyp = pptr; 3634*4c06356bSdh while (phyp && !config_changed) { 3635*4c06356bSdh pmcs_lock_phy(phyp); 3636*4c06356bSdh pnext = phyp->sibling; 3637*4c06356bSdh pchild = phyp->children; 3638*4c06356bSdh pmcs_unlock_phy(phyp); 3639*4c06356bSdh 3640*4c06356bSdh if (pchild) { 3641*4c06356bSdh (void) pmcs_check_expanders(pwp, pchild); 3642*4c06356bSdh } 3643*4c06356bSdh 3644*4c06356bSdh mutex_enter(&pwp->config_lock); 3645*4c06356bSdh config_changed = pwp->config_changed; 3646*4c06356bSdh mutex_exit(&pwp->config_lock); 3647*4c06356bSdh 3648*4c06356bSdh phyp = pnext; 3649*4c06356bSdh } 3650*4c06356bSdh 3651*4c06356bSdh /* 3652*4c06356bSdh * We're done 3653*4c06356bSdh */ 3654*4c06356bSdh return (config_changed); 3655*4c06356bSdh } 3656*4c06356bSdh 3657*4c06356bSdh /* 3658*4c06356bSdh * Called with softstate and PHY locked 3659*4c06356bSdh */ 3660*4c06356bSdh static void 3661*4c06356bSdh pmcs_clear_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr, int level) 3662*4c06356bSdh { 3663*4c06356bSdh pmcs_phy_t *ctmp; 3664*4c06356bSdh 3665*4c06356bSdh ASSERT(mutex_owned(&pwp->lock)); 3666*4c06356bSdh ASSERT(mutex_owned(&pptr->phy_lock)); 3667*4c06356bSdh ASSERT(pptr->level < PMCS_MAX_XPND - 1); 3668*4c06356bSdh 3669*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: checking %s", __func__, 3670*4c06356bSdh pptr->path); 3671*4c06356bSdh 3672*4c06356bSdh ctmp = pptr->children; 3673*4c06356bSdh while (ctmp) { 3674*4c06356bSdh /* 3675*4c06356bSdh * If the expander is dead, mark its children dead 3676*4c06356bSdh */ 3677*4c06356bSdh if (pptr->dead) { 3678*4c06356bSdh ctmp->dead = 1; 3679*4c06356bSdh } 3680*4c06356bSdh if (ctmp->dtype == EXPANDER) { 3681*4c06356bSdh pmcs_clear_expander(pwp, ctmp, level + 1); 3682*4c06356bSdh } 3683*4c06356bSdh ctmp = ctmp->sibling; 3684*4c06356bSdh } 3685*4c06356bSdh 3686*4c06356bSdh /* 3687*4c06356bSdh * If this expander is not dead, we're done here. 3688*4c06356bSdh */ 3689*4c06356bSdh if (!pptr->dead) { 3690*4c06356bSdh return; 3691*4c06356bSdh } 3692*4c06356bSdh 3693*4c06356bSdh /* 3694*4c06356bSdh * Now snip out the list of children below us and release them 3695*4c06356bSdh */ 3696*4c06356bSdh ctmp = pptr->children; 3697*4c06356bSdh while (ctmp) { 3698*4c06356bSdh pmcs_phy_t *nxt = ctmp->sibling; 3699*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 3700*4c06356bSdh "%s: dead PHY 0x%p (%s) (ref_count %d)", __func__, 3701*4c06356bSdh (void *)ctmp, ctmp->path, ctmp->ref_count); 3702*4c06356bSdh /* 3703*4c06356bSdh * Put this PHY on the dead PHY list for the watchdog to 3704*4c06356bSdh * clean up after any outstanding work has completed. 3705*4c06356bSdh */ 3706*4c06356bSdh mutex_enter(&pwp->dead_phylist_lock); 3707*4c06356bSdh ctmp->dead_next = pwp->dead_phys; 3708*4c06356bSdh pwp->dead_phys = ctmp; 3709*4c06356bSdh mutex_exit(&pwp->dead_phylist_lock); 3710*4c06356bSdh pmcs_unlock_phy(ctmp); 3711*4c06356bSdh ctmp = nxt; 3712*4c06356bSdh } 3713*4c06356bSdh 3714*4c06356bSdh pptr->children = NULL; 3715*4c06356bSdh 3716*4c06356bSdh /* 3717*4c06356bSdh * Clear subsidiary phys as well. Getting the parent's PHY lock 3718*4c06356bSdh * is only necessary if level == 0 since otherwise the parent is 3719*4c06356bSdh * already locked. 3720*4c06356bSdh */ 3721*4c06356bSdh if (!IS_ROOT_PHY(pptr)) { 3722*4c06356bSdh if (level == 0) { 3723*4c06356bSdh mutex_enter(&pptr->parent->phy_lock); 3724*4c06356bSdh } 3725*4c06356bSdh ctmp = pptr->parent->children; 3726*4c06356bSdh if (level == 0) { 3727*4c06356bSdh mutex_exit(&pptr->parent->phy_lock); 3728*4c06356bSdh } 3729*4c06356bSdh } else { 3730*4c06356bSdh ctmp = pwp->root_phys; 3731*4c06356bSdh } 3732*4c06356bSdh 3733*4c06356bSdh while (ctmp) { 3734*4c06356bSdh if (ctmp == pptr) { 3735*4c06356bSdh ctmp = ctmp->sibling; 3736*4c06356bSdh continue; 3737*4c06356bSdh } 3738*4c06356bSdh /* 3739*4c06356bSdh * We only need to lock subsidiary PHYs on the level 0 3740*4c06356bSdh * expander. Any children of that expander, subsidiaries or 3741*4c06356bSdh * not, will already be locked. 3742*4c06356bSdh */ 3743*4c06356bSdh if (level == 0) { 3744*4c06356bSdh pmcs_lock_phy(ctmp); 3745*4c06356bSdh } 3746*4c06356bSdh if (ctmp->dtype != EXPANDER || ctmp->subsidiary == 0 || 3747*4c06356bSdh memcmp(ctmp->sas_address, pptr->sas_address, 3748*4c06356bSdh sizeof (ctmp->sas_address)) != 0) { 3749*4c06356bSdh if (level == 0) { 3750*4c06356bSdh pmcs_unlock_phy(ctmp); 3751*4c06356bSdh } 3752*4c06356bSdh ctmp = ctmp->sibling; 3753*4c06356bSdh continue; 3754*4c06356bSdh } 3755*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: subsidiary %s", 3756*4c06356bSdh __func__, ctmp->path); 3757*4c06356bSdh pmcs_clear_phy(pwp, ctmp); 3758*4c06356bSdh if (level == 0) { 3759*4c06356bSdh pmcs_unlock_phy(ctmp); 3760*4c06356bSdh } 3761*4c06356bSdh ctmp = ctmp->sibling; 3762*4c06356bSdh } 3763*4c06356bSdh 3764*4c06356bSdh pmcs_clear_phy(pwp, pptr); 3765*4c06356bSdh } 3766*4c06356bSdh 3767*4c06356bSdh /* 3768*4c06356bSdh * Called with PHY locked and with scratch acquired. We return 0 if 3769*4c06356bSdh * we fail to allocate resources or notice that the configuration 3770*4c06356bSdh * count changed while we were running the command. We return 3771*4c06356bSdh * less than zero if we had an I/O error or received an unsupported 3772*4c06356bSdh * configuration. Otherwise we return the number of phys in the 3773*4c06356bSdh * expander. 3774*4c06356bSdh */ 3775*4c06356bSdh #define DFM(m, y) if (m == NULL) m = y 3776*4c06356bSdh static int 3777*4c06356bSdh pmcs_expander_get_nphy(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 3778*4c06356bSdh { 3779*4c06356bSdh struct pmcwork *pwrk; 3780*4c06356bSdh char buf[64]; 3781*4c06356bSdh const uint_t rdoff = 0x100; /* returned data offset */ 3782*4c06356bSdh smp_response_frame_t *srf; 3783*4c06356bSdh smp_report_general_resp_t *srgr; 3784*4c06356bSdh uint32_t msg[PMCS_MSG_SIZE], *ptr, htag, status, ival; 3785*4c06356bSdh int result; 3786*4c06356bSdh 3787*4c06356bSdh ival = 0x40001100; 3788*4c06356bSdh again: 3789*4c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 3790*4c06356bSdh if (pwrk == NULL) { 3791*4c06356bSdh result = 0; 3792*4c06356bSdh goto out; 3793*4c06356bSdh } 3794*4c06356bSdh (void) memset(pwp->scratch, 0x77, PMCS_SCRATCH_SIZE); 3795*4c06356bSdh pwrk->arg = pwp->scratch; 3796*4c06356bSdh pwrk->dtype = pptr->dtype; 3797*4c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 3798*4c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 3799*4c06356bSdh if (ptr == NULL) { 3800*4c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 3801*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, "%s: GET_IQ_ENTRY failed", 3802*4c06356bSdh __func__); 3803*4c06356bSdh pmcs_pwork(pwp, pwrk); 3804*4c06356bSdh result = 0; 3805*4c06356bSdh goto out; 3806*4c06356bSdh } 3807*4c06356bSdh 3808*4c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST)); 3809*4c06356bSdh msg[1] = LE_32(pwrk->htag); 3810*4c06356bSdh msg[2] = LE_32(pptr->device_id); 3811*4c06356bSdh msg[3] = LE_32((4 << SMP_REQUEST_LENGTH_SHIFT) | SMP_INDIRECT_RESPONSE); 3812*4c06356bSdh /* 3813*4c06356bSdh * Send SMP REPORT GENERAL (of either SAS1.1 or SAS2 flavors). 3814*4c06356bSdh */ 3815*4c06356bSdh msg[4] = BE_32(ival); 3816*4c06356bSdh msg[5] = 0; 3817*4c06356bSdh msg[6] = 0; 3818*4c06356bSdh msg[7] = 0; 3819*4c06356bSdh msg[8] = 0; 3820*4c06356bSdh msg[9] = 0; 3821*4c06356bSdh msg[10] = 0; 3822*4c06356bSdh msg[11] = 0; 3823*4c06356bSdh msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff)); 3824*4c06356bSdh msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff)); 3825*4c06356bSdh msg[14] = LE_32(PMCS_SCRATCH_SIZE - rdoff); 3826*4c06356bSdh msg[15] = 0; 3827*4c06356bSdh 3828*4c06356bSdh COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE); 3829*4c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 3830*4c06356bSdh htag = pwrk->htag; 3831*4c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 3832*4c06356bSdh 3833*4c06356bSdh pmcs_unlock_phy(pptr); 3834*4c06356bSdh WAIT_FOR(pwrk, 1000, result); 3835*4c06356bSdh pmcs_lock_phy(pptr); 3836*4c06356bSdh pmcs_pwork(pwp, pwrk); 3837*4c06356bSdh 3838*4c06356bSdh mutex_enter(&pwp->config_lock); 3839*4c06356bSdh if (pwp->config_changed) { 3840*4c06356bSdh RESTART_DISCOVERY_LOCKED(pwp); 3841*4c06356bSdh mutex_exit(&pwp->config_lock); 3842*4c06356bSdh result = 0; 3843*4c06356bSdh goto out; 3844*4c06356bSdh } 3845*4c06356bSdh mutex_exit(&pwp->config_lock); 3846*4c06356bSdh 3847*4c06356bSdh if (result) { 3848*4c06356bSdh pmcs_timed_out(pwp, htag, __func__); 3849*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 3850*4c06356bSdh "%s: Issuing SMP ABORT for htag 0x%08x", __func__, htag); 3851*4c06356bSdh if (pmcs_abort(pwp, pptr, htag, 0, 0)) { 3852*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 3853*4c06356bSdh "%s: Unable to issue SMP ABORT for htag 0x%08x", 3854*4c06356bSdh __func__, htag); 3855*4c06356bSdh } else { 3856*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 3857*4c06356bSdh "%s: Issuing SMP ABORT for htag 0x%08x", 3858*4c06356bSdh __func__, htag); 3859*4c06356bSdh } 3860*4c06356bSdh result = 0; 3861*4c06356bSdh goto out; 3862*4c06356bSdh } 3863*4c06356bSdh ptr = (void *)pwp->scratch; 3864*4c06356bSdh status = LE_32(ptr[2]); 3865*4c06356bSdh if (status == PMCOUT_STATUS_UNDERFLOW || 3866*4c06356bSdh status == PMCOUT_STATUS_OVERFLOW) { 3867*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, 3868*4c06356bSdh "%s: over/underflow", __func__); 3869*4c06356bSdh status = PMCOUT_STATUS_OK; 3870*4c06356bSdh } 3871*4c06356bSdh srf = (smp_response_frame_t *)&((uint32_t *)pwp->scratch)[rdoff >> 2]; 3872*4c06356bSdh srgr = (smp_report_general_resp_t *) 3873*4c06356bSdh &((uint32_t *)pwp->scratch)[(rdoff >> 2)+1]; 3874*4c06356bSdh 3875*4c06356bSdh if (status != PMCOUT_STATUS_OK) { 3876*4c06356bSdh char *nag = NULL; 3877*4c06356bSdh (void) snprintf(buf, sizeof (buf), 3878*4c06356bSdh "%s: SMP op failed (0x%x)", __func__, status); 3879*4c06356bSdh switch (status) { 3880*4c06356bSdh case PMCOUT_STATUS_IO_PORT_IN_RESET: 3881*4c06356bSdh DFM(nag, "I/O Port In Reset"); 3882*4c06356bSdh /* FALLTHROUGH */ 3883*4c06356bSdh case PMCOUT_STATUS_ERROR_HW_TIMEOUT: 3884*4c06356bSdh DFM(nag, "Hardware Timeout"); 3885*4c06356bSdh /* FALLTHROUGH */ 3886*4c06356bSdh case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE: 3887*4c06356bSdh DFM(nag, "Internal SMP Resource Failure"); 3888*4c06356bSdh /* FALLTHROUGH */ 3889*4c06356bSdh case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY: 3890*4c06356bSdh DFM(nag, "PHY Not Ready"); 3891*4c06356bSdh /* FALLTHROUGH */ 3892*4c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: 3893*4c06356bSdh DFM(nag, "Connection Rate Not Supported"); 3894*4c06356bSdh /* FALLTHROUGH */ 3895*4c06356bSdh case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT: 3896*4c06356bSdh DFM(nag, "Open Retry Timeout"); 3897*4c06356bSdh /* FALLTHROUGH */ 3898*4c06356bSdh case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR: 3899*4c06356bSdh DFM(nag, "Response Connection Error"); 3900*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 3901*4c06356bSdh "%s: expander %s SMP operation failed (%s)", 3902*4c06356bSdh __func__, pptr->path, nag); 3903*4c06356bSdh break; 3904*4c06356bSdh 3905*4c06356bSdh /* 3906*4c06356bSdh * For the IO_DS_NON_OPERATIONAL case, we need to kick off 3907*4c06356bSdh * device state recovery and return 0 so that the caller 3908*4c06356bSdh * doesn't assume this expander is dead for good. 3909*4c06356bSdh */ 3910*4c06356bSdh case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL: { 3911*4c06356bSdh pmcs_xscsi_t *xp = pptr->target; 3912*4c06356bSdh 3913*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, 3914*4c06356bSdh "%s: expander %s device state non-operational", 3915*4c06356bSdh __func__, pptr->path); 3916*4c06356bSdh 3917*4c06356bSdh if (xp == NULL) { 3918*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, 3919*4c06356bSdh "%s: No target to do DS recovery for PHY " 3920*4c06356bSdh "%p (%s), attempting PHY hard reset", 3921*4c06356bSdh __func__, (void *)pptr, pptr->path); 3922*4c06356bSdh (void) pmcs_reset_phy(pwp, pptr, 3923*4c06356bSdh PMCS_PHYOP_HARD_RESET); 3924*4c06356bSdh break; 3925*4c06356bSdh } 3926*4c06356bSdh 3927*4c06356bSdh mutex_enter(&xp->statlock); 3928*4c06356bSdh pmcs_start_dev_state_recovery(xp, pptr); 3929*4c06356bSdh mutex_exit(&xp->statlock); 3930*4c06356bSdh break; 3931*4c06356bSdh } 3932*4c06356bSdh 3933*4c06356bSdh default: 3934*4c06356bSdh pmcs_print_entry(pwp, PMCS_PRT_DEBUG, buf, ptr); 3935*4c06356bSdh result = -EIO; 3936*4c06356bSdh break; 3937*4c06356bSdh } 3938*4c06356bSdh } else if (srf->srf_frame_type != SMP_FRAME_TYPE_RESPONSE) { 3939*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 3940*4c06356bSdh "%s: bad response frame type 0x%x", 3941*4c06356bSdh __func__, srf->srf_frame_type); 3942*4c06356bSdh result = -EINVAL; 3943*4c06356bSdh } else if (srf->srf_function != SMP_FUNC_REPORT_GENERAL) { 3944*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: bad response function 0x%x", 3945*4c06356bSdh __func__, srf->srf_function); 3946*4c06356bSdh result = -EINVAL; 3947*4c06356bSdh } else if (srf->srf_result != 0) { 3948*4c06356bSdh /* 3949*4c06356bSdh * Check to see if we have a value of 3 for failure and 3950*4c06356bSdh * whether we were using a SAS2.0 allocation length value 3951*4c06356bSdh * and retry without it. 3952*4c06356bSdh */ 3953*4c06356bSdh if (srf->srf_result == 3 && (ival & 0xff00)) { 3954*4c06356bSdh ival &= ~0xff00; 3955*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 3956*4c06356bSdh "%s: err 0x%x with SAS2 request- retry with SAS1", 3957*4c06356bSdh __func__, srf->srf_result); 3958*4c06356bSdh goto again; 3959*4c06356bSdh } 3960*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: bad response 0x%x", 3961*4c06356bSdh __func__, srf->srf_result); 3962*4c06356bSdh result = -EINVAL; 3963*4c06356bSdh } else if (srgr->srgr_configuring) { 3964*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 3965*4c06356bSdh "%s: expander at phy %s is still configuring", 3966*4c06356bSdh __func__, pptr->path); 3967*4c06356bSdh result = 0; 3968*4c06356bSdh } else { 3969*4c06356bSdh result = srgr->srgr_number_of_phys; 3970*4c06356bSdh if (ival & 0xff00) { 3971*4c06356bSdh pptr->tolerates_sas2 = 1; 3972*4c06356bSdh } 3973*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 3974*4c06356bSdh "%s has %d phys and %s SAS2", pptr->path, result, 3975*4c06356bSdh pptr->tolerates_sas2? "tolerates" : "does not tolerate"); 3976*4c06356bSdh } 3977*4c06356bSdh out: 3978*4c06356bSdh return (result); 3979*4c06356bSdh } 3980*4c06356bSdh 3981*4c06356bSdh /* 3982*4c06356bSdh * Called with expander locked (and thus, pptr) as well as all PHYs up to 3983*4c06356bSdh * the root, and scratch acquired. Return 0 if we fail to allocate resources 3984*4c06356bSdh * or notice that the configuration changed while we were running the command. 3985*4c06356bSdh * 3986*4c06356bSdh * We return less than zero if we had an I/O error or received an 3987*4c06356bSdh * unsupported configuration. 3988*4c06356bSdh */ 3989*4c06356bSdh static int 3990*4c06356bSdh pmcs_expander_content_discover(pmcs_hw_t *pwp, pmcs_phy_t *expander, 3991*4c06356bSdh pmcs_phy_t *pptr) 3992*4c06356bSdh { 3993*4c06356bSdh struct pmcwork *pwrk; 3994*4c06356bSdh char buf[64]; 3995*4c06356bSdh uint8_t sas_address[8]; 3996*4c06356bSdh uint8_t att_sas_address[8]; 3997*4c06356bSdh smp_response_frame_t *srf; 3998*4c06356bSdh smp_discover_resp_t *sdr; 3999*4c06356bSdh const uint_t rdoff = 0x100; /* returned data offset */ 4000*4c06356bSdh uint8_t *roff; 4001*4c06356bSdh uint32_t status, *ptr, msg[PMCS_MSG_SIZE], htag; 4002*4c06356bSdh int result; 4003*4c06356bSdh uint8_t ini_support; 4004*4c06356bSdh uint8_t tgt_support; 4005*4c06356bSdh 4006*4c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, expander); 4007*4c06356bSdh if (pwrk == NULL) { 4008*4c06356bSdh result = 0; 4009*4c06356bSdh goto out; 4010*4c06356bSdh } 4011*4c06356bSdh (void) memset(pwp->scratch, 0x77, PMCS_SCRATCH_SIZE); 4012*4c06356bSdh pwrk->arg = pwp->scratch; 4013*4c06356bSdh pwrk->dtype = expander->dtype; 4014*4c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST)); 4015*4c06356bSdh msg[1] = LE_32(pwrk->htag); 4016*4c06356bSdh msg[2] = LE_32(expander->device_id); 4017*4c06356bSdh msg[3] = LE_32((12 << SMP_REQUEST_LENGTH_SHIFT) | 4018*4c06356bSdh SMP_INDIRECT_RESPONSE); 4019*4c06356bSdh /* 4020*4c06356bSdh * Send SMP DISCOVER (of either SAS1.1 or SAS2 flavors). 4021*4c06356bSdh */ 4022*4c06356bSdh if (expander->tolerates_sas2) { 4023*4c06356bSdh msg[4] = BE_32(0x40101B00); 4024*4c06356bSdh } else { 4025*4c06356bSdh msg[4] = BE_32(0x40100000); 4026*4c06356bSdh } 4027*4c06356bSdh msg[5] = 0; 4028*4c06356bSdh msg[6] = BE_32((pptr->phynum << 16)); 4029*4c06356bSdh msg[7] = 0; 4030*4c06356bSdh msg[8] = 0; 4031*4c06356bSdh msg[9] = 0; 4032*4c06356bSdh msg[10] = 0; 4033*4c06356bSdh msg[11] = 0; 4034*4c06356bSdh msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff)); 4035*4c06356bSdh msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff)); 4036*4c06356bSdh msg[14] = LE_32(PMCS_SCRATCH_SIZE - rdoff); 4037*4c06356bSdh msg[15] = 0; 4038*4c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 4039*4c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 4040*4c06356bSdh if (ptr == NULL) { 4041*4c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 4042*4c06356bSdh result = 0; 4043*4c06356bSdh goto out; 4044*4c06356bSdh } 4045*4c06356bSdh 4046*4c06356bSdh COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE); 4047*4c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 4048*4c06356bSdh htag = pwrk->htag; 4049*4c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 4050*4c06356bSdh 4051*4c06356bSdh /* 4052*4c06356bSdh * Drop PHY lock while waiting so other completions aren't potentially 4053*4c06356bSdh * blocked. 4054*4c06356bSdh */ 4055*4c06356bSdh pmcs_unlock_phy(expander); 4056*4c06356bSdh WAIT_FOR(pwrk, 1000, result); 4057*4c06356bSdh pmcs_lock_phy(expander); 4058*4c06356bSdh pmcs_pwork(pwp, pwrk); 4059*4c06356bSdh 4060*4c06356bSdh mutex_enter(&pwp->config_lock); 4061*4c06356bSdh if (pwp->config_changed) { 4062*4c06356bSdh RESTART_DISCOVERY_LOCKED(pwp); 4063*4c06356bSdh mutex_exit(&pwp->config_lock); 4064*4c06356bSdh result = 0; 4065*4c06356bSdh goto out; 4066*4c06356bSdh } 4067*4c06356bSdh mutex_exit(&pwp->config_lock); 4068*4c06356bSdh 4069*4c06356bSdh if (result) { 4070*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_WARN, pmcs_timeo, __func__); 4071*4c06356bSdh if (pmcs_abort(pwp, expander, htag, 0, 0)) { 4072*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 4073*4c06356bSdh "%s: Unable to issue SMP ABORT for htag 0x%08x", 4074*4c06356bSdh __func__, htag); 4075*4c06356bSdh } else { 4076*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 4077*4c06356bSdh "%s: Issuing SMP ABORT for htag 0x%08x", 4078*4c06356bSdh __func__, htag); 4079*4c06356bSdh } 4080*4c06356bSdh result = -ETIMEDOUT; 4081*4c06356bSdh goto out; 4082*4c06356bSdh } 4083*4c06356bSdh ptr = (void *)pwp->scratch; 4084*4c06356bSdh /* 4085*4c06356bSdh * Point roff to the DMA offset for returned data 4086*4c06356bSdh */ 4087*4c06356bSdh roff = pwp->scratch; 4088*4c06356bSdh roff += rdoff; 4089*4c06356bSdh srf = (smp_response_frame_t *)roff; 4090*4c06356bSdh sdr = (smp_discover_resp_t *)(roff+4); 4091*4c06356bSdh status = LE_32(ptr[2]); 4092*4c06356bSdh if (status == PMCOUT_STATUS_UNDERFLOW || 4093*4c06356bSdh status == PMCOUT_STATUS_OVERFLOW) { 4094*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, 4095*4c06356bSdh "%s: over/underflow", __func__); 4096*4c06356bSdh status = PMCOUT_STATUS_OK; 4097*4c06356bSdh } 4098*4c06356bSdh if (status != PMCOUT_STATUS_OK) { 4099*4c06356bSdh char *nag = NULL; 4100*4c06356bSdh (void) snprintf(buf, sizeof (buf), 4101*4c06356bSdh "%s: SMP op failed (0x%x)", __func__, status); 4102*4c06356bSdh switch (status) { 4103*4c06356bSdh case PMCOUT_STATUS_ERROR_HW_TIMEOUT: 4104*4c06356bSdh DFM(nag, "Hardware Timeout"); 4105*4c06356bSdh /* FALLTHROUGH */ 4106*4c06356bSdh case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE: 4107*4c06356bSdh DFM(nag, "Internal SMP Resource Failure"); 4108*4c06356bSdh /* FALLTHROUGH */ 4109*4c06356bSdh case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY: 4110*4c06356bSdh DFM(nag, "PHY Not Ready"); 4111*4c06356bSdh /* FALLTHROUGH */ 4112*4c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: 4113*4c06356bSdh DFM(nag, "Connection Rate Not Supported"); 4114*4c06356bSdh /* FALLTHROUGH */ 4115*4c06356bSdh case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT: 4116*4c06356bSdh DFM(nag, "Open Retry Timeout"); 4117*4c06356bSdh /* FALLTHROUGH */ 4118*4c06356bSdh case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR: 4119*4c06356bSdh DFM(nag, "Response Connection Error"); 4120*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 4121*4c06356bSdh "%s: expander %s SMP operation failed (%s)", 4122*4c06356bSdh __func__, pptr->path, nag); 4123*4c06356bSdh break; 4124*4c06356bSdh default: 4125*4c06356bSdh pmcs_print_entry(pwp, PMCS_PRT_DEBUG, buf, ptr); 4126*4c06356bSdh result = -EIO; 4127*4c06356bSdh break; 4128*4c06356bSdh } 4129*4c06356bSdh goto out; 4130*4c06356bSdh } else if (srf->srf_frame_type != SMP_FRAME_TYPE_RESPONSE) { 4131*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 4132*4c06356bSdh "%s: bad response frame type 0x%x", 4133*4c06356bSdh __func__, srf->srf_frame_type); 4134*4c06356bSdh result = -EINVAL; 4135*4c06356bSdh goto out; 4136*4c06356bSdh } else if (srf->srf_function != SMP_FUNC_DISCOVER) { 4137*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: bad response function 0x%x", 4138*4c06356bSdh __func__, srf->srf_function); 4139*4c06356bSdh result = -EINVAL; 4140*4c06356bSdh goto out; 4141*4c06356bSdh } else if (srf->srf_result != SMP_RES_FUNCTION_ACCEPTED) { 4142*4c06356bSdh result = pmcs_smp_function_result(pwp, srf); 4143*4c06356bSdh /* Need not fail if PHY is Vacant */ 4144*4c06356bSdh if (result != SMP_RES_PHY_VACANT) { 4145*4c06356bSdh result = -EINVAL; 4146*4c06356bSdh goto out; 4147*4c06356bSdh } 4148*4c06356bSdh } 4149*4c06356bSdh 4150*4c06356bSdh ini_support = (sdr->sdr_attached_sata_host | 4151*4c06356bSdh (sdr->sdr_attached_smp_initiator << 1) | 4152*4c06356bSdh (sdr->sdr_attached_stp_initiator << 2) | 4153*4c06356bSdh (sdr->sdr_attached_ssp_initiator << 3)); 4154*4c06356bSdh 4155*4c06356bSdh tgt_support = (sdr->sdr_attached_sata_device | 4156*4c06356bSdh (sdr->sdr_attached_smp_target << 1) | 4157*4c06356bSdh (sdr->sdr_attached_stp_target << 2) | 4158*4c06356bSdh (sdr->sdr_attached_ssp_target << 3)); 4159*4c06356bSdh 4160*4c06356bSdh pmcs_wwn2barray(BE_64(sdr->sdr_sas_addr), sas_address); 4161*4c06356bSdh pmcs_wwn2barray(BE_64(sdr->sdr_attached_sas_addr), att_sas_address); 4162*4c06356bSdh 4163*4c06356bSdh switch (sdr->sdr_attached_device_type) { 4164*4c06356bSdh case SAS_IF_DTYPE_ENDPOINT: 4165*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 4166*4c06356bSdh "exp_content: %s atdt=0x%x lr=%x is=%x ts=%x SAS=" 4167*4c06356bSdh SAS_ADDR_FMT " attSAS=" SAS_ADDR_FMT " atPHY=%x", 4168*4c06356bSdh pptr->path, 4169*4c06356bSdh sdr->sdr_attached_device_type, 4170*4c06356bSdh sdr->sdr_negotiated_logical_link_rate, 4171*4c06356bSdh ini_support, 4172*4c06356bSdh tgt_support, 4173*4c06356bSdh SAS_ADDR_PRT(sas_address), 4174*4c06356bSdh SAS_ADDR_PRT(att_sas_address), 4175*4c06356bSdh sdr->sdr_attached_phy_identifier); 4176*4c06356bSdh 4177*4c06356bSdh if (sdr->sdr_attached_sata_device || 4178*4c06356bSdh sdr->sdr_attached_stp_target) { 4179*4c06356bSdh pptr->dtype = SATA; 4180*4c06356bSdh } else if (sdr->sdr_attached_ssp_target) { 4181*4c06356bSdh pptr->dtype = SAS; 4182*4c06356bSdh } else if (tgt_support || ini_support) { 4183*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: %s has " 4184*4c06356bSdh "tgt support=%x init support=(%x)", 4185*4c06356bSdh __func__, pptr->path, tgt_support, ini_support); 4186*4c06356bSdh } 4187*4c06356bSdh break; 4188*4c06356bSdh case SAS_IF_DTYPE_EDGE: 4189*4c06356bSdh case SAS_IF_DTYPE_FANOUT: 4190*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 4191*4c06356bSdh "exp_content: %s atdt=0x%x lr=%x is=%x ts=%x SAS=" 4192*4c06356bSdh SAS_ADDR_FMT " attSAS=" SAS_ADDR_FMT " atPHY=%x", 4193*4c06356bSdh pptr->path, 4194*4c06356bSdh sdr->sdr_attached_device_type, 4195*4c06356bSdh sdr->sdr_negotiated_logical_link_rate, 4196*4c06356bSdh ini_support, 4197*4c06356bSdh tgt_support, 4198*4c06356bSdh SAS_ADDR_PRT(sas_address), 4199*4c06356bSdh SAS_ADDR_PRT(att_sas_address), 4200*4c06356bSdh sdr->sdr_attached_phy_identifier); 4201*4c06356bSdh if (sdr->sdr_attached_smp_target) { 4202*4c06356bSdh /* 4203*4c06356bSdh * Avoid configuring phys that just point back 4204*4c06356bSdh * at a parent phy 4205*4c06356bSdh */ 4206*4c06356bSdh if (expander->parent && 4207*4c06356bSdh memcmp(expander->parent->sas_address, 4208*4c06356bSdh att_sas_address, 4209*4c06356bSdh sizeof (expander->parent->sas_address)) == 0) { 4210*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG3, 4211*4c06356bSdh "%s: skipping port back to parent " 4212*4c06356bSdh "expander (%s)", __func__, pptr->path); 4213*4c06356bSdh pptr->dtype = NOTHING; 4214*4c06356bSdh break; 4215*4c06356bSdh } 4216*4c06356bSdh pptr->dtype = EXPANDER; 4217*4c06356bSdh 4218*4c06356bSdh } else if (tgt_support || ini_support) { 4219*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s has " 4220*4c06356bSdh "tgt support=%x init support=(%x)", 4221*4c06356bSdh pptr->path, tgt_support, ini_support); 4222*4c06356bSdh pptr->dtype = EXPANDER; 4223*4c06356bSdh } 4224*4c06356bSdh break; 4225*4c06356bSdh default: 4226*4c06356bSdh pptr->dtype = NOTHING; 4227*4c06356bSdh break; 4228*4c06356bSdh } 4229*4c06356bSdh if (pptr->dtype != NOTHING) { 4230*4c06356bSdh pmcs_phy_t *ctmp; 4231*4c06356bSdh 4232*4c06356bSdh /* 4233*4c06356bSdh * If the attached device is a SATA device and the expander 4234*4c06356bSdh * is (possibly) a SAS2 compliant expander, check for whether 4235*4c06356bSdh * there is a NAA=5 WWN field starting at this offset and 4236*4c06356bSdh * use that for the SAS Address for this device. 4237*4c06356bSdh */ 4238*4c06356bSdh if (expander->tolerates_sas2 && pptr->dtype == SATA && 4239*4c06356bSdh (roff[SAS_ATTACHED_NAME_OFFSET] >> 8) == 0x5) { 4240*4c06356bSdh (void) memcpy(pptr->sas_address, 4241*4c06356bSdh &roff[SAS_ATTACHED_NAME_OFFSET], 8); 4242*4c06356bSdh } else { 4243*4c06356bSdh (void) memcpy(pptr->sas_address, att_sas_address, 8); 4244*4c06356bSdh } 4245*4c06356bSdh pptr->atdt = (sdr->sdr_attached_device_type); 4246*4c06356bSdh /* 4247*4c06356bSdh * Now run up from the expander's parent up to the top to 4248*4c06356bSdh * make sure we only use the least common link_rate. 4249*4c06356bSdh */ 4250*4c06356bSdh for (ctmp = expander->parent; ctmp; ctmp = ctmp->parent) { 4251*4c06356bSdh if (ctmp->link_rate < 4252*4c06356bSdh sdr->sdr_negotiated_logical_link_rate) { 4253*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 4254*4c06356bSdh "%s: derating link rate from %x to %x due " 4255*4c06356bSdh "to %s being slower", pptr->path, 4256*4c06356bSdh sdr->sdr_negotiated_logical_link_rate, 4257*4c06356bSdh ctmp->link_rate, 4258*4c06356bSdh ctmp->path); 4259*4c06356bSdh sdr->sdr_negotiated_logical_link_rate = 4260*4c06356bSdh ctmp->link_rate; 4261*4c06356bSdh } 4262*4c06356bSdh } 4263*4c06356bSdh pptr->link_rate = sdr->sdr_negotiated_logical_link_rate; 4264*4c06356bSdh pptr->state.prog_min_rate = sdr->sdr_prog_min_phys_link_rate; 4265*4c06356bSdh pptr->state.hw_min_rate = sdr->sdr_hw_min_phys_link_rate; 4266*4c06356bSdh pptr->state.prog_max_rate = sdr->sdr_prog_max_phys_link_rate; 4267*4c06356bSdh pptr->state.hw_max_rate = sdr->sdr_hw_max_phys_link_rate; 4268*4c06356bSdh PHY_CHANGED(pwp, pptr); 4269*4c06356bSdh } else { 4270*4c06356bSdh pmcs_clear_phy(pwp, pptr); 4271*4c06356bSdh } 4272*4c06356bSdh result = 1; 4273*4c06356bSdh out: 4274*4c06356bSdh return (result); 4275*4c06356bSdh } 4276*4c06356bSdh 4277*4c06356bSdh /* 4278*4c06356bSdh * Get a work structure and assign it a tag with type and serial number 4279*4c06356bSdh * If a structure is returned, it is returned locked. 4280*4c06356bSdh */ 4281*4c06356bSdh pmcwork_t * 4282*4c06356bSdh pmcs_gwork(pmcs_hw_t *pwp, uint32_t tag_type, pmcs_phy_t *phyp) 4283*4c06356bSdh { 4284*4c06356bSdh pmcwork_t *p; 4285*4c06356bSdh uint16_t snum; 4286*4c06356bSdh uint32_t off; 4287*4c06356bSdh 4288*4c06356bSdh mutex_enter(&pwp->wfree_lock); 4289*4c06356bSdh p = STAILQ_FIRST(&pwp->wf); 4290*4c06356bSdh if (p == NULL) { 4291*4c06356bSdh /* 4292*4c06356bSdh * If we couldn't get a work structure, it's time to bite 4293*4c06356bSdh * the bullet, grab the pfree_lock and copy over all the 4294*4c06356bSdh * work structures from the pending free list to the actual 4295*4c06356bSdh * free list. This shouldn't happen all that often. 4296*4c06356bSdh */ 4297*4c06356bSdh mutex_enter(&pwp->pfree_lock); 4298*4c06356bSdh pwp->wf.stqh_first = pwp->pf.stqh_first; 4299*4c06356bSdh pwp->wf.stqh_last = pwp->pf.stqh_last; 4300*4c06356bSdh STAILQ_INIT(&pwp->pf); 4301*4c06356bSdh mutex_exit(&pwp->pfree_lock); 4302*4c06356bSdh 4303*4c06356bSdh p = STAILQ_FIRST(&pwp->wf); 4304*4c06356bSdh if (p == NULL) { 4305*4c06356bSdh mutex_exit(&pwp->wfree_lock); 4306*4c06356bSdh return (NULL); 4307*4c06356bSdh } 4308*4c06356bSdh } 4309*4c06356bSdh STAILQ_REMOVE(&pwp->wf, p, pmcwork, next); 4310*4c06356bSdh snum = pwp->wserno++; 4311*4c06356bSdh mutex_exit(&pwp->wfree_lock); 4312*4c06356bSdh 4313*4c06356bSdh off = p - pwp->work; 4314*4c06356bSdh 4315*4c06356bSdh mutex_enter(&p->lock); 4316*4c06356bSdh ASSERT(p->state == PMCS_WORK_STATE_NIL); 4317*4c06356bSdh ASSERT(p->htag == PMCS_TAG_FREE); 4318*4c06356bSdh p->htag = (tag_type << PMCS_TAG_TYPE_SHIFT) & PMCS_TAG_TYPE_MASK; 4319*4c06356bSdh p->htag |= ((snum << PMCS_TAG_SERNO_SHIFT) & PMCS_TAG_SERNO_MASK); 4320*4c06356bSdh p->htag |= ((off << PMCS_TAG_INDEX_SHIFT) & PMCS_TAG_INDEX_MASK); 4321*4c06356bSdh p->start = gethrtime(); 4322*4c06356bSdh p->state = PMCS_WORK_STATE_READY; 4323*4c06356bSdh p->ssp_event = 0; 4324*4c06356bSdh p->dead = 0; 4325*4c06356bSdh 4326*4c06356bSdh if (phyp) { 4327*4c06356bSdh p->phy = phyp; 4328*4c06356bSdh pmcs_inc_phy_ref_count(phyp); 4329*4c06356bSdh } 4330*4c06356bSdh 4331*4c06356bSdh return (p); 4332*4c06356bSdh } 4333*4c06356bSdh 4334*4c06356bSdh /* 4335*4c06356bSdh * Called with pwrk lock held. Returned with lock released. 4336*4c06356bSdh */ 4337*4c06356bSdh void 4338*4c06356bSdh pmcs_pwork(pmcs_hw_t *pwp, pmcwork_t *p) 4339*4c06356bSdh { 4340*4c06356bSdh ASSERT(p != NULL); 4341*4c06356bSdh ASSERT(mutex_owned(&p->lock)); 4342*4c06356bSdh 4343*4c06356bSdh #ifdef DEBUG 4344*4c06356bSdh p->last_ptr = p->ptr; 4345*4c06356bSdh p->last_arg = p->arg; 4346*4c06356bSdh p->last_phy = p->phy; 4347*4c06356bSdh p->last_xp = p->xp; 4348*4c06356bSdh p->last_htag = p->htag; 4349*4c06356bSdh p->last_state = p->state; 4350*4c06356bSdh #endif 4351*4c06356bSdh p->finish = gethrtime(); 4352*4c06356bSdh 4353*4c06356bSdh if (p->phy) { 4354*4c06356bSdh pmcs_dec_phy_ref_count(p->phy); 4355*4c06356bSdh } 4356*4c06356bSdh 4357*4c06356bSdh p->state = PMCS_WORK_STATE_NIL; 4358*4c06356bSdh p->htag = PMCS_TAG_FREE; 4359*4c06356bSdh p->xp = NULL; 4360*4c06356bSdh p->ptr = NULL; 4361*4c06356bSdh p->arg = NULL; 4362*4c06356bSdh p->phy = NULL; 4363*4c06356bSdh p->timer = 0; 4364*4c06356bSdh mutex_exit(&p->lock); 4365*4c06356bSdh 4366*4c06356bSdh if (mutex_tryenter(&pwp->wfree_lock) == 0) { 4367*4c06356bSdh mutex_enter(&pwp->pfree_lock); 4368*4c06356bSdh STAILQ_INSERT_TAIL(&pwp->pf, p, next); 4369*4c06356bSdh mutex_exit(&pwp->pfree_lock); 4370*4c06356bSdh } else { 4371*4c06356bSdh STAILQ_INSERT_TAIL(&pwp->wf, p, next); 4372*4c06356bSdh mutex_exit(&pwp->wfree_lock); 4373*4c06356bSdh } 4374*4c06356bSdh } 4375*4c06356bSdh 4376*4c06356bSdh /* 4377*4c06356bSdh * Find a work structure based upon a tag and make sure that the tag 4378*4c06356bSdh * serial number matches the work structure we've found. 4379*4c06356bSdh * If a structure is found, its lock is held upon return. 4380*4c06356bSdh */ 4381*4c06356bSdh pmcwork_t * 4382*4c06356bSdh pmcs_tag2wp(pmcs_hw_t *pwp, uint32_t htag) 4383*4c06356bSdh { 4384*4c06356bSdh pmcwork_t *p; 4385*4c06356bSdh uint32_t idx = PMCS_TAG_INDEX(htag); 4386*4c06356bSdh 4387*4c06356bSdh p = &pwp->work[idx]; 4388*4c06356bSdh 4389*4c06356bSdh mutex_enter(&p->lock); 4390*4c06356bSdh if (p->htag == htag) { 4391*4c06356bSdh return (p); 4392*4c06356bSdh } 4393*4c06356bSdh mutex_exit(&p->lock); 4394*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, "INDEX 0x%x HTAG 0x%x got p->htag 0x%x", 4395*4c06356bSdh idx, htag, p->htag); 4396*4c06356bSdh return (NULL); 4397*4c06356bSdh } 4398*4c06356bSdh 4399*4c06356bSdh /* 4400*4c06356bSdh * Issue an abort for a command or for all commands. 4401*4c06356bSdh * 4402*4c06356bSdh * Since this can be called from interrupt context, 4403*4c06356bSdh * we don't wait for completion if wait is not set. 4404*4c06356bSdh * 4405*4c06356bSdh * Called with PHY lock held. 4406*4c06356bSdh */ 4407*4c06356bSdh int 4408*4c06356bSdh pmcs_abort(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint32_t tag, int all_cmds, 4409*4c06356bSdh int wait) 4410*4c06356bSdh { 4411*4c06356bSdh pmcwork_t *pwrk; 4412*4c06356bSdh pmcs_xscsi_t *tgt; 4413*4c06356bSdh uint32_t msg[PMCS_MSG_SIZE], *ptr; 4414*4c06356bSdh int result, abt_type; 4415*4c06356bSdh uint32_t abt_htag, status; 4416*4c06356bSdh 4417*4c06356bSdh if (pptr->abort_all_start) { 4418*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: ABORT_ALL for (%s) already" 4419*4c06356bSdh " in progress.", __func__, pptr->path); 4420*4c06356bSdh return (EBUSY); 4421*4c06356bSdh } 4422*4c06356bSdh 4423*4c06356bSdh switch (pptr->dtype) { 4424*4c06356bSdh case SAS: 4425*4c06356bSdh abt_type = PMCIN_SSP_ABORT; 4426*4c06356bSdh break; 4427*4c06356bSdh case SATA: 4428*4c06356bSdh abt_type = PMCIN_SATA_ABORT; 4429*4c06356bSdh break; 4430*4c06356bSdh case EXPANDER: 4431*4c06356bSdh abt_type = PMCIN_SMP_ABORT; 4432*4c06356bSdh break; 4433*4c06356bSdh default: 4434*4c06356bSdh return (0); 4435*4c06356bSdh } 4436*4c06356bSdh 4437*4c06356bSdh pwrk = pmcs_gwork(pwp, wait ? PMCS_TAG_TYPE_WAIT : PMCS_TAG_TYPE_NONE, 4438*4c06356bSdh pptr); 4439*4c06356bSdh 4440*4c06356bSdh if (pwrk == NULL) { 4441*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__); 4442*4c06356bSdh return (ENOMEM); 4443*4c06356bSdh } 4444*4c06356bSdh 4445*4c06356bSdh pwrk->dtype = pptr->dtype; 4446*4c06356bSdh if (wait) { 4447*4c06356bSdh pwrk->arg = msg; 4448*4c06356bSdh } 4449*4c06356bSdh if (pptr->valid_device_id == 0) { 4450*4c06356bSdh pmcs_pwork(pwp, pwrk); 4451*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Invalid DeviceID", __func__); 4452*4c06356bSdh return (ENODEV); 4453*4c06356bSdh } 4454*4c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, abt_type)); 4455*4c06356bSdh msg[1] = LE_32(pwrk->htag); 4456*4c06356bSdh msg[2] = LE_32(pptr->device_id); 4457*4c06356bSdh if (all_cmds) { 4458*4c06356bSdh msg[3] = 0; 4459*4c06356bSdh msg[4] = LE_32(1); 4460*4c06356bSdh pwrk->ptr = NULL; 4461*4c06356bSdh pptr->abort_all_start = gethrtime(); 4462*4c06356bSdh } else { 4463*4c06356bSdh msg[3] = LE_32(tag); 4464*4c06356bSdh msg[4] = 0; 4465*4c06356bSdh pwrk->ptr = &tag; 4466*4c06356bSdh } 4467*4c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 4468*4c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 4469*4c06356bSdh if (ptr == NULL) { 4470*4c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 4471*4c06356bSdh pmcs_pwork(pwp, pwrk); 4472*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__); 4473*4c06356bSdh return (ENOMEM); 4474*4c06356bSdh } 4475*4c06356bSdh 4476*4c06356bSdh COPY_MESSAGE(ptr, msg, 5); 4477*4c06356bSdh if (all_cmds) { 4478*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 4479*4c06356bSdh "%s: aborting all commands for %s device %s. (htag=0x%x)", 4480*4c06356bSdh __func__, pmcs_get_typename(pptr->dtype), pptr->path, 4481*4c06356bSdh msg[1]); 4482*4c06356bSdh } else { 4483*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 4484*4c06356bSdh "%s: aborting tag 0x%x for %s device %s. (htag=0x%x)", 4485*4c06356bSdh __func__, tag, pmcs_get_typename(pptr->dtype), pptr->path, 4486*4c06356bSdh msg[1]); 4487*4c06356bSdh } 4488*4c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 4489*4c06356bSdh 4490*4c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 4491*4c06356bSdh if (!wait) { 4492*4c06356bSdh mutex_exit(&pwrk->lock); 4493*4c06356bSdh return (0); 4494*4c06356bSdh } 4495*4c06356bSdh 4496*4c06356bSdh abt_htag = pwrk->htag; 4497*4c06356bSdh pmcs_unlock_phy(pwrk->phy); 4498*4c06356bSdh WAIT_FOR(pwrk, 1000, result); 4499*4c06356bSdh pmcs_lock_phy(pwrk->phy); 4500*4c06356bSdh 4501*4c06356bSdh tgt = pwrk->xp; 4502*4c06356bSdh pmcs_pwork(pwp, pwrk); 4503*4c06356bSdh 4504*4c06356bSdh if (tgt != NULL) { 4505*4c06356bSdh mutex_enter(&tgt->aqlock); 4506*4c06356bSdh if (!STAILQ_EMPTY(&tgt->aq)) { 4507*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 4508*4c06356bSdh "%s: Abort complete (result=0x%x), but " 4509*4c06356bSdh "aq not empty (tgt 0x%p), waiting", 4510*4c06356bSdh __func__, result, (void *)tgt); 4511*4c06356bSdh cv_wait(&tgt->abort_cv, &tgt->aqlock); 4512*4c06356bSdh } 4513*4c06356bSdh mutex_exit(&tgt->aqlock); 4514*4c06356bSdh } 4515*4c06356bSdh 4516*4c06356bSdh if (all_cmds) { 4517*4c06356bSdh pptr->abort_all_start = 0; 4518*4c06356bSdh cv_signal(&pptr->abort_all_cv); 4519*4c06356bSdh } 4520*4c06356bSdh 4521*4c06356bSdh if (result) { 4522*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 4523*4c06356bSdh "%s: Abort (htag 0x%08x) request timed out", 4524*4c06356bSdh __func__, abt_htag); 4525*4c06356bSdh if (tgt != NULL) { 4526*4c06356bSdh mutex_enter(&tgt->statlock); 4527*4c06356bSdh if ((tgt->dev_state != PMCS_DEVICE_STATE_IN_RECOVERY) && 4528*4c06356bSdh (tgt->dev_state != 4529*4c06356bSdh PMCS_DEVICE_STATE_NON_OPERATIONAL)) { 4530*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 4531*4c06356bSdh "%s: Trying DS error recovery for tgt 0x%p", 4532*4c06356bSdh __func__, (void *)tgt); 4533*4c06356bSdh (void) pmcs_send_err_recovery_cmd(pwp, 4534*4c06356bSdh PMCS_DEVICE_STATE_IN_RECOVERY, tgt); 4535*4c06356bSdh } 4536*4c06356bSdh mutex_exit(&tgt->statlock); 4537*4c06356bSdh } 4538*4c06356bSdh return (ETIMEDOUT); 4539*4c06356bSdh } 4540*4c06356bSdh 4541*4c06356bSdh status = LE_32(msg[2]); 4542*4c06356bSdh if (status != PMCOUT_STATUS_OK) { 4543*4c06356bSdh /* 4544*4c06356bSdh * The only non-success status are IO_NOT_VALID & 4545*4c06356bSdh * IO_ABORT_IN_PROGRESS. 4546*4c06356bSdh * In case of IO_ABORT_IN_PROGRESS, the other ABORT cmd's 4547*4c06356bSdh * status is of concern and this duplicate cmd status can 4548*4c06356bSdh * be ignored. 4549*4c06356bSdh * If IO_NOT_VALID, that's not an error per-se. 4550*4c06356bSdh * For abort of single I/O complete the command anyway. 4551*4c06356bSdh * If, however, we were aborting all, that is a problem 4552*4c06356bSdh * as IO_NOT_VALID really means that the IO or device is 4553*4c06356bSdh * not there. So, discovery process will take of the cleanup. 4554*4c06356bSdh */ 4555*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: abort result 0x%x", 4556*4c06356bSdh __func__, LE_32(msg[2])); 4557*4c06356bSdh if (all_cmds) { 4558*4c06356bSdh PHY_CHANGED(pwp, pptr); 4559*4c06356bSdh RESTART_DISCOVERY(pwp); 4560*4c06356bSdh } else { 4561*4c06356bSdh return (EINVAL); 4562*4c06356bSdh } 4563*4c06356bSdh 4564*4c06356bSdh return (0); 4565*4c06356bSdh } 4566*4c06356bSdh 4567*4c06356bSdh if (tgt != NULL) { 4568*4c06356bSdh mutex_enter(&tgt->statlock); 4569*4c06356bSdh if (tgt->dev_state == PMCS_DEVICE_STATE_IN_RECOVERY) { 4570*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 4571*4c06356bSdh "%s: Restoring OPERATIONAL dev_state for tgt 0x%p", 4572*4c06356bSdh __func__, (void *)tgt); 4573*4c06356bSdh (void) pmcs_send_err_recovery_cmd(pwp, 4574*4c06356bSdh PMCS_DEVICE_STATE_OPERATIONAL, tgt); 4575*4c06356bSdh } 4576*4c06356bSdh mutex_exit(&tgt->statlock); 4577*4c06356bSdh } 4578*4c06356bSdh 4579*4c06356bSdh return (0); 4580*4c06356bSdh } 4581*4c06356bSdh 4582*4c06356bSdh /* 4583*4c06356bSdh * Issue a task management function to an SSP device. 4584*4c06356bSdh * 4585*4c06356bSdh * Called with PHY lock held. 4586*4c06356bSdh * statlock CANNOT be held upon entry. 4587*4c06356bSdh */ 4588*4c06356bSdh int 4589*4c06356bSdh pmcs_ssp_tmf(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint8_t tmf, uint32_t tag, 4590*4c06356bSdh uint64_t lun, uint32_t *response) 4591*4c06356bSdh { 4592*4c06356bSdh int result, ds; 4593*4c06356bSdh uint8_t local[PMCS_QENTRY_SIZE << 1], *xd; 4594*4c06356bSdh sas_ssp_rsp_iu_t *rptr = (void *)local; 4595*4c06356bSdh static const uint8_t ssp_rsp_evec[] = { 4596*4c06356bSdh 0x58, 0x61, 0x56, 0x72, 0x00 4597*4c06356bSdh }; 4598*4c06356bSdh uint32_t msg[PMCS_MSG_SIZE], *ptr, status; 4599*4c06356bSdh struct pmcwork *pwrk; 4600*4c06356bSdh pmcs_xscsi_t *xp; 4601*4c06356bSdh 4602*4c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 4603*4c06356bSdh if (pwrk == NULL) { 4604*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__); 4605*4c06356bSdh return (ENOMEM); 4606*4c06356bSdh } 4607*4c06356bSdh /* 4608*4c06356bSdh * NB: We use the PMCS_OQ_GENERAL outbound queue 4609*4c06356bSdh * NB: so as to not get entangled in normal I/O 4610*4c06356bSdh * NB: processing. 4611*4c06356bSdh */ 4612*4c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 4613*4c06356bSdh PMCIN_SSP_INI_TM_START)); 4614*4c06356bSdh msg[1] = LE_32(pwrk->htag); 4615*4c06356bSdh msg[2] = LE_32(pptr->device_id); 4616*4c06356bSdh if (tmf == SAS_ABORT_TASK || tmf == SAS_QUERY_TASK) { 4617*4c06356bSdh msg[3] = LE_32(tag); 4618*4c06356bSdh } else { 4619*4c06356bSdh msg[3] = 0; 4620*4c06356bSdh } 4621*4c06356bSdh msg[4] = LE_32(tmf); 4622*4c06356bSdh msg[5] = BE_32((uint32_t)lun); 4623*4c06356bSdh msg[6] = BE_32((uint32_t)(lun >> 32)); 4624*4c06356bSdh msg[7] = LE_32(PMCIN_MESSAGE_REPORT); 4625*4c06356bSdh 4626*4c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 4627*4c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 4628*4c06356bSdh if (ptr == NULL) { 4629*4c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 4630*4c06356bSdh pmcs_pwork(pwp, pwrk); 4631*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__); 4632*4c06356bSdh return (ENOMEM); 4633*4c06356bSdh } 4634*4c06356bSdh COPY_MESSAGE(ptr, msg, 7); 4635*4c06356bSdh pwrk->arg = msg; 4636*4c06356bSdh pwrk->dtype = pptr->dtype; 4637*4c06356bSdh 4638*4c06356bSdh xp = pptr->target; 4639*4c06356bSdh if (xp != NULL) { 4640*4c06356bSdh mutex_enter(&xp->statlock); 4641*4c06356bSdh if (xp->dev_state == PMCS_DEVICE_STATE_NON_OPERATIONAL) { 4642*4c06356bSdh mutex_exit(&xp->statlock); 4643*4c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 4644*4c06356bSdh pmcs_pwork(pwp, pwrk); 4645*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Not sending '%s'" 4646*4c06356bSdh " because DS is '%s'", __func__, pmcs_tmf2str(tmf), 4647*4c06356bSdh pmcs_status_str 4648*4c06356bSdh (PMCOUT_STATUS_IO_DS_NON_OPERATIONAL)); 4649*4c06356bSdh return (EIO); 4650*4c06356bSdh } 4651*4c06356bSdh mutex_exit(&xp->statlock); 4652*4c06356bSdh } 4653*4c06356bSdh 4654*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 4655*4c06356bSdh "%s: sending '%s' to %s (lun %llu) tag 0x%x", __func__, 4656*4c06356bSdh pmcs_tmf2str(tmf), pptr->path, (unsigned long long) lun, tag); 4657*4c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 4658*4c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 4659*4c06356bSdh 4660*4c06356bSdh pmcs_unlock_phy(pptr); 4661*4c06356bSdh /* 4662*4c06356bSdh * This is a command sent to the target device, so it can take 4663*4c06356bSdh * significant amount of time to complete when path & device is busy. 4664*4c06356bSdh * Set a timeout to 20 seconds 4665*4c06356bSdh */ 4666*4c06356bSdh WAIT_FOR(pwrk, 20000, result); 4667*4c06356bSdh pmcs_lock_phy(pptr); 4668*4c06356bSdh pmcs_pwork(pwp, pwrk); 4669*4c06356bSdh 4670*4c06356bSdh if (result) { 4671*4c06356bSdh if (xp == NULL) { 4672*4c06356bSdh return (ETIMEDOUT); 4673*4c06356bSdh } 4674*4c06356bSdh 4675*4c06356bSdh mutex_enter(&xp->statlock); 4676*4c06356bSdh pmcs_start_dev_state_recovery(xp, pptr); 4677*4c06356bSdh mutex_exit(&xp->statlock); 4678*4c06356bSdh return (ETIMEDOUT); 4679*4c06356bSdh } 4680*4c06356bSdh 4681*4c06356bSdh status = LE_32(msg[2]); 4682*4c06356bSdh if (status != PMCOUT_STATUS_OK) { 4683*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 4684*4c06356bSdh "%s: status %s for TMF %s action to %s, lun %llu", 4685*4c06356bSdh __func__, pmcs_status_str(status), pmcs_tmf2str(tmf), 4686*4c06356bSdh pptr->path, (unsigned long long) lun); 4687*4c06356bSdh if ((status == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) || 4688*4c06356bSdh (status == PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK) || 4689*4c06356bSdh (status == PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS)) { 4690*4c06356bSdh ds = PMCS_DEVICE_STATE_NON_OPERATIONAL; 4691*4c06356bSdh } else if (status == PMCOUT_STATUS_IO_DS_IN_RECOVERY) { 4692*4c06356bSdh /* 4693*4c06356bSdh * If the status is IN_RECOVERY, it's an indication 4694*4c06356bSdh * that it's now time for us to request to have the 4695*4c06356bSdh * device state set to OPERATIONAL since we're the ones 4696*4c06356bSdh * that requested recovery to begin with. 4697*4c06356bSdh */ 4698*4c06356bSdh ds = PMCS_DEVICE_STATE_OPERATIONAL; 4699*4c06356bSdh } else { 4700*4c06356bSdh ds = PMCS_DEVICE_STATE_IN_RECOVERY; 4701*4c06356bSdh } 4702*4c06356bSdh if (xp != NULL) { 4703*4c06356bSdh mutex_enter(&xp->statlock); 4704*4c06356bSdh if (xp->dev_state != ds) { 4705*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 4706*4c06356bSdh "%s: Sending err recovery cmd" 4707*4c06356bSdh " for tgt 0x%p (status = %s)", 4708*4c06356bSdh __func__, (void *)xp, 4709*4c06356bSdh pmcs_status_str(status)); 4710*4c06356bSdh (void) pmcs_send_err_recovery_cmd(pwp, ds, xp); 4711*4c06356bSdh } 4712*4c06356bSdh mutex_exit(&xp->statlock); 4713*4c06356bSdh } 4714*4c06356bSdh return (EIO); 4715*4c06356bSdh } else { 4716*4c06356bSdh ds = PMCS_DEVICE_STATE_OPERATIONAL; 4717*4c06356bSdh if (xp != NULL) { 4718*4c06356bSdh mutex_enter(&xp->statlock); 4719*4c06356bSdh if (xp->dev_state != ds) { 4720*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 4721*4c06356bSdh "%s: Sending err recovery cmd" 4722*4c06356bSdh " for tgt 0x%p (status = %s)", 4723*4c06356bSdh __func__, (void *)xp, 4724*4c06356bSdh pmcs_status_str(status)); 4725*4c06356bSdh (void) pmcs_send_err_recovery_cmd(pwp, ds, xp); 4726*4c06356bSdh } 4727*4c06356bSdh mutex_exit(&xp->statlock); 4728*4c06356bSdh } 4729*4c06356bSdh } 4730*4c06356bSdh if (LE_32(msg[3]) == 0) { 4731*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "TMF completed with no response"); 4732*4c06356bSdh return (EIO); 4733*4c06356bSdh } 4734*4c06356bSdh pmcs_endian_transform(pwp, local, &msg[5], ssp_rsp_evec); 4735*4c06356bSdh xd = (uint8_t *)(&msg[5]); 4736*4c06356bSdh xd += SAS_RSP_HDR_SIZE; 4737*4c06356bSdh if (rptr->datapres != SAS_RSP_DATAPRES_RESPONSE_DATA) { 4738*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 4739*4c06356bSdh "%s: TMF response not RESPONSE DATA (0x%x)", 4740*4c06356bSdh __func__, rptr->datapres); 4741*4c06356bSdh return (EIO); 4742*4c06356bSdh } 4743*4c06356bSdh if (rptr->response_data_length != 4) { 4744*4c06356bSdh pmcs_print_entry(pwp, PMCS_PRT_DEBUG, 4745*4c06356bSdh "Bad SAS RESPONSE DATA LENGTH", msg); 4746*4c06356bSdh return (EIO); 4747*4c06356bSdh } 4748*4c06356bSdh (void) memcpy(&status, xd, sizeof (uint32_t)); 4749*4c06356bSdh status = BE_32(status); 4750*4c06356bSdh if (response != NULL) 4751*4c06356bSdh *response = status; 4752*4c06356bSdh /* 4753*4c06356bSdh * The status is actually in the low-order byte. The upper three 4754*4c06356bSdh * bytes contain additional information for the TMFs that support them. 4755*4c06356bSdh * However, at this time we do not issue any of those. In the other 4756*4c06356bSdh * cases, the upper three bytes are supposed to be 0, but it appears 4757*4c06356bSdh * they aren't always. Just mask them off. 4758*4c06356bSdh */ 4759*4c06356bSdh switch (status & 0xff) { 4760*4c06356bSdh case SAS_RSP_TMF_COMPLETE: 4761*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: TMF complete", __func__); 4762*4c06356bSdh result = 0; 4763*4c06356bSdh break; 4764*4c06356bSdh case SAS_RSP_TMF_SUCCEEDED: 4765*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: TMF succeeded", __func__); 4766*4c06356bSdh result = 0; 4767*4c06356bSdh break; 4768*4c06356bSdh case SAS_RSP_INVALID_FRAME: 4769*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 4770*4c06356bSdh "%s: TMF returned INVALID FRAME", __func__); 4771*4c06356bSdh result = EIO; 4772*4c06356bSdh break; 4773*4c06356bSdh case SAS_RSP_TMF_NOT_SUPPORTED: 4774*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 4775*4c06356bSdh "%s: TMF returned TMF NOT SUPPORTED", __func__); 4776*4c06356bSdh result = EIO; 4777*4c06356bSdh break; 4778*4c06356bSdh case SAS_RSP_TMF_FAILED: 4779*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 4780*4c06356bSdh "%s: TMF returned TMF FAILED", __func__); 4781*4c06356bSdh result = EIO; 4782*4c06356bSdh break; 4783*4c06356bSdh case SAS_RSP_TMF_INCORRECT_LUN: 4784*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 4785*4c06356bSdh "%s: TMF returned INCORRECT LUN", __func__); 4786*4c06356bSdh result = EIO; 4787*4c06356bSdh break; 4788*4c06356bSdh case SAS_RSP_OVERLAPPED_OIPTTA: 4789*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 4790*4c06356bSdh "%s: TMF returned OVERLAPPED INITIATOR PORT TRANSFER TAG " 4791*4c06356bSdh "ATTEMPTED", __func__); 4792*4c06356bSdh result = EIO; 4793*4c06356bSdh break; 4794*4c06356bSdh default: 4795*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 4796*4c06356bSdh "%s: TMF returned unknown code 0x%x", __func__, status); 4797*4c06356bSdh result = EIO; 4798*4c06356bSdh break; 4799*4c06356bSdh } 4800*4c06356bSdh return (result); 4801*4c06356bSdh } 4802*4c06356bSdh 4803*4c06356bSdh /* 4804*4c06356bSdh * Called with PHY lock held and scratch acquired 4805*4c06356bSdh */ 4806*4c06356bSdh int 4807*4c06356bSdh pmcs_sata_abort_ncq(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 4808*4c06356bSdh { 4809*4c06356bSdh const char *utag_fail_fmt = "%s: untagged NCQ command failure"; 4810*4c06356bSdh const char *tag_fail_fmt = "%s: NCQ command failure (tag 0x%x)"; 4811*4c06356bSdh uint32_t msg[PMCS_QENTRY_SIZE], *ptr, result, status; 4812*4c06356bSdh uint8_t *fp = pwp->scratch, ds; 4813*4c06356bSdh fis_t fis; 4814*4c06356bSdh pmcwork_t *pwrk; 4815*4c06356bSdh pmcs_xscsi_t *tgt; 4816*4c06356bSdh 4817*4c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 4818*4c06356bSdh if (pwrk == NULL) { 4819*4c06356bSdh return (ENOMEM); 4820*4c06356bSdh } 4821*4c06356bSdh msg[0] = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE, 4822*4c06356bSdh PMCIN_SATA_HOST_IO_START)); 4823*4c06356bSdh msg[1] = LE_32(pwrk->htag); 4824*4c06356bSdh msg[2] = LE_32(pptr->device_id); 4825*4c06356bSdh msg[3] = LE_32(512); 4826*4c06356bSdh msg[4] = LE_32(SATA_PROTOCOL_PIO | PMCIN_DATADIR_2_INI); 4827*4c06356bSdh msg[5] = LE_32((READ_LOG_EXT << 16) | (C_BIT << 8) | FIS_REG_H2DEV); 4828*4c06356bSdh msg[6] = LE_32(0x10); 4829*4c06356bSdh msg[8] = LE_32(1); 4830*4c06356bSdh msg[9] = 0; 4831*4c06356bSdh msg[10] = 0; 4832*4c06356bSdh msg[11] = 0; 4833*4c06356bSdh msg[12] = LE_32(DWORD0(pwp->scratch_dma)); 4834*4c06356bSdh msg[13] = LE_32(DWORD1(pwp->scratch_dma)); 4835*4c06356bSdh msg[14] = LE_32(512); 4836*4c06356bSdh msg[15] = 0; 4837*4c06356bSdh 4838*4c06356bSdh pwrk->arg = msg; 4839*4c06356bSdh pwrk->dtype = pptr->dtype; 4840*4c06356bSdh 4841*4c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 4842*4c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 4843*4c06356bSdh if (ptr == NULL) { 4844*4c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 4845*4c06356bSdh pmcs_pwork(pwp, pwrk); 4846*4c06356bSdh return (ENOMEM); 4847*4c06356bSdh } 4848*4c06356bSdh COPY_MESSAGE(ptr, msg, PMCS_QENTRY_SIZE); 4849*4c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 4850*4c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 4851*4c06356bSdh 4852*4c06356bSdh pmcs_unlock_phy(pptr); 4853*4c06356bSdh WAIT_FOR(pwrk, 250, result); 4854*4c06356bSdh pmcs_lock_phy(pptr); 4855*4c06356bSdh pmcs_pwork(pwp, pwrk); 4856*4c06356bSdh 4857*4c06356bSdh if (result) { 4858*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, pmcs_timeo, __func__); 4859*4c06356bSdh return (EIO); 4860*4c06356bSdh } 4861*4c06356bSdh status = LE_32(msg[2]); 4862*4c06356bSdh if (status != PMCOUT_STATUS_OK || LE_32(msg[3])) { 4863*4c06356bSdh tgt = pptr->target; 4864*4c06356bSdh if (tgt == NULL) { 4865*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 4866*4c06356bSdh "%s: cannot find target for phy 0x%p for " 4867*4c06356bSdh "dev state recovery", __func__, (void *)pptr); 4868*4c06356bSdh return (EIO); 4869*4c06356bSdh } 4870*4c06356bSdh 4871*4c06356bSdh mutex_enter(&tgt->statlock); 4872*4c06356bSdh 4873*4c06356bSdh pmcs_print_entry(pwp, PMCS_PRT_DEBUG, "READ LOG EXT", msg); 4874*4c06356bSdh if ((status == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) || 4875*4c06356bSdh (status == PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK) || 4876*4c06356bSdh (status == PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS)) { 4877*4c06356bSdh ds = PMCS_DEVICE_STATE_NON_OPERATIONAL; 4878*4c06356bSdh } else { 4879*4c06356bSdh ds = PMCS_DEVICE_STATE_IN_RECOVERY; 4880*4c06356bSdh } 4881*4c06356bSdh if (tgt->dev_state != ds) { 4882*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Trying SATA DS Error" 4883*4c06356bSdh " Recovery for tgt(0x%p) for status(%s)", 4884*4c06356bSdh __func__, (void *)tgt, pmcs_status_str(status)); 4885*4c06356bSdh (void) pmcs_send_err_recovery_cmd(pwp, ds, tgt); 4886*4c06356bSdh } 4887*4c06356bSdh 4888*4c06356bSdh mutex_exit(&tgt->statlock); 4889*4c06356bSdh return (EIO); 4890*4c06356bSdh } 4891*4c06356bSdh fis[0] = (fp[4] << 24) | (fp[3] << 16) | (fp[2] << 8) | FIS_REG_D2H; 4892*4c06356bSdh fis[1] = (fp[8] << 24) | (fp[7] << 16) | (fp[6] << 8) | fp[5]; 4893*4c06356bSdh fis[2] = (fp[12] << 24) | (fp[11] << 16) | (fp[10] << 8) | fp[9]; 4894*4c06356bSdh fis[3] = (fp[16] << 24) | (fp[15] << 16) | (fp[14] << 8) | fp[13]; 4895*4c06356bSdh fis[4] = 0; 4896*4c06356bSdh if (fp[0] & 0x80) { 4897*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, utag_fail_fmt, __func__); 4898*4c06356bSdh } else { 4899*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, tag_fail_fmt, __func__, 4900*4c06356bSdh fp[0] & 0x1f); 4901*4c06356bSdh } 4902*4c06356bSdh pmcs_fis_dump(pwp, fis); 4903*4c06356bSdh pptr->need_rl_ext = 0; 4904*4c06356bSdh return (0); 4905*4c06356bSdh } 4906*4c06356bSdh 4907*4c06356bSdh /* 4908*4c06356bSdh * Transform a structure from CPU to Device endian format, or 4909*4c06356bSdh * vice versa, based upon a transformation vector. 4910*4c06356bSdh * 4911*4c06356bSdh * A transformation vector is an array of bytes, each byte 4912*4c06356bSdh * of which is defined thusly: 4913*4c06356bSdh * 4914*4c06356bSdh * bit 7: from CPU to desired endian, otherwise from desired endian 4915*4c06356bSdh * to CPU format 4916*4c06356bSdh * bit 6: Big Endian, else Little Endian 4917*4c06356bSdh * bits 5-4: 4918*4c06356bSdh * 00 Undefined 4919*4c06356bSdh * 01 One Byte quantities 4920*4c06356bSdh * 02 Two Byte quantities 4921*4c06356bSdh * 03 Four Byte quantities 4922*4c06356bSdh * 4923*4c06356bSdh * bits 3-0: 4924*4c06356bSdh * 00 Undefined 4925*4c06356bSdh * Number of quantities to transform 4926*4c06356bSdh * 4927*4c06356bSdh * The vector is terminated by a 0 value. 4928*4c06356bSdh */ 4929*4c06356bSdh 4930*4c06356bSdh void 4931*4c06356bSdh pmcs_endian_transform(pmcs_hw_t *pwp, void *orig_out, void *orig_in, 4932*4c06356bSdh const uint8_t *xfvec) 4933*4c06356bSdh { 4934*4c06356bSdh uint8_t c, *out = orig_out, *in = orig_in; 4935*4c06356bSdh 4936*4c06356bSdh if (xfvec == NULL) { 4937*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: null xfvec", __func__); 4938*4c06356bSdh return; 4939*4c06356bSdh } 4940*4c06356bSdh if (out == NULL) { 4941*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: null out", __func__); 4942*4c06356bSdh return; 4943*4c06356bSdh } 4944*4c06356bSdh if (in == NULL) { 4945*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: null in", __func__); 4946*4c06356bSdh return; 4947*4c06356bSdh } 4948*4c06356bSdh while ((c = *xfvec++) != 0) { 4949*4c06356bSdh int nbyt = (c & 0xf); 4950*4c06356bSdh int size = (c >> 4) & 0x3; 4951*4c06356bSdh int bige = (c >> 4) & 0x4; 4952*4c06356bSdh 4953*4c06356bSdh switch (size) { 4954*4c06356bSdh case 1: 4955*4c06356bSdh { 4956*4c06356bSdh while (nbyt-- > 0) { 4957*4c06356bSdh *out++ = *in++; 4958*4c06356bSdh } 4959*4c06356bSdh break; 4960*4c06356bSdh } 4961*4c06356bSdh case 2: 4962*4c06356bSdh { 4963*4c06356bSdh uint16_t tmp; 4964*4c06356bSdh while (nbyt-- > 0) { 4965*4c06356bSdh (void) memcpy(&tmp, in, sizeof (uint16_t)); 4966*4c06356bSdh if (bige) { 4967*4c06356bSdh tmp = BE_16(tmp); 4968*4c06356bSdh } else { 4969*4c06356bSdh tmp = LE_16(tmp); 4970*4c06356bSdh } 4971*4c06356bSdh (void) memcpy(out, &tmp, sizeof (uint16_t)); 4972*4c06356bSdh out += sizeof (uint16_t); 4973*4c06356bSdh in += sizeof (uint16_t); 4974*4c06356bSdh } 4975*4c06356bSdh break; 4976*4c06356bSdh } 4977*4c06356bSdh case 3: 4978*4c06356bSdh { 4979*4c06356bSdh uint32_t tmp; 4980*4c06356bSdh while (nbyt-- > 0) { 4981*4c06356bSdh (void) memcpy(&tmp, in, sizeof (uint32_t)); 4982*4c06356bSdh if (bige) { 4983*4c06356bSdh tmp = BE_32(tmp); 4984*4c06356bSdh } else { 4985*4c06356bSdh tmp = LE_32(tmp); 4986*4c06356bSdh } 4987*4c06356bSdh (void) memcpy(out, &tmp, sizeof (uint32_t)); 4988*4c06356bSdh out += sizeof (uint32_t); 4989*4c06356bSdh in += sizeof (uint32_t); 4990*4c06356bSdh } 4991*4c06356bSdh break; 4992*4c06356bSdh } 4993*4c06356bSdh default: 4994*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: bad size", __func__); 4995*4c06356bSdh return; 4996*4c06356bSdh } 4997*4c06356bSdh } 4998*4c06356bSdh } 4999*4c06356bSdh 5000*4c06356bSdh const char * 5001*4c06356bSdh pmcs_get_rate(unsigned int linkrt) 5002*4c06356bSdh { 5003*4c06356bSdh const char *rate; 5004*4c06356bSdh switch (linkrt) { 5005*4c06356bSdh case SAS_LINK_RATE_1_5GBIT: 5006*4c06356bSdh rate = "1.5"; 5007*4c06356bSdh break; 5008*4c06356bSdh case SAS_LINK_RATE_3GBIT: 5009*4c06356bSdh rate = "3.0"; 5010*4c06356bSdh break; 5011*4c06356bSdh case SAS_LINK_RATE_6GBIT: 5012*4c06356bSdh rate = "6.0"; 5013*4c06356bSdh break; 5014*4c06356bSdh default: 5015*4c06356bSdh rate = "???"; 5016*4c06356bSdh break; 5017*4c06356bSdh } 5018*4c06356bSdh return (rate); 5019*4c06356bSdh } 5020*4c06356bSdh 5021*4c06356bSdh const char * 5022*4c06356bSdh pmcs_get_typename(pmcs_dtype_t type) 5023*4c06356bSdh { 5024*4c06356bSdh switch (type) { 5025*4c06356bSdh case NOTHING: 5026*4c06356bSdh return ("NIL"); 5027*4c06356bSdh case SATA: 5028*4c06356bSdh return ("SATA"); 5029*4c06356bSdh case SAS: 5030*4c06356bSdh return ("SSP"); 5031*4c06356bSdh case EXPANDER: 5032*4c06356bSdh return ("EXPANDER"); 5033*4c06356bSdh } 5034*4c06356bSdh return ("????"); 5035*4c06356bSdh } 5036*4c06356bSdh 5037*4c06356bSdh const char * 5038*4c06356bSdh pmcs_tmf2str(int tmf) 5039*4c06356bSdh { 5040*4c06356bSdh switch (tmf) { 5041*4c06356bSdh case SAS_ABORT_TASK: 5042*4c06356bSdh return ("Abort Task"); 5043*4c06356bSdh case SAS_ABORT_TASK_SET: 5044*4c06356bSdh return ("Abort Task Set"); 5045*4c06356bSdh case SAS_CLEAR_TASK_SET: 5046*4c06356bSdh return ("Clear Task Set"); 5047*4c06356bSdh case SAS_LOGICAL_UNIT_RESET: 5048*4c06356bSdh return ("Logical Unit Reset"); 5049*4c06356bSdh case SAS_I_T_NEXUS_RESET: 5050*4c06356bSdh return ("I_T Nexus Reset"); 5051*4c06356bSdh case SAS_CLEAR_ACA: 5052*4c06356bSdh return ("Clear ACA"); 5053*4c06356bSdh case SAS_QUERY_TASK: 5054*4c06356bSdh return ("Query Task"); 5055*4c06356bSdh case SAS_QUERY_TASK_SET: 5056*4c06356bSdh return ("Query Task Set"); 5057*4c06356bSdh case SAS_QUERY_UNIT_ATTENTION: 5058*4c06356bSdh return ("Query Unit Attention"); 5059*4c06356bSdh default: 5060*4c06356bSdh return ("Unknown"); 5061*4c06356bSdh } 5062*4c06356bSdh } 5063*4c06356bSdh 5064*4c06356bSdh const char * 5065*4c06356bSdh pmcs_status_str(uint32_t status) 5066*4c06356bSdh { 5067*4c06356bSdh switch (status) { 5068*4c06356bSdh case PMCOUT_STATUS_OK: 5069*4c06356bSdh return ("OK"); 5070*4c06356bSdh case PMCOUT_STATUS_ABORTED: 5071*4c06356bSdh return ("ABORTED"); 5072*4c06356bSdh case PMCOUT_STATUS_OVERFLOW: 5073*4c06356bSdh return ("OVERFLOW"); 5074*4c06356bSdh case PMCOUT_STATUS_UNDERFLOW: 5075*4c06356bSdh return ("UNDERFLOW"); 5076*4c06356bSdh case PMCOUT_STATUS_FAILED: 5077*4c06356bSdh return ("FAILED"); 5078*4c06356bSdh case PMCOUT_STATUS_ABORT_RESET: 5079*4c06356bSdh return ("ABORT_RESET"); 5080*4c06356bSdh case PMCOUT_STATUS_IO_NOT_VALID: 5081*4c06356bSdh return ("IO_NOT_VALID"); 5082*4c06356bSdh case PMCOUT_STATUS_NO_DEVICE: 5083*4c06356bSdh return ("NO_DEVICE"); 5084*4c06356bSdh case PMCOUT_STATUS_ILLEGAL_PARAMETER: 5085*4c06356bSdh return ("ILLEGAL_PARAMETER"); 5086*4c06356bSdh case PMCOUT_STATUS_LINK_FAILURE: 5087*4c06356bSdh return ("LINK_FAILURE"); 5088*4c06356bSdh case PMCOUT_STATUS_PROG_ERROR: 5089*4c06356bSdh return ("PROG_ERROR"); 5090*4c06356bSdh case PMCOUT_STATUS_EDC_IN_ERROR: 5091*4c06356bSdh return ("EDC_IN_ERROR"); 5092*4c06356bSdh case PMCOUT_STATUS_EDC_OUT_ERROR: 5093*4c06356bSdh return ("EDC_OUT_ERROR"); 5094*4c06356bSdh case PMCOUT_STATUS_ERROR_HW_TIMEOUT: 5095*4c06356bSdh return ("ERROR_HW_TIMEOUT"); 5096*4c06356bSdh case PMCOUT_STATUS_XFER_ERR_BREAK: 5097*4c06356bSdh return ("XFER_ERR_BREAK"); 5098*4c06356bSdh case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY: 5099*4c06356bSdh return ("XFER_ERR_PHY_NOT_READY"); 5100*4c06356bSdh case PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED: 5101*4c06356bSdh return ("OPEN_CNX_PROTOCOL_NOT_SUPPORTED"); 5102*4c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION: 5103*4c06356bSdh return ("OPEN_CNX_ERROR_ZONE_VIOLATION"); 5104*4c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK: 5105*4c06356bSdh return ("OPEN_CNX_ERROR_BREAK"); 5106*4c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS: 5107*4c06356bSdh return ("OPEN_CNX_ERROR_IT_NEXUS_LOSS"); 5108*4c06356bSdh case PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION: 5109*4c06356bSdh return ("OPENCNX_ERROR_BAD_DESTINATION"); 5110*4c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: 5111*4c06356bSdh return ("OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED"); 5112*4c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY: 5113*4c06356bSdh return ("OPEN_CNX_ERROR_STP_RESOURCES_BUSY"); 5114*4c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION: 5115*4c06356bSdh return ("OPEN_CNX_ERROR_WRONG_DESTINATION"); 5116*4c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_EROOR: 5117*4c06356bSdh return ("OPEN_CNX_ERROR_UNKNOWN_EROOR"); 5118*4c06356bSdh case PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED: 5119*4c06356bSdh return ("IO_XFER_ERROR_NAK_RECEIVED"); 5120*4c06356bSdh case PMCOUT_STATUS_XFER_ERROR_ACK_NAK_TIMEOUT: 5121*4c06356bSdh return ("XFER_ERROR_ACK_NAK_TIMEOUT"); 5122*4c06356bSdh case PMCOUT_STATUS_XFER_ERROR_PEER_ABORTED: 5123*4c06356bSdh return ("XFER_ERROR_PEER_ABORTED"); 5124*4c06356bSdh case PMCOUT_STATUS_XFER_ERROR_RX_FRAME: 5125*4c06356bSdh return ("XFER_ERROR_RX_FRAME"); 5126*4c06356bSdh case PMCOUT_STATUS_IO_XFER_ERROR_DMA: 5127*4c06356bSdh return ("IO_XFER_ERROR_DMA"); 5128*4c06356bSdh case PMCOUT_STATUS_XFER_ERROR_CREDIT_TIMEOUT: 5129*4c06356bSdh return ("XFER_ERROR_CREDIT_TIMEOUT"); 5130*4c06356bSdh case PMCOUT_STATUS_XFER_ERROR_SATA_LINK_TIMEOUT: 5131*4c06356bSdh return ("XFER_ERROR_SATA_LINK_TIMEOUT"); 5132*4c06356bSdh case PMCOUT_STATUS_XFER_ERROR_SATA: 5133*4c06356bSdh return ("XFER_ERROR_SATA"); 5134*4c06356bSdh case PMCOUT_STATUS_XFER_ERROR_REJECTED_NCQ_MODE: 5135*4c06356bSdh return ("XFER_ERROR_REJECTED_NCQ_MODE"); 5136*4c06356bSdh case PMCOUT_STATUS_XFER_ERROR_ABORTED_DUE_TO_SRST: 5137*4c06356bSdh return ("XFER_ERROR_ABORTED_DUE_TO_SRST"); 5138*4c06356bSdh case PMCOUT_STATUS_XFER_ERROR_ABORTED_NCQ_MODE: 5139*4c06356bSdh return ("XFER_ERROR_ABORTED_NCQ_MODE"); 5140*4c06356bSdh case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT: 5141*4c06356bSdh return ("IO_XFER_OPEN_RETRY_TIMEOUT"); 5142*4c06356bSdh case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR: 5143*4c06356bSdh return ("SMP_RESP_CONNECTION_ERROR"); 5144*4c06356bSdh case PMCOUT_STATUS_XFER_ERROR_UNEXPECTED_PHASE: 5145*4c06356bSdh return ("XFER_ERROR_UNEXPECTED_PHASE"); 5146*4c06356bSdh case PMCOUT_STATUS_XFER_ERROR_RDY_OVERRUN: 5147*4c06356bSdh return ("XFER_ERROR_RDY_OVERRUN"); 5148*4c06356bSdh case PMCOUT_STATUS_XFER_ERROR_RDY_NOT_EXPECTED: 5149*4c06356bSdh return ("XFER_ERROR_RDY_NOT_EXPECTED"); 5150*4c06356bSdh case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT: 5151*4c06356bSdh return ("XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT"); 5152*4c06356bSdh case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK: 5153*4c06356bSdh return ("XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK"); 5154*4c06356bSdh case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK: 5155*4c06356bSdh return ("XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK"); 5156*4c06356bSdh case PMCOUT_STATUS_XFER_ERROR_OFFSET_MISMATCH: 5157*4c06356bSdh return ("XFER_ERROR_OFFSET_MISMATCH"); 5158*4c06356bSdh case PMCOUT_STATUS_XFER_ERROR_ZERO_DATA_LEN: 5159*4c06356bSdh return ("XFER_ERROR_ZERO_DATA_LEN"); 5160*4c06356bSdh case PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED: 5161*4c06356bSdh return ("XFER_CMD_FRAME_ISSUED"); 5162*4c06356bSdh case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE: 5163*4c06356bSdh return ("ERROR_INTERNAL_SMP_RESOURCE"); 5164*4c06356bSdh case PMCOUT_STATUS_IO_PORT_IN_RESET: 5165*4c06356bSdh return ("IO_PORT_IN_RESET"); 5166*4c06356bSdh case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL: 5167*4c06356bSdh return ("DEVICE STATE NON-OPERATIONAL"); 5168*4c06356bSdh case PMCOUT_STATUS_IO_DS_IN_RECOVERY: 5169*4c06356bSdh return ("DEVICE STATE IN RECOVERY"); 5170*4c06356bSdh default: 5171*4c06356bSdh return (NULL); 5172*4c06356bSdh } 5173*4c06356bSdh } 5174*4c06356bSdh 5175*4c06356bSdh uint64_t 5176*4c06356bSdh pmcs_barray2wwn(uint8_t ba[8]) 5177*4c06356bSdh { 5178*4c06356bSdh uint64_t result = 0; 5179*4c06356bSdh int i; 5180*4c06356bSdh 5181*4c06356bSdh for (i = 0; i < 8; i++) { 5182*4c06356bSdh result <<= 8; 5183*4c06356bSdh result |= ba[i]; 5184*4c06356bSdh } 5185*4c06356bSdh return (result); 5186*4c06356bSdh } 5187*4c06356bSdh 5188*4c06356bSdh void 5189*4c06356bSdh pmcs_wwn2barray(uint64_t wwn, uint8_t ba[8]) 5190*4c06356bSdh { 5191*4c06356bSdh int i; 5192*4c06356bSdh for (i = 0; i < 8; i++) { 5193*4c06356bSdh ba[7 - i] = wwn & 0xff; 5194*4c06356bSdh wwn >>= 8; 5195*4c06356bSdh } 5196*4c06356bSdh } 5197*4c06356bSdh 5198*4c06356bSdh void 5199*4c06356bSdh pmcs_report_fwversion(pmcs_hw_t *pwp) 5200*4c06356bSdh { 5201*4c06356bSdh const char *fwsupport; 5202*4c06356bSdh switch (PMCS_FW_TYPE(pwp)) { 5203*4c06356bSdh case PMCS_FW_TYPE_RELEASED: 5204*4c06356bSdh fwsupport = "Released"; 5205*4c06356bSdh break; 5206*4c06356bSdh case PMCS_FW_TYPE_DEVELOPMENT: 5207*4c06356bSdh fwsupport = "Development"; 5208*4c06356bSdh break; 5209*4c06356bSdh case PMCS_FW_TYPE_ALPHA: 5210*4c06356bSdh fwsupport = "Alpha"; 5211*4c06356bSdh break; 5212*4c06356bSdh case PMCS_FW_TYPE_BETA: 5213*4c06356bSdh fwsupport = "Beta"; 5214*4c06356bSdh break; 5215*4c06356bSdh default: 5216*4c06356bSdh fwsupport = "Special"; 5217*4c06356bSdh break; 5218*4c06356bSdh } 5219*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, 5220*4c06356bSdh "Chip Revision: %c; F/W Revision %x.%x.%x %s", 'A' + pwp->chiprev, 5221*4c06356bSdh PMCS_FW_MAJOR(pwp), PMCS_FW_MINOR(pwp), PMCS_FW_MICRO(pwp), 5222*4c06356bSdh fwsupport); 5223*4c06356bSdh } 5224*4c06356bSdh 5225*4c06356bSdh void 5226*4c06356bSdh pmcs_phy_name(pmcs_hw_t *pwp, pmcs_phy_t *pptr, char *obuf, size_t olen) 5227*4c06356bSdh { 5228*4c06356bSdh if (pptr->parent) { 5229*4c06356bSdh pmcs_phy_name(pwp, pptr->parent, obuf, olen); 5230*4c06356bSdh (void) snprintf(obuf, olen, "%s.%02x", obuf, pptr->phynum); 5231*4c06356bSdh } else { 5232*4c06356bSdh (void) snprintf(obuf, olen, "pp%02x", pptr->phynum); 5233*4c06356bSdh } 5234*4c06356bSdh } 5235*4c06356bSdh 5236*4c06356bSdh /* 5237*4c06356bSdh * Implementation for pmcs_find_phy_by_devid. 5238*4c06356bSdh * If the PHY is found, it is returned locked. 5239*4c06356bSdh */ 5240*4c06356bSdh static pmcs_phy_t * 5241*4c06356bSdh pmcs_find_phy_by_devid_impl(pmcs_phy_t *phyp, uint32_t device_id) 5242*4c06356bSdh { 5243*4c06356bSdh pmcs_phy_t *match, *cphyp, *nphyp; 5244*4c06356bSdh 5245*4c06356bSdh ASSERT(!mutex_owned(&phyp->phy_lock)); 5246*4c06356bSdh 5247*4c06356bSdh while (phyp) { 5248*4c06356bSdh pmcs_lock_phy(phyp); 5249*4c06356bSdh 5250*4c06356bSdh if ((phyp->valid_device_id) && (phyp->device_id == device_id)) { 5251*4c06356bSdh return (phyp); 5252*4c06356bSdh } 5253*4c06356bSdh if (phyp->children) { 5254*4c06356bSdh cphyp = phyp->children; 5255*4c06356bSdh pmcs_unlock_phy(phyp); 5256*4c06356bSdh match = pmcs_find_phy_by_devid_impl(cphyp, device_id); 5257*4c06356bSdh if (match) { 5258*4c06356bSdh ASSERT(mutex_owned(&match->phy_lock)); 5259*4c06356bSdh return (match); 5260*4c06356bSdh } 5261*4c06356bSdh pmcs_lock_phy(phyp); 5262*4c06356bSdh } 5263*4c06356bSdh 5264*4c06356bSdh if (IS_ROOT_PHY(phyp)) { 5265*4c06356bSdh pmcs_unlock_phy(phyp); 5266*4c06356bSdh phyp = NULL; 5267*4c06356bSdh } else { 5268*4c06356bSdh nphyp = phyp->sibling; 5269*4c06356bSdh pmcs_unlock_phy(phyp); 5270*4c06356bSdh phyp = nphyp; 5271*4c06356bSdh } 5272*4c06356bSdh } 5273*4c06356bSdh 5274*4c06356bSdh return (NULL); 5275*4c06356bSdh } 5276*4c06356bSdh 5277*4c06356bSdh /* 5278*4c06356bSdh * If the PHY is found, it is returned locked 5279*4c06356bSdh */ 5280*4c06356bSdh pmcs_phy_t * 5281*4c06356bSdh pmcs_find_phy_by_devid(pmcs_hw_t *pwp, uint32_t device_id) 5282*4c06356bSdh { 5283*4c06356bSdh pmcs_phy_t *phyp, *match = NULL; 5284*4c06356bSdh 5285*4c06356bSdh phyp = pwp->root_phys; 5286*4c06356bSdh 5287*4c06356bSdh while (phyp) { 5288*4c06356bSdh match = pmcs_find_phy_by_devid_impl(phyp, device_id); 5289*4c06356bSdh if (match) { 5290*4c06356bSdh ASSERT(mutex_owned(&match->phy_lock)); 5291*4c06356bSdh return (match); 5292*4c06356bSdh } 5293*4c06356bSdh phyp = phyp->sibling; 5294*4c06356bSdh } 5295*4c06356bSdh 5296*4c06356bSdh return (NULL); 5297*4c06356bSdh } 5298*4c06356bSdh 5299*4c06356bSdh /* 5300*4c06356bSdh * This function is called as a sanity check to ensure that a newly registered 5301*4c06356bSdh * PHY doesn't have a device_id that exists with another registered PHY. 5302*4c06356bSdh */ 5303*4c06356bSdh static boolean_t 5304*4c06356bSdh pmcs_validate_devid(pmcs_phy_t *parent, pmcs_phy_t *phyp, uint32_t device_id) 5305*4c06356bSdh { 5306*4c06356bSdh pmcs_phy_t *pptr; 5307*4c06356bSdh boolean_t rval; 5308*4c06356bSdh 5309*4c06356bSdh pptr = parent; 5310*4c06356bSdh 5311*4c06356bSdh while (pptr) { 5312*4c06356bSdh if (pptr->valid_device_id && (pptr != phyp) && 5313*4c06356bSdh (pptr->device_id == device_id)) { 5314*4c06356bSdh pmcs_prt(pptr->pwp, PMCS_PRT_DEBUG, 5315*4c06356bSdh "%s: phy %s already exists as %s with " 5316*4c06356bSdh "device id 0x%x", __func__, phyp->path, 5317*4c06356bSdh pptr->path, device_id); 5318*4c06356bSdh return (B_FALSE); 5319*4c06356bSdh } 5320*4c06356bSdh 5321*4c06356bSdh if (pptr->children) { 5322*4c06356bSdh rval = pmcs_validate_devid(pptr->children, phyp, 5323*4c06356bSdh device_id); 5324*4c06356bSdh if (rval == B_FALSE) { 5325*4c06356bSdh return (rval); 5326*4c06356bSdh } 5327*4c06356bSdh } 5328*4c06356bSdh 5329*4c06356bSdh pptr = pptr->sibling; 5330*4c06356bSdh } 5331*4c06356bSdh 5332*4c06356bSdh /* This PHY and device_id are valid */ 5333*4c06356bSdh return (B_TRUE); 5334*4c06356bSdh } 5335*4c06356bSdh 5336*4c06356bSdh /* 5337*4c06356bSdh * If the PHY is found, it is returned locked 5338*4c06356bSdh */ 5339*4c06356bSdh static pmcs_phy_t * 5340*4c06356bSdh pmcs_find_phy_by_wwn_impl(pmcs_phy_t *phyp, uint8_t *wwn) 5341*4c06356bSdh { 5342*4c06356bSdh pmcs_phy_t *matched_phy, *cphyp, *nphyp; 5343*4c06356bSdh 5344*4c06356bSdh ASSERT(!mutex_owned(&phyp->phy_lock)); 5345*4c06356bSdh 5346*4c06356bSdh while (phyp) { 5347*4c06356bSdh pmcs_lock_phy(phyp); 5348*4c06356bSdh 5349*4c06356bSdh if (phyp->valid_device_id) { 5350*4c06356bSdh if (memcmp(phyp->sas_address, wwn, 8) == 0) { 5351*4c06356bSdh return (phyp); 5352*4c06356bSdh } 5353*4c06356bSdh } 5354*4c06356bSdh 5355*4c06356bSdh if (phyp->children) { 5356*4c06356bSdh cphyp = phyp->children; 5357*4c06356bSdh pmcs_unlock_phy(phyp); 5358*4c06356bSdh matched_phy = pmcs_find_phy_by_wwn_impl(cphyp, wwn); 5359*4c06356bSdh if (matched_phy) { 5360*4c06356bSdh ASSERT(mutex_owned(&matched_phy->phy_lock)); 5361*4c06356bSdh return (matched_phy); 5362*4c06356bSdh } 5363*4c06356bSdh pmcs_lock_phy(phyp); 5364*4c06356bSdh } 5365*4c06356bSdh 5366*4c06356bSdh /* 5367*4c06356bSdh * Only iterate through non-root PHYs 5368*4c06356bSdh */ 5369*4c06356bSdh if (IS_ROOT_PHY(phyp)) { 5370*4c06356bSdh pmcs_unlock_phy(phyp); 5371*4c06356bSdh phyp = NULL; 5372*4c06356bSdh } else { 5373*4c06356bSdh nphyp = phyp->sibling; 5374*4c06356bSdh pmcs_unlock_phy(phyp); 5375*4c06356bSdh phyp = nphyp; 5376*4c06356bSdh } 5377*4c06356bSdh } 5378*4c06356bSdh 5379*4c06356bSdh return (NULL); 5380*4c06356bSdh } 5381*4c06356bSdh 5382*4c06356bSdh pmcs_phy_t * 5383*4c06356bSdh pmcs_find_phy_by_wwn(pmcs_hw_t *pwp, uint64_t wwn) 5384*4c06356bSdh { 5385*4c06356bSdh uint8_t ebstr[8]; 5386*4c06356bSdh pmcs_phy_t *pptr, *matched_phy; 5387*4c06356bSdh 5388*4c06356bSdh pmcs_wwn2barray(wwn, ebstr); 5389*4c06356bSdh 5390*4c06356bSdh pptr = pwp->root_phys; 5391*4c06356bSdh while (pptr) { 5392*4c06356bSdh matched_phy = pmcs_find_phy_by_wwn_impl(pptr, ebstr); 5393*4c06356bSdh if (matched_phy) { 5394*4c06356bSdh ASSERT(mutex_owned(&matched_phy->phy_lock)); 5395*4c06356bSdh return (matched_phy); 5396*4c06356bSdh } 5397*4c06356bSdh 5398*4c06356bSdh pptr = pptr->sibling; 5399*4c06356bSdh } 5400*4c06356bSdh 5401*4c06356bSdh return (NULL); 5402*4c06356bSdh } 5403*4c06356bSdh 5404*4c06356bSdh 5405*4c06356bSdh /* 5406*4c06356bSdh * pmcs_find_phy_by_sas_address 5407*4c06356bSdh * 5408*4c06356bSdh * Find a PHY that both matches "sas_addr" and is on "iport". 5409*4c06356bSdh * If a matching PHY is found, it is returned locked. 5410*4c06356bSdh */ 5411*4c06356bSdh pmcs_phy_t * 5412*4c06356bSdh pmcs_find_phy_by_sas_address(pmcs_hw_t *pwp, pmcs_iport_t *iport, 5413*4c06356bSdh pmcs_phy_t *root, char *sas_addr) 5414*4c06356bSdh { 5415*4c06356bSdh int ua_form = 1; 5416*4c06356bSdh uint64_t wwn; 5417*4c06356bSdh char addr[PMCS_MAX_UA_SIZE]; 5418*4c06356bSdh pmcs_phy_t *pptr, *pnext, *pchild; 5419*4c06356bSdh 5420*4c06356bSdh if (root == NULL) { 5421*4c06356bSdh pptr = pwp->root_phys; 5422*4c06356bSdh } else { 5423*4c06356bSdh pptr = root; 5424*4c06356bSdh } 5425*4c06356bSdh 5426*4c06356bSdh while (pptr) { 5427*4c06356bSdh pmcs_lock_phy(pptr); 5428*4c06356bSdh /* 5429*4c06356bSdh * If the PHY is dead or does not have a valid device ID, 5430*4c06356bSdh * skip it. 5431*4c06356bSdh */ 5432*4c06356bSdh if ((pptr->dead) || (!pptr->valid_device_id)) { 5433*4c06356bSdh goto next_phy; 5434*4c06356bSdh } 5435*4c06356bSdh 5436*4c06356bSdh if (pptr->iport != iport) { 5437*4c06356bSdh goto next_phy; 5438*4c06356bSdh } 5439*4c06356bSdh 5440*4c06356bSdh wwn = pmcs_barray2wwn(pptr->sas_address); 5441*4c06356bSdh (void *) scsi_wwn_to_wwnstr(wwn, ua_form, addr); 5442*4c06356bSdh if (strncmp(addr, sas_addr, strlen(addr)) == 0) { 5443*4c06356bSdh return (pptr); 5444*4c06356bSdh } 5445*4c06356bSdh 5446*4c06356bSdh if (pptr->children) { 5447*4c06356bSdh pchild = pptr->children; 5448*4c06356bSdh pmcs_unlock_phy(pptr); 5449*4c06356bSdh pnext = pmcs_find_phy_by_sas_address(pwp, iport, pchild, 5450*4c06356bSdh sas_addr); 5451*4c06356bSdh if (pnext) { 5452*4c06356bSdh return (pnext); 5453*4c06356bSdh } 5454*4c06356bSdh pmcs_lock_phy(pptr); 5455*4c06356bSdh } 5456*4c06356bSdh 5457*4c06356bSdh next_phy: 5458*4c06356bSdh pnext = pptr->sibling; 5459*4c06356bSdh pmcs_unlock_phy(pptr); 5460*4c06356bSdh pptr = pnext; 5461*4c06356bSdh } 5462*4c06356bSdh 5463*4c06356bSdh return (NULL); 5464*4c06356bSdh } 5465*4c06356bSdh 5466*4c06356bSdh void 5467*4c06356bSdh pmcs_fis_dump(pmcs_hw_t *pwp, fis_t fis) 5468*4c06356bSdh { 5469*4c06356bSdh switch (fis[0] & 0xff) { 5470*4c06356bSdh case FIS_REG_H2DEV: 5471*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, "FIS REGISTER HOST TO DEVICE: " 5472*4c06356bSdh "OP=0x%02x Feature=0x%04x Count=0x%04x Device=0x%02x " 5473*4c06356bSdh "LBA=%llu", BYTE2(fis[0]), BYTE3(fis[2]) << 8 | 5474*4c06356bSdh BYTE3(fis[0]), WORD0(fis[3]), BYTE3(fis[1]), 5475*4c06356bSdh (unsigned long long) 5476*4c06356bSdh (((uint64_t)fis[2] & 0x00ffffff) << 24 | 5477*4c06356bSdh ((uint64_t)fis[1] & 0x00ffffff))); 5478*4c06356bSdh break; 5479*4c06356bSdh case FIS_REG_D2H: 5480*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, "FIS REGISTER DEVICE TO HOST: Stat" 5481*4c06356bSdh "us=0x%02x Error=0x%02x Dev=0x%02x Count=0x%04x LBA=%llu", 5482*4c06356bSdh BYTE2(fis[0]), BYTE3(fis[0]), BYTE3(fis[1]), WORD0(fis[3]), 5483*4c06356bSdh (unsigned long long)(((uint64_t)fis[2] & 0x00ffffff) << 24 | 5484*4c06356bSdh ((uint64_t)fis[1] & 0x00ffffff))); 5485*4c06356bSdh break; 5486*4c06356bSdh default: 5487*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_INFO, "FIS: 0x%08x 0x%08x 0x%08x 0x%08x " 5488*4c06356bSdh "0x%08x 0x%08x 0x%08x", 5489*4c06356bSdh fis[0], fis[1], fis[2], fis[3], fis[4], fis[5], fis[6]); 5490*4c06356bSdh break; 5491*4c06356bSdh } 5492*4c06356bSdh } 5493*4c06356bSdh 5494*4c06356bSdh void 5495*4c06356bSdh pmcs_print_entry(pmcs_hw_t *pwp, int level, char *msg, void *arg) 5496*4c06356bSdh { 5497*4c06356bSdh uint32_t *mb = arg; 5498*4c06356bSdh size_t i; 5499*4c06356bSdh 5500*4c06356bSdh pmcs_prt(pwp, level, msg); 5501*4c06356bSdh for (i = 0; i < (PMCS_QENTRY_SIZE / sizeof (uint32_t)); i += 4) { 5502*4c06356bSdh pmcs_prt(pwp, level, "Offset %2lu: 0x%08x 0x%08x 0x%08" 5503*4c06356bSdh "x 0x%08x", i * sizeof (uint32_t), LE_32(mb[i]), 5504*4c06356bSdh LE_32(mb[i+1]), LE_32(mb[i+2]), 5505*4c06356bSdh LE_32(mb[i+3])); 5506*4c06356bSdh } 5507*4c06356bSdh } 5508*4c06356bSdh 5509*4c06356bSdh /* 5510*4c06356bSdh * If phyp == NULL we're being called from the worker thread, in which 5511*4c06356bSdh * case we need to check all the PHYs. In this case, the softstate lock 5512*4c06356bSdh * will be held. 5513*4c06356bSdh * If phyp is non-NULL, just issue the spinup release for the specified PHY 5514*4c06356bSdh * (which will already be locked). 5515*4c06356bSdh */ 5516*4c06356bSdh void 5517*4c06356bSdh pmcs_spinup_release(pmcs_hw_t *pwp, pmcs_phy_t *phyp) 5518*4c06356bSdh { 5519*4c06356bSdh uint32_t *msg; 5520*4c06356bSdh struct pmcwork *pwrk; 5521*4c06356bSdh pmcs_phy_t *tphyp; 5522*4c06356bSdh 5523*4c06356bSdh if (phyp != NULL) { 5524*4c06356bSdh ASSERT(mutex_owned(&phyp->phy_lock)); 5525*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 5526*4c06356bSdh "%s: Issuing spinup release only for PHY %s", __func__, 5527*4c06356bSdh phyp->path); 5528*4c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 5529*4c06356bSdh msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 5530*4c06356bSdh if (msg == NULL || (pwrk = 5531*4c06356bSdh pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) { 5532*4c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 5533*4c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_SPINUP_RELEASE); 5534*4c06356bSdh return; 5535*4c06356bSdh } 5536*4c06356bSdh 5537*4c06356bSdh phyp->spinup_hold = 0; 5538*4c06356bSdh bzero(msg, PMCS_QENTRY_SIZE); 5539*4c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 5540*4c06356bSdh PMCIN_LOCAL_PHY_CONTROL)); 5541*4c06356bSdh msg[1] = LE_32(pwrk->htag); 5542*4c06356bSdh msg[2] = LE_32((0x10 << 8) | phyp->phynum); 5543*4c06356bSdh 5544*4c06356bSdh pwrk->dtype = phyp->dtype; 5545*4c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 5546*4c06356bSdh mutex_exit(&pwrk->lock); 5547*4c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 5548*4c06356bSdh return; 5549*4c06356bSdh } 5550*4c06356bSdh 5551*4c06356bSdh ASSERT(mutex_owned(&pwp->lock)); 5552*4c06356bSdh 5553*4c06356bSdh tphyp = pwp->root_phys; 5554*4c06356bSdh while (tphyp) { 5555*4c06356bSdh pmcs_lock_phy(tphyp); 5556*4c06356bSdh if (tphyp->spinup_hold == 0) { 5557*4c06356bSdh pmcs_unlock_phy(tphyp); 5558*4c06356bSdh tphyp = tphyp->sibling; 5559*4c06356bSdh continue; 5560*4c06356bSdh } 5561*4c06356bSdh 5562*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 5563*4c06356bSdh "%s: Issuing spinup release for PHY %s", __func__, 5564*4c06356bSdh phyp->path); 5565*4c06356bSdh 5566*4c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 5567*4c06356bSdh msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 5568*4c06356bSdh if (msg == NULL || (pwrk = 5569*4c06356bSdh pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) { 5570*4c06356bSdh pmcs_unlock_phy(tphyp); 5571*4c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 5572*4c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_SPINUP_RELEASE); 5573*4c06356bSdh break; 5574*4c06356bSdh } 5575*4c06356bSdh 5576*4c06356bSdh tphyp->spinup_hold = 0; 5577*4c06356bSdh bzero(msg, PMCS_QENTRY_SIZE); 5578*4c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 5579*4c06356bSdh PMCIN_LOCAL_PHY_CONTROL)); 5580*4c06356bSdh msg[1] = LE_32(pwrk->htag); 5581*4c06356bSdh msg[2] = LE_32((0x10 << 8) | tphyp->phynum); 5582*4c06356bSdh 5583*4c06356bSdh pwrk->dtype = phyp->dtype; 5584*4c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 5585*4c06356bSdh mutex_exit(&pwrk->lock); 5586*4c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 5587*4c06356bSdh pmcs_unlock_phy(tphyp); 5588*4c06356bSdh 5589*4c06356bSdh tphyp = tphyp->sibling; 5590*4c06356bSdh } 5591*4c06356bSdh } 5592*4c06356bSdh 5593*4c06356bSdh /* 5594*4c06356bSdh * Abort commands on dead PHYs and deregister them as well as removing 5595*4c06356bSdh * the associated targets. 5596*4c06356bSdh */ 5597*4c06356bSdh static int 5598*4c06356bSdh pmcs_kill_devices(pmcs_hw_t *pwp, pmcs_phy_t *phyp) 5599*4c06356bSdh { 5600*4c06356bSdh pmcs_phy_t *pnext, *pchild; 5601*4c06356bSdh boolean_t remove_device; 5602*4c06356bSdh int rval = 0; 5603*4c06356bSdh 5604*4c06356bSdh while (phyp) { 5605*4c06356bSdh pmcs_lock_phy(phyp); 5606*4c06356bSdh pchild = phyp->children; 5607*4c06356bSdh pnext = phyp->sibling; 5608*4c06356bSdh pmcs_unlock_phy(phyp); 5609*4c06356bSdh 5610*4c06356bSdh if (pchild) { 5611*4c06356bSdh rval = pmcs_kill_devices(pwp, pchild); 5612*4c06356bSdh if (rval) { 5613*4c06356bSdh return (rval); 5614*4c06356bSdh } 5615*4c06356bSdh } 5616*4c06356bSdh 5617*4c06356bSdh /* 5618*4c06356bSdh * pmcs_remove_device requires the softstate lock. 5619*4c06356bSdh */ 5620*4c06356bSdh mutex_enter(&pwp->lock); 5621*4c06356bSdh pmcs_lock_phy(phyp); 5622*4c06356bSdh if (phyp->dead && phyp->valid_device_id) { 5623*4c06356bSdh remove_device = B_TRUE; 5624*4c06356bSdh } else { 5625*4c06356bSdh remove_device = B_FALSE; 5626*4c06356bSdh } 5627*4c06356bSdh 5628*4c06356bSdh if (remove_device) { 5629*4c06356bSdh pmcs_remove_device(pwp, phyp); 5630*4c06356bSdh mutex_exit(&pwp->lock); 5631*4c06356bSdh 5632*4c06356bSdh rval = pmcs_kill_device(pwp, phyp); 5633*4c06356bSdh 5634*4c06356bSdh if (rval) { 5635*4c06356bSdh pmcs_unlock_phy(phyp); 5636*4c06356bSdh return (rval); 5637*4c06356bSdh } 5638*4c06356bSdh } else { 5639*4c06356bSdh mutex_exit(&pwp->lock); 5640*4c06356bSdh } 5641*4c06356bSdh 5642*4c06356bSdh pmcs_unlock_phy(phyp); 5643*4c06356bSdh phyp = pnext; 5644*4c06356bSdh } 5645*4c06356bSdh 5646*4c06356bSdh return (rval); 5647*4c06356bSdh } 5648*4c06356bSdh 5649*4c06356bSdh /* 5650*4c06356bSdh * Called with PHY locked 5651*4c06356bSdh */ 5652*4c06356bSdh int 5653*4c06356bSdh pmcs_kill_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 5654*4c06356bSdh { 5655*4c06356bSdh int r, result; 5656*4c06356bSdh uint32_t msg[PMCS_MSG_SIZE], *ptr, status; 5657*4c06356bSdh struct pmcwork *pwrk; 5658*4c06356bSdh pmcs_xscsi_t *tgt; 5659*4c06356bSdh 5660*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "kill %s device @ %s", 5661*4c06356bSdh pmcs_get_typename(pptr->dtype), pptr->path); 5662*4c06356bSdh 5663*4c06356bSdh /* 5664*4c06356bSdh * There may be an outstanding ABORT_ALL running, which we wouldn't 5665*4c06356bSdh * know just by checking abort_pending. We can, however, check 5666*4c06356bSdh * abort_all_start. If it's non-zero, there is one, and we'll just 5667*4c06356bSdh * sit here and wait for it to complete. If we don't, we'll remove 5668*4c06356bSdh * the device while there are still commands pending. 5669*4c06356bSdh */ 5670*4c06356bSdh if (pptr->abort_all_start) { 5671*4c06356bSdh while (pptr->abort_all_start) { 5672*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 5673*4c06356bSdh "%s: Waiting for outstanding ABORT_ALL on PHY 0x%p", 5674*4c06356bSdh __func__, (void *)pptr); 5675*4c06356bSdh cv_wait(&pptr->abort_all_cv, &pptr->phy_lock); 5676*4c06356bSdh } 5677*4c06356bSdh } else if (pptr->abort_pending) { 5678*4c06356bSdh r = pmcs_abort(pwp, pptr, pptr->device_id, 1, 1); 5679*4c06356bSdh 5680*4c06356bSdh if (r) { 5681*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 5682*4c06356bSdh "%s: ABORT_ALL returned non-zero status (%d) for " 5683*4c06356bSdh "PHY 0x%p", __func__, r, (void *)pptr); 5684*4c06356bSdh return (r); 5685*4c06356bSdh } 5686*4c06356bSdh pptr->abort_pending = 0; 5687*4c06356bSdh } 5688*4c06356bSdh 5689*4c06356bSdh /* 5690*4c06356bSdh * Now that everything is aborted from the chip's perspective (or even 5691*4c06356bSdh * if it is not), flush out the wait queue. We won't flush the active 5692*4c06356bSdh * queue since it is possible that abort completions may follow after 5693*4c06356bSdh * the notification that the abort all has completed. 5694*4c06356bSdh */ 5695*4c06356bSdh tgt = pptr->target; 5696*4c06356bSdh if (tgt) { 5697*4c06356bSdh mutex_enter(&tgt->statlock); 5698*4c06356bSdh pmcs_flush_target_queues(pwp, tgt, PMCS_TGT_WAIT_QUEUE); 5699*4c06356bSdh mutex_exit(&tgt->statlock); 5700*4c06356bSdh } 5701*4c06356bSdh 5702*4c06356bSdh if (pptr->valid_device_id == 0) { 5703*4c06356bSdh return (0); 5704*4c06356bSdh } 5705*4c06356bSdh 5706*4c06356bSdh if ((pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr)) == NULL) { 5707*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__); 5708*4c06356bSdh return (ENOMEM); 5709*4c06356bSdh } 5710*4c06356bSdh pwrk->arg = msg; 5711*4c06356bSdh pwrk->dtype = pptr->dtype; 5712*4c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 5713*4c06356bSdh PMCIN_DEREGISTER_DEVICE_HANDLE)); 5714*4c06356bSdh msg[1] = LE_32(pwrk->htag); 5715*4c06356bSdh msg[2] = LE_32(pptr->device_id); 5716*4c06356bSdh 5717*4c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 5718*4c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 5719*4c06356bSdh if (ptr == NULL) { 5720*4c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 5721*4c06356bSdh mutex_exit(&pwrk->lock); 5722*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__); 5723*4c06356bSdh return (ENOMEM); 5724*4c06356bSdh } 5725*4c06356bSdh 5726*4c06356bSdh COPY_MESSAGE(ptr, msg, 3); 5727*4c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 5728*4c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 5729*4c06356bSdh 5730*4c06356bSdh pmcs_unlock_phy(pptr); 5731*4c06356bSdh WAIT_FOR(pwrk, 250, result); 5732*4c06356bSdh pmcs_lock_phy(pptr); 5733*4c06356bSdh pmcs_pwork(pwp, pwrk); 5734*4c06356bSdh 5735*4c06356bSdh if (result) { 5736*4c06356bSdh return (ETIMEDOUT); 5737*4c06356bSdh } 5738*4c06356bSdh status = LE_32(msg[2]); 5739*4c06356bSdh if (status != PMCOUT_STATUS_OK) { 5740*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 5741*4c06356bSdh "%s: status 0x%x when trying to deregister device %s", 5742*4c06356bSdh __func__, status, pptr->path); 5743*4c06356bSdh } 5744*4c06356bSdh 5745*4c06356bSdh pptr->device_id = PMCS_INVALID_DEVICE_ID; 5746*4c06356bSdh PHY_CHANGED(pwp, pptr); 5747*4c06356bSdh RESTART_DISCOVERY(pwp); 5748*4c06356bSdh pptr->valid_device_id = 0; 5749*4c06356bSdh return (0); 5750*4c06356bSdh } 5751*4c06356bSdh 5752*4c06356bSdh /* 5753*4c06356bSdh * Acknowledge the SAS h/w events that need acknowledgement. 5754*4c06356bSdh * This is only needed for first level PHYs. 5755*4c06356bSdh */ 5756*4c06356bSdh void 5757*4c06356bSdh pmcs_ack_events(pmcs_hw_t *pwp) 5758*4c06356bSdh { 5759*4c06356bSdh uint32_t msg[PMCS_MSG_SIZE], *ptr; 5760*4c06356bSdh struct pmcwork *pwrk; 5761*4c06356bSdh pmcs_phy_t *pptr; 5762*4c06356bSdh 5763*4c06356bSdh for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) { 5764*4c06356bSdh pmcs_lock_phy(pptr); 5765*4c06356bSdh if (pptr->hw_event_ack == 0) { 5766*4c06356bSdh pmcs_unlock_phy(pptr); 5767*4c06356bSdh continue; 5768*4c06356bSdh } 5769*4c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 5770*4c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 5771*4c06356bSdh 5772*4c06356bSdh if ((ptr == NULL) || (pwrk = 5773*4c06356bSdh pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) { 5774*4c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 5775*4c06356bSdh pmcs_unlock_phy(pptr); 5776*4c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_SAS_HW_ACK); 5777*4c06356bSdh break; 5778*4c06356bSdh } 5779*4c06356bSdh 5780*4c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 5781*4c06356bSdh PMCIN_SAW_HW_EVENT_ACK)); 5782*4c06356bSdh msg[1] = LE_32(pwrk->htag); 5783*4c06356bSdh msg[2] = LE_32(pptr->hw_event_ack); 5784*4c06356bSdh 5785*4c06356bSdh mutex_exit(&pwrk->lock); 5786*4c06356bSdh pwrk->dtype = pptr->dtype; 5787*4c06356bSdh pptr->hw_event_ack = 0; 5788*4c06356bSdh COPY_MESSAGE(ptr, msg, 3); 5789*4c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 5790*4c06356bSdh pmcs_unlock_phy(pptr); 5791*4c06356bSdh } 5792*4c06356bSdh } 5793*4c06356bSdh 5794*4c06356bSdh /* 5795*4c06356bSdh * Load DMA 5796*4c06356bSdh */ 5797*4c06356bSdh int 5798*4c06356bSdh pmcs_dma_load(pmcs_hw_t *pwp, pmcs_cmd_t *sp, uint32_t *msg) 5799*4c06356bSdh { 5800*4c06356bSdh ddi_dma_cookie_t *sg; 5801*4c06356bSdh pmcs_dmachunk_t *tc; 5802*4c06356bSdh pmcs_dmasgl_t *sgl, *prior; 5803*4c06356bSdh int seg, tsc; 5804*4c06356bSdh uint64_t sgl_addr; 5805*4c06356bSdh 5806*4c06356bSdh /* 5807*4c06356bSdh * If we have no data segments, we're done. 5808*4c06356bSdh */ 5809*4c06356bSdh if (CMD2PKT(sp)->pkt_numcookies == 0) { 5810*4c06356bSdh return (0); 5811*4c06356bSdh } 5812*4c06356bSdh 5813*4c06356bSdh /* 5814*4c06356bSdh * Get the S/G list pointer. 5815*4c06356bSdh */ 5816*4c06356bSdh sg = CMD2PKT(sp)->pkt_cookies; 5817*4c06356bSdh 5818*4c06356bSdh /* 5819*4c06356bSdh * If we only have one dma segment, we can directly address that 5820*4c06356bSdh * data within the Inbound message itself. 5821*4c06356bSdh */ 5822*4c06356bSdh if (CMD2PKT(sp)->pkt_numcookies == 1) { 5823*4c06356bSdh msg[12] = LE_32(DWORD0(sg->dmac_laddress)); 5824*4c06356bSdh msg[13] = LE_32(DWORD1(sg->dmac_laddress)); 5825*4c06356bSdh msg[14] = LE_32(sg->dmac_size); 5826*4c06356bSdh msg[15] = 0; 5827*4c06356bSdh return (0); 5828*4c06356bSdh } 5829*4c06356bSdh 5830*4c06356bSdh /* 5831*4c06356bSdh * Otherwise, we'll need one or more external S/G list chunks. 5832*4c06356bSdh * Get the first one and its dma address into the Inbound message. 5833*4c06356bSdh */ 5834*4c06356bSdh mutex_enter(&pwp->dma_lock); 5835*4c06356bSdh tc = pwp->dma_freelist; 5836*4c06356bSdh if (tc == NULL) { 5837*4c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS); 5838*4c06356bSdh mutex_exit(&pwp->dma_lock); 5839*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, "%s: out of SG lists", __func__); 5840*4c06356bSdh return (-1); 5841*4c06356bSdh } 5842*4c06356bSdh pwp->dma_freelist = tc->nxt; 5843*4c06356bSdh mutex_exit(&pwp->dma_lock); 5844*4c06356bSdh 5845*4c06356bSdh tc->nxt = NULL; 5846*4c06356bSdh sp->cmd_clist = tc; 5847*4c06356bSdh sgl = tc->chunks; 5848*4c06356bSdh (void) memset(tc->chunks, 0, PMCS_SGL_CHUNKSZ); 5849*4c06356bSdh sgl_addr = tc->addr; 5850*4c06356bSdh msg[12] = LE_32(DWORD0(sgl_addr)); 5851*4c06356bSdh msg[13] = LE_32(DWORD1(sgl_addr)); 5852*4c06356bSdh msg[14] = 0; 5853*4c06356bSdh msg[15] = LE_32(PMCS_DMASGL_EXTENSION); 5854*4c06356bSdh 5855*4c06356bSdh prior = sgl; 5856*4c06356bSdh tsc = 0; 5857*4c06356bSdh 5858*4c06356bSdh for (seg = 0; seg < CMD2PKT(sp)->pkt_numcookies; seg++) { 5859*4c06356bSdh /* 5860*4c06356bSdh * If the current segment count for this chunk is one less than 5861*4c06356bSdh * the number s/g lists per chunk and we have more than one seg 5862*4c06356bSdh * to go, we need another chunk. Get it, and make sure that the 5863*4c06356bSdh * tail end of the the previous chunk points the new chunk 5864*4c06356bSdh * (if remembering an offset can be called 'pointing to'). 5865*4c06356bSdh * 5866*4c06356bSdh * Note that we can store the offset into our command area that 5867*4c06356bSdh * represents the new chunk in the length field of the part 5868*4c06356bSdh * that points the PMC chip at the next chunk- the PMC chip 5869*4c06356bSdh * ignores this field when the EXTENSION bit is set. 5870*4c06356bSdh * 5871*4c06356bSdh * This is required for dma unloads later. 5872*4c06356bSdh */ 5873*4c06356bSdh if (tsc == (PMCS_SGL_NCHUNKS - 1) && 5874*4c06356bSdh seg < (CMD2PKT(sp)->pkt_numcookies - 1)) { 5875*4c06356bSdh mutex_enter(&pwp->dma_lock); 5876*4c06356bSdh tc = pwp->dma_freelist; 5877*4c06356bSdh if (tc == NULL) { 5878*4c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS); 5879*4c06356bSdh mutex_exit(&pwp->dma_lock); 5880*4c06356bSdh pmcs_dma_unload(pwp, sp); 5881*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, 5882*4c06356bSdh "%s: out of SG lists", __func__); 5883*4c06356bSdh return (-1); 5884*4c06356bSdh } 5885*4c06356bSdh pwp->dma_freelist = tc->nxt; 5886*4c06356bSdh tc->nxt = sp->cmd_clist; 5887*4c06356bSdh mutex_exit(&pwp->dma_lock); 5888*4c06356bSdh 5889*4c06356bSdh sp->cmd_clist = tc; 5890*4c06356bSdh (void) memset(tc->chunks, 0, PMCS_SGL_CHUNKSZ); 5891*4c06356bSdh sgl = tc->chunks; 5892*4c06356bSdh sgl_addr = tc->addr; 5893*4c06356bSdh prior[PMCS_SGL_NCHUNKS-1].sglal = 5894*4c06356bSdh LE_32(DWORD0(sgl_addr)); 5895*4c06356bSdh prior[PMCS_SGL_NCHUNKS-1].sglah = 5896*4c06356bSdh LE_32(DWORD1(sgl_addr)); 5897*4c06356bSdh prior[PMCS_SGL_NCHUNKS-1].sglen = 0; 5898*4c06356bSdh prior[PMCS_SGL_NCHUNKS-1].flags = 5899*4c06356bSdh LE_32(PMCS_DMASGL_EXTENSION); 5900*4c06356bSdh prior = sgl; 5901*4c06356bSdh tsc = 0; 5902*4c06356bSdh } 5903*4c06356bSdh sgl[tsc].sglal = LE_32(DWORD0(sg->dmac_laddress)); 5904*4c06356bSdh sgl[tsc].sglah = LE_32(DWORD1(sg->dmac_laddress)); 5905*4c06356bSdh sgl[tsc].sglen = LE_32(sg->dmac_size); 5906*4c06356bSdh sgl[tsc++].flags = 0; 5907*4c06356bSdh sg++; 5908*4c06356bSdh } 5909*4c06356bSdh return (0); 5910*4c06356bSdh } 5911*4c06356bSdh 5912*4c06356bSdh /* 5913*4c06356bSdh * Unload DMA 5914*4c06356bSdh */ 5915*4c06356bSdh void 5916*4c06356bSdh pmcs_dma_unload(pmcs_hw_t *pwp, pmcs_cmd_t *sp) 5917*4c06356bSdh { 5918*4c06356bSdh pmcs_dmachunk_t *cp; 5919*4c06356bSdh 5920*4c06356bSdh mutex_enter(&pwp->dma_lock); 5921*4c06356bSdh while ((cp = sp->cmd_clist) != NULL) { 5922*4c06356bSdh sp->cmd_clist = cp->nxt; 5923*4c06356bSdh cp->nxt = pwp->dma_freelist; 5924*4c06356bSdh pwp->dma_freelist = cp; 5925*4c06356bSdh } 5926*4c06356bSdh mutex_exit(&pwp->dma_lock); 5927*4c06356bSdh } 5928*4c06356bSdh 5929*4c06356bSdh /* 5930*4c06356bSdh * Take a chunk of consistent memory that has just been allocated and inserted 5931*4c06356bSdh * into the cip indices and prepare it for DMA chunk usage and add it to the 5932*4c06356bSdh * freelist. 5933*4c06356bSdh * 5934*4c06356bSdh * Called with dma_lock locked (except during attach when it's unnecessary) 5935*4c06356bSdh */ 5936*4c06356bSdh void 5937*4c06356bSdh pmcs_idma_chunks(pmcs_hw_t *pwp, pmcs_dmachunk_t *dcp, 5938*4c06356bSdh pmcs_chunk_t *pchunk, unsigned long lim) 5939*4c06356bSdh { 5940*4c06356bSdh unsigned long off, n; 5941*4c06356bSdh pmcs_dmachunk_t *np = dcp; 5942*4c06356bSdh pmcs_chunk_t *tmp_chunk; 5943*4c06356bSdh 5944*4c06356bSdh if (pwp->dma_chunklist == NULL) { 5945*4c06356bSdh pwp->dma_chunklist = pchunk; 5946*4c06356bSdh } else { 5947*4c06356bSdh tmp_chunk = pwp->dma_chunklist; 5948*4c06356bSdh while (tmp_chunk->next) { 5949*4c06356bSdh tmp_chunk = tmp_chunk->next; 5950*4c06356bSdh } 5951*4c06356bSdh tmp_chunk->next = pchunk; 5952*4c06356bSdh } 5953*4c06356bSdh 5954*4c06356bSdh /* 5955*4c06356bSdh * Install offsets into chunk lists. 5956*4c06356bSdh */ 5957*4c06356bSdh for (n = 0, off = 0; off < lim; off += PMCS_SGL_CHUNKSZ, n++) { 5958*4c06356bSdh np->chunks = (void *)&pchunk->addrp[off]; 5959*4c06356bSdh np->addr = pchunk->dma_addr + off; 5960*4c06356bSdh np->acc_handle = pchunk->acc_handle; 5961*4c06356bSdh np->dma_handle = pchunk->dma_handle; 5962*4c06356bSdh if ((off + PMCS_SGL_CHUNKSZ) < lim) { 5963*4c06356bSdh np = np->nxt; 5964*4c06356bSdh } 5965*4c06356bSdh } 5966*4c06356bSdh np->nxt = pwp->dma_freelist; 5967*4c06356bSdh pwp->dma_freelist = dcp; 5968*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, 5969*4c06356bSdh "added %lu DMA chunks ", n); 5970*4c06356bSdh } 5971*4c06356bSdh 5972*4c06356bSdh /* 5973*4c06356bSdh * Change the value of the interrupt coalescing timer. This is done currently 5974*4c06356bSdh * only for I/O completions. If we're using the "auto clear" feature, it can 5975*4c06356bSdh * be turned back on when interrupt coalescing is turned off and must be 5976*4c06356bSdh * turned off when the coalescing timer is on. 5977*4c06356bSdh * NOTE: PMCS_MSIX_GENERAL and PMCS_OQ_IODONE are the same value. As long 5978*4c06356bSdh * as that's true, we don't need to distinguish between them. 5979*4c06356bSdh */ 5980*4c06356bSdh 5981*4c06356bSdh void 5982*4c06356bSdh pmcs_set_intr_coal_timer(pmcs_hw_t *pwp, pmcs_coal_timer_adj_t adj) 5983*4c06356bSdh { 5984*4c06356bSdh if (adj == DECREASE_TIMER) { 5985*4c06356bSdh /* If the timer is already off, nothing to do. */ 5986*4c06356bSdh if (pwp->io_intr_coal.timer_on == B_FALSE) { 5987*4c06356bSdh return; 5988*4c06356bSdh } 5989*4c06356bSdh 5990*4c06356bSdh pwp->io_intr_coal.intr_coal_timer -= PMCS_COAL_TIMER_GRAN; 5991*4c06356bSdh 5992*4c06356bSdh if (pwp->io_intr_coal.intr_coal_timer == 0) { 5993*4c06356bSdh /* Disable the timer */ 5994*4c06356bSdh pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_CONTROL, 0); 5995*4c06356bSdh 5996*4c06356bSdh if (pwp->odb_auto_clear & (1 << PMCS_MSIX_IODONE)) { 5997*4c06356bSdh pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR, 5998*4c06356bSdh pwp->odb_auto_clear); 5999*4c06356bSdh } 6000*4c06356bSdh 6001*4c06356bSdh pwp->io_intr_coal.timer_on = B_FALSE; 6002*4c06356bSdh pwp->io_intr_coal.max_io_completions = B_FALSE; 6003*4c06356bSdh pwp->io_intr_coal.num_intrs = 0; 6004*4c06356bSdh pwp->io_intr_coal.int_cleared = B_FALSE; 6005*4c06356bSdh pwp->io_intr_coal.num_io_completions = 0; 6006*4c06356bSdh 6007*4c06356bSdh DTRACE_PROBE1(pmcs__intr__coalesce__timer__off, 6008*4c06356bSdh pmcs_io_intr_coal_t *, &pwp->io_intr_coal); 6009*4c06356bSdh } else { 6010*4c06356bSdh pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_TIMER, 6011*4c06356bSdh pwp->io_intr_coal.intr_coal_timer); 6012*4c06356bSdh } 6013*4c06356bSdh } else { 6014*4c06356bSdh /* 6015*4c06356bSdh * If the timer isn't on yet, do the setup for it now. 6016*4c06356bSdh */ 6017*4c06356bSdh if (pwp->io_intr_coal.timer_on == B_FALSE) { 6018*4c06356bSdh /* If auto clear is being used, turn it off. */ 6019*4c06356bSdh if (pwp->odb_auto_clear & (1 << PMCS_MSIX_IODONE)) { 6020*4c06356bSdh pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR, 6021*4c06356bSdh (pwp->odb_auto_clear & 6022*4c06356bSdh ~(1 << PMCS_MSIX_IODONE))); 6023*4c06356bSdh } 6024*4c06356bSdh 6025*4c06356bSdh pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_CONTROL, 6026*4c06356bSdh (1 << PMCS_MSIX_IODONE)); 6027*4c06356bSdh pwp->io_intr_coal.timer_on = B_TRUE; 6028*4c06356bSdh pwp->io_intr_coal.intr_coal_timer = 6029*4c06356bSdh PMCS_COAL_TIMER_GRAN; 6030*4c06356bSdh 6031*4c06356bSdh DTRACE_PROBE1(pmcs__intr__coalesce__timer__on, 6032*4c06356bSdh pmcs_io_intr_coal_t *, &pwp->io_intr_coal); 6033*4c06356bSdh } else { 6034*4c06356bSdh pwp->io_intr_coal.intr_coal_timer += 6035*4c06356bSdh PMCS_COAL_TIMER_GRAN; 6036*4c06356bSdh } 6037*4c06356bSdh 6038*4c06356bSdh if (pwp->io_intr_coal.intr_coal_timer > PMCS_MAX_COAL_TIMER) { 6039*4c06356bSdh pwp->io_intr_coal.intr_coal_timer = PMCS_MAX_COAL_TIMER; 6040*4c06356bSdh } 6041*4c06356bSdh 6042*4c06356bSdh pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_TIMER, 6043*4c06356bSdh pwp->io_intr_coal.intr_coal_timer); 6044*4c06356bSdh } 6045*4c06356bSdh 6046*4c06356bSdh /* 6047*4c06356bSdh * Adjust the interrupt threshold based on the current timer value 6048*4c06356bSdh */ 6049*4c06356bSdh pwp->io_intr_coal.intr_threshold = 6050*4c06356bSdh PMCS_INTR_THRESHOLD(PMCS_QUANTUM_TIME_USECS * 1000 / 6051*4c06356bSdh (pwp->io_intr_coal.intr_latency + 6052*4c06356bSdh (pwp->io_intr_coal.intr_coal_timer * 1000))); 6053*4c06356bSdh } 6054*4c06356bSdh 6055*4c06356bSdh /* 6056*4c06356bSdh * Register Access functions 6057*4c06356bSdh */ 6058*4c06356bSdh uint32_t 6059*4c06356bSdh pmcs_rd_iqci(pmcs_hw_t *pwp, uint32_t qnum) 6060*4c06356bSdh { 6061*4c06356bSdh uint32_t iqci; 6062*4c06356bSdh 6063*4c06356bSdh if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORKERNEL) != 6064*4c06356bSdh DDI_SUCCESS) { 6065*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: ddi_dma_sync failed?", 6066*4c06356bSdh __func__); 6067*4c06356bSdh } 6068*4c06356bSdh 6069*4c06356bSdh iqci = LE_32( 6070*4c06356bSdh ((uint32_t *)((void *)pwp->cip))[IQ_OFFSET(qnum) >> 2]); 6071*4c06356bSdh 6072*4c06356bSdh return (iqci); 6073*4c06356bSdh } 6074*4c06356bSdh 6075*4c06356bSdh uint32_t 6076*4c06356bSdh pmcs_rd_oqpi(pmcs_hw_t *pwp, uint32_t qnum) 6077*4c06356bSdh { 6078*4c06356bSdh uint32_t oqpi; 6079*4c06356bSdh 6080*4c06356bSdh if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORKERNEL) != 6081*4c06356bSdh DDI_SUCCESS) { 6082*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: ddi_dma_sync failed?", 6083*4c06356bSdh __func__); 6084*4c06356bSdh } 6085*4c06356bSdh 6086*4c06356bSdh oqpi = LE_32( 6087*4c06356bSdh ((uint32_t *)((void *)pwp->cip))[OQ_OFFSET(qnum) >> 2]); 6088*4c06356bSdh 6089*4c06356bSdh return (oqpi); 6090*4c06356bSdh } 6091*4c06356bSdh 6092*4c06356bSdh uint32_t 6093*4c06356bSdh pmcs_rd_gsm_reg(pmcs_hw_t *pwp, uint32_t off) 6094*4c06356bSdh { 6095*4c06356bSdh uint32_t rv, newaxil, oldaxil; 6096*4c06356bSdh 6097*4c06356bSdh newaxil = off & ~GSM_BASE_MASK; 6098*4c06356bSdh off &= GSM_BASE_MASK; 6099*4c06356bSdh mutex_enter(&pwp->axil_lock); 6100*4c06356bSdh oldaxil = ddi_get32(pwp->top_acc_handle, 6101*4c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2]); 6102*4c06356bSdh ddi_put32(pwp->top_acc_handle, 6103*4c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2], newaxil); 6104*4c06356bSdh drv_usecwait(10); 6105*4c06356bSdh if (ddi_get32(pwp->top_acc_handle, 6106*4c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != newaxil) { 6107*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "AXIL register update failed"); 6108*4c06356bSdh } 6109*4c06356bSdh rv = ddi_get32(pwp->gsm_acc_handle, &pwp->gsm_regs[off >> 2]); 6110*4c06356bSdh ddi_put32(pwp->top_acc_handle, 6111*4c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2], oldaxil); 6112*4c06356bSdh drv_usecwait(10); 6113*4c06356bSdh if (ddi_get32(pwp->top_acc_handle, 6114*4c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != oldaxil) { 6115*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "AXIL register restore failed"); 6116*4c06356bSdh } 6117*4c06356bSdh mutex_exit(&pwp->axil_lock); 6118*4c06356bSdh return (rv); 6119*4c06356bSdh } 6120*4c06356bSdh 6121*4c06356bSdh void 6122*4c06356bSdh pmcs_wr_gsm_reg(pmcs_hw_t *pwp, uint32_t off, uint32_t val) 6123*4c06356bSdh { 6124*4c06356bSdh uint32_t newaxil, oldaxil; 6125*4c06356bSdh 6126*4c06356bSdh newaxil = off & ~GSM_BASE_MASK; 6127*4c06356bSdh off &= GSM_BASE_MASK; 6128*4c06356bSdh mutex_enter(&pwp->axil_lock); 6129*4c06356bSdh oldaxil = ddi_get32(pwp->top_acc_handle, 6130*4c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2]); 6131*4c06356bSdh ddi_put32(pwp->top_acc_handle, 6132*4c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2], newaxil); 6133*4c06356bSdh drv_usecwait(10); 6134*4c06356bSdh if (ddi_get32(pwp->top_acc_handle, 6135*4c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != newaxil) { 6136*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "AXIL register update failed"); 6137*4c06356bSdh } 6138*4c06356bSdh ddi_put32(pwp->gsm_acc_handle, &pwp->gsm_regs[off >> 2], val); 6139*4c06356bSdh ddi_put32(pwp->top_acc_handle, 6140*4c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2], oldaxil); 6141*4c06356bSdh drv_usecwait(10); 6142*4c06356bSdh if (ddi_get32(pwp->top_acc_handle, 6143*4c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != oldaxil) { 6144*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "AXIL register restore failed"); 6145*4c06356bSdh } 6146*4c06356bSdh mutex_exit(&pwp->axil_lock); 6147*4c06356bSdh } 6148*4c06356bSdh 6149*4c06356bSdh uint32_t 6150*4c06356bSdh pmcs_rd_topunit(pmcs_hw_t *pwp, uint32_t off) 6151*4c06356bSdh { 6152*4c06356bSdh switch (off) { 6153*4c06356bSdh case PMCS_SPC_RESET: 6154*4c06356bSdh case PMCS_SPC_BOOT_STRAP: 6155*4c06356bSdh case PMCS_SPC_DEVICE_ID: 6156*4c06356bSdh case PMCS_DEVICE_REVISION: 6157*4c06356bSdh off = pmcs_rd_gsm_reg(pwp, off); 6158*4c06356bSdh break; 6159*4c06356bSdh default: 6160*4c06356bSdh off = ddi_get32(pwp->top_acc_handle, 6161*4c06356bSdh &pwp->top_regs[off >> 2]); 6162*4c06356bSdh break; 6163*4c06356bSdh } 6164*4c06356bSdh return (off); 6165*4c06356bSdh } 6166*4c06356bSdh 6167*4c06356bSdh void 6168*4c06356bSdh pmcs_wr_topunit(pmcs_hw_t *pwp, uint32_t off, uint32_t val) 6169*4c06356bSdh { 6170*4c06356bSdh switch (off) { 6171*4c06356bSdh case PMCS_SPC_RESET: 6172*4c06356bSdh case PMCS_DEVICE_REVISION: 6173*4c06356bSdh pmcs_wr_gsm_reg(pwp, off, val); 6174*4c06356bSdh break; 6175*4c06356bSdh default: 6176*4c06356bSdh ddi_put32(pwp->top_acc_handle, &pwp->top_regs[off >> 2], val); 6177*4c06356bSdh break; 6178*4c06356bSdh } 6179*4c06356bSdh } 6180*4c06356bSdh 6181*4c06356bSdh uint32_t 6182*4c06356bSdh pmcs_rd_msgunit(pmcs_hw_t *pwp, uint32_t off) 6183*4c06356bSdh { 6184*4c06356bSdh return (ddi_get32(pwp->msg_acc_handle, &pwp->msg_regs[off >> 2])); 6185*4c06356bSdh } 6186*4c06356bSdh 6187*4c06356bSdh uint32_t 6188*4c06356bSdh pmcs_rd_mpi_tbl(pmcs_hw_t *pwp, uint32_t off) 6189*4c06356bSdh { 6190*4c06356bSdh return (ddi_get32(pwp->mpi_acc_handle, 6191*4c06356bSdh &pwp->mpi_regs[(pwp->mpi_offset + off) >> 2])); 6192*4c06356bSdh } 6193*4c06356bSdh 6194*4c06356bSdh uint32_t 6195*4c06356bSdh pmcs_rd_gst_tbl(pmcs_hw_t *pwp, uint32_t off) 6196*4c06356bSdh { 6197*4c06356bSdh return (ddi_get32(pwp->mpi_acc_handle, 6198*4c06356bSdh &pwp->mpi_regs[(pwp->mpi_gst_offset + off) >> 2])); 6199*4c06356bSdh } 6200*4c06356bSdh 6201*4c06356bSdh uint32_t 6202*4c06356bSdh pmcs_rd_iqc_tbl(pmcs_hw_t *pwp, uint32_t off) 6203*4c06356bSdh { 6204*4c06356bSdh return (ddi_get32(pwp->mpi_acc_handle, 6205*4c06356bSdh &pwp->mpi_regs[(pwp->mpi_iqc_offset + off) >> 2])); 6206*4c06356bSdh } 6207*4c06356bSdh 6208*4c06356bSdh uint32_t 6209*4c06356bSdh pmcs_rd_oqc_tbl(pmcs_hw_t *pwp, uint32_t off) 6210*4c06356bSdh { 6211*4c06356bSdh return (ddi_get32(pwp->mpi_acc_handle, 6212*4c06356bSdh &pwp->mpi_regs[(pwp->mpi_oqc_offset + off) >> 2])); 6213*4c06356bSdh } 6214*4c06356bSdh 6215*4c06356bSdh uint32_t 6216*4c06356bSdh pmcs_rd_iqpi(pmcs_hw_t *pwp, uint32_t qnum) 6217*4c06356bSdh { 6218*4c06356bSdh return (ddi_get32(pwp->mpi_acc_handle, 6219*4c06356bSdh &pwp->mpi_regs[pwp->iqpi_offset[qnum] >> 2])); 6220*4c06356bSdh } 6221*4c06356bSdh 6222*4c06356bSdh uint32_t 6223*4c06356bSdh pmcs_rd_oqci(pmcs_hw_t *pwp, uint32_t qnum) 6224*4c06356bSdh { 6225*4c06356bSdh return (ddi_get32(pwp->mpi_acc_handle, 6226*4c06356bSdh &pwp->mpi_regs[pwp->oqci_offset[qnum] >> 2])); 6227*4c06356bSdh } 6228*4c06356bSdh 6229*4c06356bSdh void 6230*4c06356bSdh pmcs_wr_msgunit(pmcs_hw_t *pwp, uint32_t off, uint32_t val) 6231*4c06356bSdh { 6232*4c06356bSdh ddi_put32(pwp->msg_acc_handle, &pwp->msg_regs[off >> 2], val); 6233*4c06356bSdh } 6234*4c06356bSdh 6235*4c06356bSdh void 6236*4c06356bSdh pmcs_wr_mpi_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val) 6237*4c06356bSdh { 6238*4c06356bSdh ddi_put32(pwp->mpi_acc_handle, 6239*4c06356bSdh &pwp->mpi_regs[(pwp->mpi_offset + off) >> 2], (val)); 6240*4c06356bSdh } 6241*4c06356bSdh 6242*4c06356bSdh void 6243*4c06356bSdh pmcs_wr_gst_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val) 6244*4c06356bSdh { 6245*4c06356bSdh ddi_put32(pwp->mpi_acc_handle, 6246*4c06356bSdh &pwp->mpi_regs[(pwp->mpi_gst_offset + off) >> 2], val); 6247*4c06356bSdh } 6248*4c06356bSdh 6249*4c06356bSdh void 6250*4c06356bSdh pmcs_wr_iqc_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val) 6251*4c06356bSdh { 6252*4c06356bSdh ddi_put32(pwp->mpi_acc_handle, 6253*4c06356bSdh &pwp->mpi_regs[(pwp->mpi_iqc_offset + off) >> 2], val); 6254*4c06356bSdh } 6255*4c06356bSdh 6256*4c06356bSdh void 6257*4c06356bSdh pmcs_wr_oqc_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val) 6258*4c06356bSdh { 6259*4c06356bSdh ddi_put32(pwp->mpi_acc_handle, 6260*4c06356bSdh &pwp->mpi_regs[(pwp->mpi_oqc_offset + off) >> 2], val); 6261*4c06356bSdh } 6262*4c06356bSdh 6263*4c06356bSdh void 6264*4c06356bSdh pmcs_wr_iqci(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val) 6265*4c06356bSdh { 6266*4c06356bSdh ((uint32_t *)((void *)pwp->cip))[IQ_OFFSET(qnum) >> 2] = val; 6267*4c06356bSdh if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORDEV) != 6268*4c06356bSdh DDI_SUCCESS) { 6269*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: ddi_dma_sync failed?", 6270*4c06356bSdh __func__); 6271*4c06356bSdh } 6272*4c06356bSdh } 6273*4c06356bSdh 6274*4c06356bSdh void 6275*4c06356bSdh pmcs_wr_iqpi(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val) 6276*4c06356bSdh { 6277*4c06356bSdh ddi_put32(pwp->mpi_acc_handle, 6278*4c06356bSdh &pwp->mpi_regs[pwp->iqpi_offset[qnum] >> 2], val); 6279*4c06356bSdh } 6280*4c06356bSdh 6281*4c06356bSdh void 6282*4c06356bSdh pmcs_wr_oqci(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val) 6283*4c06356bSdh { 6284*4c06356bSdh ddi_put32(pwp->mpi_acc_handle, 6285*4c06356bSdh &pwp->mpi_regs[pwp->oqci_offset[qnum] >> 2], val); 6286*4c06356bSdh } 6287*4c06356bSdh 6288*4c06356bSdh void 6289*4c06356bSdh pmcs_wr_oqpi(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val) 6290*4c06356bSdh { 6291*4c06356bSdh ((uint32_t *)((void *)pwp->cip))[OQ_OFFSET(qnum) >> 2] = val; 6292*4c06356bSdh if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORDEV) != 6293*4c06356bSdh DDI_SUCCESS) { 6294*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: ddi_dma_sync failed?", 6295*4c06356bSdh __func__); 6296*4c06356bSdh } 6297*4c06356bSdh } 6298*4c06356bSdh 6299*4c06356bSdh /* 6300*4c06356bSdh * Check the status value of an outbound IOMB and report anything bad 6301*4c06356bSdh */ 6302*4c06356bSdh 6303*4c06356bSdh void 6304*4c06356bSdh pmcs_check_iomb_status(pmcs_hw_t *pwp, uint32_t *iomb) 6305*4c06356bSdh { 6306*4c06356bSdh uint16_t opcode; 6307*4c06356bSdh int offset; 6308*4c06356bSdh 6309*4c06356bSdh if (iomb == NULL) { 6310*4c06356bSdh return; 6311*4c06356bSdh } 6312*4c06356bSdh 6313*4c06356bSdh opcode = LE_32(iomb[0]) & 0xfff; 6314*4c06356bSdh 6315*4c06356bSdh switch (opcode) { 6316*4c06356bSdh /* 6317*4c06356bSdh * The following have no status field, so ignore them 6318*4c06356bSdh */ 6319*4c06356bSdh case PMCOUT_ECHO: 6320*4c06356bSdh case PMCOUT_SAS_HW_EVENT: 6321*4c06356bSdh case PMCOUT_GET_DEVICE_HANDLE: 6322*4c06356bSdh case PMCOUT_SATA_EVENT: 6323*4c06356bSdh case PMCOUT_SSP_EVENT: 6324*4c06356bSdh case PMCOUT_DEVICE_HANDLE_ARRIVED: 6325*4c06356bSdh case PMCOUT_SMP_REQUEST_RECEIVED: 6326*4c06356bSdh case PMCOUT_GPIO: 6327*4c06356bSdh case PMCOUT_GPIO_EVENT: 6328*4c06356bSdh case PMCOUT_GET_TIME_STAMP: 6329*4c06356bSdh case PMCOUT_SKIP_ENTRIES: 6330*4c06356bSdh case PMCOUT_GET_NVMD_DATA: /* Actually lower 16 bits of word 3 */ 6331*4c06356bSdh case PMCOUT_SET_NVMD_DATA: /* but ignore - we don't use these */ 6332*4c06356bSdh case PMCOUT_DEVICE_HANDLE_REMOVED: 6333*4c06356bSdh case PMCOUT_SSP_REQUEST_RECEIVED: 6334*4c06356bSdh return; 6335*4c06356bSdh 6336*4c06356bSdh case PMCOUT_GENERAL_EVENT: 6337*4c06356bSdh offset = 1; 6338*4c06356bSdh break; 6339*4c06356bSdh 6340*4c06356bSdh case PMCOUT_SSP_COMPLETION: 6341*4c06356bSdh case PMCOUT_SMP_COMPLETION: 6342*4c06356bSdh case PMCOUT_DEVICE_REGISTRATION: 6343*4c06356bSdh case PMCOUT_DEREGISTER_DEVICE_HANDLE: 6344*4c06356bSdh case PMCOUT_SATA_COMPLETION: 6345*4c06356bSdh case PMCOUT_DEVICE_INFO: 6346*4c06356bSdh case PMCOUT_FW_FLASH_UPDATE: 6347*4c06356bSdh case PMCOUT_SSP_ABORT: 6348*4c06356bSdh case PMCOUT_SATA_ABORT: 6349*4c06356bSdh case PMCOUT_SAS_DIAG_MODE_START_END: 6350*4c06356bSdh case PMCOUT_SAS_HW_EVENT_ACK_ACK: 6351*4c06356bSdh case PMCOUT_SMP_ABORT: 6352*4c06356bSdh case PMCOUT_SET_DEVICE_STATE: 6353*4c06356bSdh case PMCOUT_GET_DEVICE_STATE: 6354*4c06356bSdh case PMCOUT_SET_DEVICE_INFO: 6355*4c06356bSdh offset = 2; 6356*4c06356bSdh break; 6357*4c06356bSdh 6358*4c06356bSdh case PMCOUT_LOCAL_PHY_CONTROL: 6359*4c06356bSdh case PMCOUT_SAS_DIAG_EXECUTE: 6360*4c06356bSdh case PMCOUT_PORT_CONTROL: 6361*4c06356bSdh offset = 3; 6362*4c06356bSdh break; 6363*4c06356bSdh 6364*4c06356bSdh case PMCOUT_GET_INFO: 6365*4c06356bSdh case PMCOUT_GET_VPD: 6366*4c06356bSdh case PMCOUT_SAS_ASSISTED_DISCOVERY_EVENT: 6367*4c06356bSdh case PMCOUT_SATA_ASSISTED_DISCOVERY_EVENT: 6368*4c06356bSdh case PMCOUT_SET_VPD: 6369*4c06356bSdh case PMCOUT_TWI: 6370*4c06356bSdh pmcs_print_entry(pwp, PMCS_PRT_DEBUG, 6371*4c06356bSdh "Got response for deprecated opcode", iomb); 6372*4c06356bSdh return; 6373*4c06356bSdh 6374*4c06356bSdh default: 6375*4c06356bSdh pmcs_print_entry(pwp, PMCS_PRT_DEBUG, 6376*4c06356bSdh "Got response for unknown opcode", iomb); 6377*4c06356bSdh return; 6378*4c06356bSdh } 6379*4c06356bSdh 6380*4c06356bSdh if (LE_32(iomb[offset]) != PMCOUT_STATUS_OK) { 6381*4c06356bSdh pmcs_print_entry(pwp, PMCS_PRT_DEBUG, 6382*4c06356bSdh "bad status on TAG_TYPE_NONE command", iomb); 6383*4c06356bSdh } 6384*4c06356bSdh } 6385*4c06356bSdh 6386*4c06356bSdh /* 6387*4c06356bSdh * Called with statlock held 6388*4c06356bSdh */ 6389*4c06356bSdh void 6390*4c06356bSdh pmcs_clear_xp(pmcs_hw_t *pwp, pmcs_xscsi_t *xp) 6391*4c06356bSdh { 6392*4c06356bSdh _NOTE(ARGUNUSED(pwp)); 6393*4c06356bSdh 6394*4c06356bSdh ASSERT(mutex_owned(&xp->statlock)); 6395*4c06356bSdh ASSERT(xp->dying); 6396*4c06356bSdh 6397*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Device 0x%p is gone.", __func__, 6398*4c06356bSdh (void *)xp); 6399*4c06356bSdh 6400*4c06356bSdh /* 6401*4c06356bSdh * Clear the dip now. This keeps pmcs_rem_old_devices from attempting 6402*4c06356bSdh * to call us on the same device while we're still flushing queues. 6403*4c06356bSdh * The only side effect is we can no longer update SM-HBA properties, 6404*4c06356bSdh * but this device is going away anyway, so no matter. 6405*4c06356bSdh */ 6406*4c06356bSdh xp->dip = NULL; 6407*4c06356bSdh 6408*4c06356bSdh /* 6409*4c06356bSdh * Flush all target queues 6410*4c06356bSdh */ 6411*4c06356bSdh pmcs_flush_target_queues(pwp, xp, PMCS_TGT_ALL_QUEUES); 6412*4c06356bSdh 6413*4c06356bSdh xp->special_running = 0; 6414*4c06356bSdh xp->recovering = 0; 6415*4c06356bSdh xp->recover_wait = 0; 6416*4c06356bSdh xp->draining = 0; 6417*4c06356bSdh xp->dying = 0; 6418*4c06356bSdh xp->new = 0; 6419*4c06356bSdh xp->assigned = 0; 6420*4c06356bSdh xp->dev_state = 0; 6421*4c06356bSdh xp->tagmap = 0; 6422*4c06356bSdh xp->dev_gone = 1; 6423*4c06356bSdh xp->event_recovery = 0; 6424*4c06356bSdh xp->dtype = NOTHING; 6425*4c06356bSdh xp->wq_recovery_tail = NULL; 6426*4c06356bSdh /* Don't clear xp->phy */ 6427*4c06356bSdh /* Don't clear xp->actv_cnt */ 6428*4c06356bSdh } 6429*4c06356bSdh 6430*4c06356bSdh static int 6431*4c06356bSdh pmcs_smp_function_result(pmcs_hw_t *pwp, smp_response_frame_t *srf) 6432*4c06356bSdh { 6433*4c06356bSdh int result = srf->srf_result; 6434*4c06356bSdh 6435*4c06356bSdh switch (result) { 6436*4c06356bSdh case SMP_RES_UNKNOWN_FUNCTION: 6437*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: SMP DISCOVER Response " 6438*4c06356bSdh "Function Result: Unknown SMP Function(0x%x)", 6439*4c06356bSdh __func__, result); 6440*4c06356bSdh break; 6441*4c06356bSdh case SMP_RES_FUNCTION_FAILED: 6442*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: SMP DISCOVER Response " 6443*4c06356bSdh "Function Result: SMP Function Failed(0x%x)", 6444*4c06356bSdh __func__, result); 6445*4c06356bSdh break; 6446*4c06356bSdh case SMP_RES_INVALID_REQUEST_FRAME_LENGTH: 6447*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: SMP DISCOVER Response " 6448*4c06356bSdh "Function Result: Invalid Request Frame Length(0x%x)", 6449*4c06356bSdh __func__, result); 6450*4c06356bSdh break; 6451*4c06356bSdh case SMP_RES_INCOMPLETE_DESCRIPTOR_LIST: 6452*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: SMP DISCOVER Response " 6453*4c06356bSdh "Function Result: Incomplete Descriptor List(0x%x)", 6454*4c06356bSdh __func__, result); 6455*4c06356bSdh break; 6456*4c06356bSdh case SMP_RES_PHY_DOES_NOT_EXIST: 6457*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: SMP DISCOVER Response " 6458*4c06356bSdh "Function Result: PHY does not exist(0x%x)", 6459*4c06356bSdh __func__, result); 6460*4c06356bSdh break; 6461*4c06356bSdh case SMP_RES_PHY_VACANT: 6462*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: SMP DISCOVER Response " 6463*4c06356bSdh "Function Result: PHY Vacant(0x%x)", 6464*4c06356bSdh __func__, result); 6465*4c06356bSdh break; 6466*4c06356bSdh default: 6467*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: SMP DISCOVER Response " 6468*4c06356bSdh "Function Result: (0x%x)", 6469*4c06356bSdh __func__, result); 6470*4c06356bSdh break; 6471*4c06356bSdh } 6472*4c06356bSdh 6473*4c06356bSdh return (result); 6474*4c06356bSdh } 6475*4c06356bSdh 6476*4c06356bSdh /* 6477*4c06356bSdh * Do all the repetitive stuff necessary to setup for DMA 6478*4c06356bSdh * 6479*4c06356bSdh * pwp: Used for dip 6480*4c06356bSdh * dma_attr: ddi_dma_attr_t to use for the mapping 6481*4c06356bSdh * acch: ddi_acc_handle_t to use for the mapping 6482*4c06356bSdh * dmah: ddi_dma_handle_t to use 6483*4c06356bSdh * length: Amount of memory for mapping 6484*4c06356bSdh * kvp: Pointer filled in with kernel virtual address on successful return 6485*4c06356bSdh * dma_addr: Pointer filled in with DMA address on successful return 6486*4c06356bSdh */ 6487*4c06356bSdh boolean_t 6488*4c06356bSdh pmcs_dma_setup(pmcs_hw_t *pwp, ddi_dma_attr_t *dma_attr, ddi_acc_handle_t *acch, 6489*4c06356bSdh ddi_dma_handle_t *dmah, size_t length, caddr_t *kvp, uint64_t *dma_addr) 6490*4c06356bSdh { 6491*4c06356bSdh dev_info_t *dip = pwp->dip; 6492*4c06356bSdh ddi_dma_cookie_t cookie; 6493*4c06356bSdh size_t real_length; 6494*4c06356bSdh uint_t ddma_flag = DDI_DMA_CONSISTENT; 6495*4c06356bSdh uint_t ddabh_flag = DDI_DMA_CONSISTENT | DDI_DMA_RDWR; 6496*4c06356bSdh uint_t cookie_cnt; 6497*4c06356bSdh ddi_device_acc_attr_t mattr = { 6498*4c06356bSdh DDI_DEVICE_ATTR_V0, 6499*4c06356bSdh DDI_NEVERSWAP_ACC, 6500*4c06356bSdh DDI_STRICTORDER_ACC, 6501*4c06356bSdh DDI_DEFAULT_ACC 6502*4c06356bSdh }; 6503*4c06356bSdh 6504*4c06356bSdh *acch = NULL; 6505*4c06356bSdh *dmah = NULL; 6506*4c06356bSdh 6507*4c06356bSdh if (ddi_dma_alloc_handle(dip, dma_attr, DDI_DMA_SLEEP, NULL, dmah) != 6508*4c06356bSdh DDI_SUCCESS) { 6509*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "Failed to allocate DMA handle"); 6510*4c06356bSdh return (B_FALSE); 6511*4c06356bSdh } 6512*4c06356bSdh 6513*4c06356bSdh if (ddi_dma_mem_alloc(*dmah, length, &mattr, ddma_flag, DDI_DMA_SLEEP, 6514*4c06356bSdh NULL, kvp, &real_length, acch) != DDI_SUCCESS) { 6515*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "Failed to allocate DMA mem"); 6516*4c06356bSdh ddi_dma_free_handle(dmah); 6517*4c06356bSdh *dmah = NULL; 6518*4c06356bSdh return (B_FALSE); 6519*4c06356bSdh } 6520*4c06356bSdh 6521*4c06356bSdh if (ddi_dma_addr_bind_handle(*dmah, NULL, *kvp, real_length, 6522*4c06356bSdh ddabh_flag, DDI_DMA_SLEEP, NULL, &cookie, &cookie_cnt) 6523*4c06356bSdh != DDI_DMA_MAPPED) { 6524*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "Failed to bind DMA"); 6525*4c06356bSdh ddi_dma_free_handle(dmah); 6526*4c06356bSdh ddi_dma_mem_free(acch); 6527*4c06356bSdh *dmah = NULL; 6528*4c06356bSdh *acch = NULL; 6529*4c06356bSdh return (B_FALSE); 6530*4c06356bSdh } 6531*4c06356bSdh 6532*4c06356bSdh if (cookie_cnt != 1) { 6533*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "Multiple cookies"); 6534*4c06356bSdh if (ddi_dma_unbind_handle(*dmah) != DDI_SUCCESS) { 6535*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "Condition failed at " 6536*4c06356bSdh "%s():%d", __func__, __LINE__); 6537*4c06356bSdh } 6538*4c06356bSdh ddi_dma_free_handle(dmah); 6539*4c06356bSdh ddi_dma_mem_free(acch); 6540*4c06356bSdh *dmah = NULL; 6541*4c06356bSdh *acch = NULL; 6542*4c06356bSdh return (B_FALSE); 6543*4c06356bSdh } 6544*4c06356bSdh 6545*4c06356bSdh *dma_addr = cookie.dmac_laddress; 6546*4c06356bSdh 6547*4c06356bSdh return (B_TRUE); 6548*4c06356bSdh } 6549*4c06356bSdh 6550*4c06356bSdh /* 6551*4c06356bSdh * Flush requested queues for a particular target. Called with statlock held 6552*4c06356bSdh */ 6553*4c06356bSdh void 6554*4c06356bSdh pmcs_flush_target_queues(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt, uint8_t queues) 6555*4c06356bSdh { 6556*4c06356bSdh pmcs_cmd_t *sp; 6557*4c06356bSdh pmcwork_t *pwrk; 6558*4c06356bSdh 6559*4c06356bSdh ASSERT(pwp != NULL); 6560*4c06356bSdh ASSERT(tgt != NULL); 6561*4c06356bSdh 6562*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 6563*4c06356bSdh "%s: Flushing queues (%d) for target 0x%p", __func__, 6564*4c06356bSdh queues, (void *)tgt); 6565*4c06356bSdh 6566*4c06356bSdh /* 6567*4c06356bSdh * Commands on the wait queue (or the special queue below) don't have 6568*4c06356bSdh * work structures associated with them. 6569*4c06356bSdh */ 6570*4c06356bSdh if (queues & PMCS_TGT_WAIT_QUEUE) { 6571*4c06356bSdh mutex_enter(&tgt->wqlock); 6572*4c06356bSdh while ((sp = STAILQ_FIRST(&tgt->wq)) != NULL) { 6573*4c06356bSdh STAILQ_REMOVE(&tgt->wq, sp, pmcs_cmd, cmd_next); 6574*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG1, 6575*4c06356bSdh "%s: Removing cmd 0x%p from wq for target 0x%p", 6576*4c06356bSdh __func__, (void *)sp, (void *)tgt); 6577*4c06356bSdh CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE; 6578*4c06356bSdh CMD2PKT(sp)->pkt_state = STATE_GOT_BUS; 6579*4c06356bSdh mutex_exit(&tgt->wqlock); 6580*4c06356bSdh pmcs_dma_unload(pwp, sp); 6581*4c06356bSdh mutex_enter(&pwp->cq_lock); 6582*4c06356bSdh STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 6583*4c06356bSdh mutex_exit(&pwp->cq_lock); 6584*4c06356bSdh mutex_enter(&tgt->wqlock); 6585*4c06356bSdh } 6586*4c06356bSdh mutex_exit(&tgt->wqlock); 6587*4c06356bSdh } 6588*4c06356bSdh 6589*4c06356bSdh /* 6590*4c06356bSdh * Commands on the active queue will have work structures associated 6591*4c06356bSdh * with them. 6592*4c06356bSdh */ 6593*4c06356bSdh if (queues & PMCS_TGT_ACTIVE_QUEUE) { 6594*4c06356bSdh mutex_enter(&tgt->aqlock); 6595*4c06356bSdh while ((sp = STAILQ_FIRST(&tgt->aq)) != NULL) { 6596*4c06356bSdh STAILQ_REMOVE(&tgt->aq, sp, pmcs_cmd, cmd_next); 6597*4c06356bSdh pwrk = pmcs_tag2wp(pwp, sp->cmd_tag); 6598*4c06356bSdh mutex_exit(&tgt->aqlock); 6599*4c06356bSdh mutex_exit(&tgt->statlock); 6600*4c06356bSdh /* 6601*4c06356bSdh * If we found a work structure, mark it as dead 6602*4c06356bSdh * and complete it 6603*4c06356bSdh */ 6604*4c06356bSdh if (pwrk != NULL) { 6605*4c06356bSdh pwrk->dead = 1; 6606*4c06356bSdh CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE; 6607*4c06356bSdh CMD2PKT(sp)->pkt_state = STATE_GOT_BUS; 6608*4c06356bSdh pmcs_complete_work_impl(pwp, pwrk, NULL, 0); 6609*4c06356bSdh } 6610*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG1, 6611*4c06356bSdh "%s: Removing cmd 0x%p from aq for target 0x%p", 6612*4c06356bSdh __func__, (void *)sp, (void *)tgt); 6613*4c06356bSdh pmcs_dma_unload(pwp, sp); 6614*4c06356bSdh mutex_enter(&pwp->cq_lock); 6615*4c06356bSdh STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 6616*4c06356bSdh mutex_exit(&pwp->cq_lock); 6617*4c06356bSdh mutex_enter(&tgt->aqlock); 6618*4c06356bSdh mutex_enter(&tgt->statlock); 6619*4c06356bSdh } 6620*4c06356bSdh mutex_exit(&tgt->aqlock); 6621*4c06356bSdh } 6622*4c06356bSdh 6623*4c06356bSdh if (queues & PMCS_TGT_SPECIAL_QUEUE) { 6624*4c06356bSdh while ((sp = STAILQ_FIRST(&tgt->sq)) != NULL) { 6625*4c06356bSdh STAILQ_REMOVE(&tgt->sq, sp, pmcs_cmd, cmd_next); 6626*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG1, 6627*4c06356bSdh "%s: Removing cmd 0x%p from sq for target 0x%p", 6628*4c06356bSdh __func__, (void *)sp, (void *)tgt); 6629*4c06356bSdh CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE; 6630*4c06356bSdh CMD2PKT(sp)->pkt_state = STATE_GOT_BUS; 6631*4c06356bSdh pmcs_dma_unload(pwp, sp); 6632*4c06356bSdh mutex_enter(&pwp->cq_lock); 6633*4c06356bSdh STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 6634*4c06356bSdh mutex_exit(&pwp->cq_lock); 6635*4c06356bSdh } 6636*4c06356bSdh } 6637*4c06356bSdh } 6638*4c06356bSdh 6639*4c06356bSdh void 6640*4c06356bSdh pmcs_complete_work_impl(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *iomb, 6641*4c06356bSdh size_t amt) 6642*4c06356bSdh { 6643*4c06356bSdh switch (PMCS_TAG_TYPE(pwrk->htag)) { 6644*4c06356bSdh case PMCS_TAG_TYPE_CBACK: 6645*4c06356bSdh { 6646*4c06356bSdh pmcs_cb_t callback = (pmcs_cb_t)pwrk->ptr; 6647*4c06356bSdh (*callback)(pwp, pwrk, iomb); 6648*4c06356bSdh break; 6649*4c06356bSdh } 6650*4c06356bSdh case PMCS_TAG_TYPE_WAIT: 6651*4c06356bSdh if (pwrk->arg && iomb && amt) { 6652*4c06356bSdh (void) memcpy(pwrk->arg, iomb, amt); 6653*4c06356bSdh } 6654*4c06356bSdh cv_signal(&pwrk->sleep_cv); 6655*4c06356bSdh mutex_exit(&pwrk->lock); 6656*4c06356bSdh break; 6657*4c06356bSdh case PMCS_TAG_TYPE_NONE: 6658*4c06356bSdh #ifdef DEBUG 6659*4c06356bSdh pmcs_check_iomb_status(pwp, iomb); 6660*4c06356bSdh #endif 6661*4c06356bSdh pmcs_pwork(pwp, pwrk); 6662*4c06356bSdh break; 6663*4c06356bSdh default: 6664*4c06356bSdh /* 6665*4c06356bSdh * We will leak a structure here if we don't know 6666*4c06356bSdh * what happened 6667*4c06356bSdh */ 6668*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Unknown PMCS_TAG_TYPE (%x)", 6669*4c06356bSdh __func__, PMCS_TAG_TYPE(pwrk->htag)); 6670*4c06356bSdh break; 6671*4c06356bSdh } 6672*4c06356bSdh } 6673*4c06356bSdh 6674*4c06356bSdh /* 6675*4c06356bSdh * Determine if iport still has targets. During detach(9E), if SCSA is 6676*4c06356bSdh * successfull in its guarantee of tran_tgt_free(9E) before detach(9E), 6677*4c06356bSdh * this should always return B_FALSE. 6678*4c06356bSdh */ 6679*4c06356bSdh boolean_t 6680*4c06356bSdh pmcs_iport_has_targets(pmcs_hw_t *pwp, pmcs_iport_t *iport) 6681*4c06356bSdh { 6682*4c06356bSdh pmcs_xscsi_t *xp; 6683*4c06356bSdh int i; 6684*4c06356bSdh 6685*4c06356bSdh mutex_enter(&pwp->lock); 6686*4c06356bSdh 6687*4c06356bSdh if (!pwp->targets || !pwp->max_dev) { 6688*4c06356bSdh mutex_exit(&pwp->lock); 6689*4c06356bSdh return (B_FALSE); 6690*4c06356bSdh } 6691*4c06356bSdh 6692*4c06356bSdh for (i = 0; i < pwp->max_dev; i++) { 6693*4c06356bSdh xp = pwp->targets[i]; 6694*4c06356bSdh if ((xp == NULL) || (xp->phy == NULL) || 6695*4c06356bSdh (xp->phy->iport != iport)) { 6696*4c06356bSdh continue; 6697*4c06356bSdh } 6698*4c06356bSdh 6699*4c06356bSdh mutex_exit(&pwp->lock); 6700*4c06356bSdh return (B_TRUE); 6701*4c06356bSdh } 6702*4c06356bSdh 6703*4c06356bSdh mutex_exit(&pwp->lock); 6704*4c06356bSdh return (B_FALSE); 6705*4c06356bSdh } 6706*4c06356bSdh 6707*4c06356bSdh /* 6708*4c06356bSdh * Called with softstate lock held 6709*4c06356bSdh */ 6710*4c06356bSdh void 6711*4c06356bSdh pmcs_destroy_target(pmcs_xscsi_t *target) 6712*4c06356bSdh { 6713*4c06356bSdh pmcs_hw_t *pwp = target->pwp; 6714*4c06356bSdh pmcs_iport_t *iport; 6715*4c06356bSdh 6716*4c06356bSdh ASSERT(pwp); 6717*4c06356bSdh ASSERT(mutex_owned(&pwp->lock)); 6718*4c06356bSdh 6719*4c06356bSdh if (!target->ua) { 6720*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 6721*4c06356bSdh "%s: target %p iport addres is null", 6722*4c06356bSdh __func__, (void *)target); 6723*4c06356bSdh } 6724*4c06356bSdh 6725*4c06356bSdh iport = pmcs_get_iport_by_ua(pwp, target->ua); 6726*4c06356bSdh if (iport == NULL) { 6727*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 6728*4c06356bSdh "%s: no iport associated with tgt(0x%p)", 6729*4c06356bSdh __func__, (void *)target); 6730*4c06356bSdh return; 6731*4c06356bSdh } 6732*4c06356bSdh 6733*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 6734*4c06356bSdh "%s: free target %p", __func__, (void *)target); 6735*4c06356bSdh if (target->ua) { 6736*4c06356bSdh strfree(target->ua); 6737*4c06356bSdh } 6738*4c06356bSdh 6739*4c06356bSdh mutex_destroy(&target->wqlock); 6740*4c06356bSdh mutex_destroy(&target->aqlock); 6741*4c06356bSdh mutex_destroy(&target->statlock); 6742*4c06356bSdh cv_destroy(&target->reset_cv); 6743*4c06356bSdh cv_destroy(&target->abort_cv); 6744*4c06356bSdh ddi_soft_state_bystr_fini(&target->lun_sstate); 6745*4c06356bSdh ddi_soft_state_bystr_free(iport->tgt_sstate, target->unit_address); 6746*4c06356bSdh pmcs_rele_iport(iport); 6747*4c06356bSdh } 6748*4c06356bSdh 6749*4c06356bSdh /* 6750*4c06356bSdh * Get device state. Called with statlock and PHY lock held. 6751*4c06356bSdh */ 6752*4c06356bSdh int 6753*4c06356bSdh pmcs_get_dev_state(pmcs_hw_t *pwp, pmcs_xscsi_t *xp, uint8_t *ds) 6754*4c06356bSdh { 6755*4c06356bSdh uint32_t htag, *ptr, msg[PMCS_MSG_SIZE]; 6756*4c06356bSdh int result; 6757*4c06356bSdh struct pmcwork *pwrk; 6758*4c06356bSdh pmcs_phy_t *phyp; 6759*4c06356bSdh 6760*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG3, "%s: tgt(0x%p)", __func__, (void *)xp); 6761*4c06356bSdh if (xp == NULL) { 6762*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Target is NULL", __func__); 6763*4c06356bSdh return (-1); 6764*4c06356bSdh } 6765*4c06356bSdh 6766*4c06356bSdh ASSERT(mutex_owned(&xp->statlock)); 6767*4c06356bSdh phyp = xp->phy; 6768*4c06356bSdh ASSERT(mutex_owned(&phyp->phy_lock)); 6769*4c06356bSdh 6770*4c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, phyp); 6771*4c06356bSdh if (pwrk == NULL) { 6772*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__); 6773*4c06356bSdh return (-1); 6774*4c06356bSdh } 6775*4c06356bSdh pwrk->arg = msg; 6776*4c06356bSdh pwrk->dtype = phyp->dtype; 6777*4c06356bSdh 6778*4c06356bSdh if (phyp->valid_device_id == 0) { 6779*4c06356bSdh pmcs_pwork(pwp, pwrk); 6780*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Invalid DeviceID", __func__); 6781*4c06356bSdh return (-1); 6782*4c06356bSdh } 6783*4c06356bSdh htag = pwrk->htag; 6784*4c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 6785*4c06356bSdh PMCIN_GET_DEVICE_STATE)); 6786*4c06356bSdh msg[1] = LE_32(pwrk->htag); 6787*4c06356bSdh msg[2] = LE_32(phyp->device_id); 6788*4c06356bSdh 6789*4c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 6790*4c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 6791*4c06356bSdh if (ptr == NULL) { 6792*4c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 6793*4c06356bSdh pmcs_pwork(pwp, pwrk); 6794*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__); 6795*4c06356bSdh return (-1); 6796*4c06356bSdh } 6797*4c06356bSdh COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE); 6798*4c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 6799*4c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 6800*4c06356bSdh mutex_exit(&xp->statlock); 6801*4c06356bSdh pmcs_unlock_phy(phyp); 6802*4c06356bSdh WAIT_FOR(pwrk, 1000, result); 6803*4c06356bSdh pmcs_lock_phy(phyp); 6804*4c06356bSdh pmcs_pwork(pwp, pwrk); 6805*4c06356bSdh mutex_enter(&xp->statlock); 6806*4c06356bSdh 6807*4c06356bSdh if (result) { 6808*4c06356bSdh pmcs_timed_out(pwp, htag, __func__); 6809*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: cmd timed out, returning ", 6810*4c06356bSdh __func__); 6811*4c06356bSdh return (-1); 6812*4c06356bSdh } 6813*4c06356bSdh if (LE_32(msg[2]) == 0) { 6814*4c06356bSdh *ds = (uint8_t)(LE_32(msg[4])); 6815*4c06356bSdh if (*ds != xp->dev_state) { 6816*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, 6817*4c06356bSdh "%s: retrieved_ds=0x%x, target_ds=0x%x", __func__, 6818*4c06356bSdh *ds, xp->dev_state); 6819*4c06356bSdh } 6820*4c06356bSdh return (0); 6821*4c06356bSdh } else { 6822*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, 6823*4c06356bSdh "%s: cmd failed Status(0x%x), returning ", __func__, 6824*4c06356bSdh LE_32(msg[2])); 6825*4c06356bSdh return (-1); 6826*4c06356bSdh } 6827*4c06356bSdh } 6828*4c06356bSdh 6829*4c06356bSdh /* 6830*4c06356bSdh * Set device state. Called with target's statlock and PHY lock held. 6831*4c06356bSdh */ 6832*4c06356bSdh int 6833*4c06356bSdh pmcs_set_dev_state(pmcs_hw_t *pwp, pmcs_xscsi_t *xp, uint8_t ds) 6834*4c06356bSdh { 6835*4c06356bSdh uint32_t htag, *ptr, msg[PMCS_MSG_SIZE]; 6836*4c06356bSdh int result; 6837*4c06356bSdh uint8_t pds, nds; 6838*4c06356bSdh struct pmcwork *pwrk; 6839*4c06356bSdh pmcs_phy_t *phyp; 6840*4c06356bSdh 6841*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, "%s: ds(0x%x), tgt(0x%p)", 6842*4c06356bSdh __func__, ds, (void *)xp); 6843*4c06356bSdh if (xp == NULL) { 6844*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Target is Null", __func__); 6845*4c06356bSdh return (-1); 6846*4c06356bSdh } 6847*4c06356bSdh 6848*4c06356bSdh phyp = xp->phy; 6849*4c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, phyp); 6850*4c06356bSdh if (pwrk == NULL) { 6851*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__); 6852*4c06356bSdh return (-1); 6853*4c06356bSdh } 6854*4c06356bSdh if (phyp == NULL) { 6855*4c06356bSdh pmcs_pwork(pwp, pwrk); 6856*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, "%s: PHY is Null", 6857*4c06356bSdh __func__); 6858*4c06356bSdh return (-1); 6859*4c06356bSdh } 6860*4c06356bSdh if (phyp->valid_device_id == 0) { 6861*4c06356bSdh pmcs_pwork(pwp, pwrk); 6862*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, 6863*4c06356bSdh "%s: Invalid DeviceID", __func__); 6864*4c06356bSdh return (-1); 6865*4c06356bSdh } 6866*4c06356bSdh pwrk->arg = msg; 6867*4c06356bSdh pwrk->dtype = phyp->dtype; 6868*4c06356bSdh htag = pwrk->htag; 6869*4c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 6870*4c06356bSdh PMCIN_SET_DEVICE_STATE)); 6871*4c06356bSdh msg[1] = LE_32(pwrk->htag); 6872*4c06356bSdh msg[2] = LE_32(phyp->device_id); 6873*4c06356bSdh msg[3] = LE_32(ds); 6874*4c06356bSdh 6875*4c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 6876*4c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 6877*4c06356bSdh if (ptr == NULL) { 6878*4c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 6879*4c06356bSdh pmcs_pwork(pwp, pwrk); 6880*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__); 6881*4c06356bSdh return (-1); 6882*4c06356bSdh } 6883*4c06356bSdh COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE); 6884*4c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 6885*4c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 6886*4c06356bSdh 6887*4c06356bSdh mutex_exit(&xp->statlock); 6888*4c06356bSdh pmcs_unlock_phy(phyp); 6889*4c06356bSdh WAIT_FOR(pwrk, 1000, result); 6890*4c06356bSdh pmcs_lock_phy(phyp); 6891*4c06356bSdh pmcs_pwork(pwp, pwrk); 6892*4c06356bSdh mutex_enter(&xp->statlock); 6893*4c06356bSdh 6894*4c06356bSdh if (result) { 6895*4c06356bSdh pmcs_timed_out(pwp, htag, __func__); 6896*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, 6897*4c06356bSdh "%s: cmd timed out, returning", __func__); 6898*4c06356bSdh return (-1); 6899*4c06356bSdh } 6900*4c06356bSdh if (LE_32(msg[2]) == 0) { 6901*4c06356bSdh pds = (uint8_t)(LE_32(msg[4]) >> 4); 6902*4c06356bSdh nds = (uint8_t)(LE_32(msg[4]) & 0x0000000f); 6903*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, "%s: previous_ds=0x%x, " 6904*4c06356bSdh "new_ds=0x%x", __func__, pds, nds); 6905*4c06356bSdh xp->dev_state = nds; 6906*4c06356bSdh return (0); 6907*4c06356bSdh } else { 6908*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, 6909*4c06356bSdh "%s: cmd failed Status(0x%x), returning ", __func__, 6910*4c06356bSdh LE_32(msg[2])); 6911*4c06356bSdh return (-1); 6912*4c06356bSdh } 6913*4c06356bSdh } 6914*4c06356bSdh 6915*4c06356bSdh void 6916*4c06356bSdh pmcs_dev_state_recovery(pmcs_hw_t *pwp, pmcs_phy_t *phyp) 6917*4c06356bSdh { 6918*4c06356bSdh uint8_t ds; 6919*4c06356bSdh int rc; 6920*4c06356bSdh pmcs_xscsi_t *tgt; 6921*4c06356bSdh pmcs_phy_t *pptr, *pnext, *pchild; 6922*4c06356bSdh 6923*4c06356bSdh /* 6924*4c06356bSdh * First time, check to see if we're already performing recovery 6925*4c06356bSdh */ 6926*4c06356bSdh if (phyp == NULL) { 6927*4c06356bSdh mutex_enter(&pwp->lock); 6928*4c06356bSdh if (pwp->ds_err_recovering) { 6929*4c06356bSdh mutex_exit(&pwp->lock); 6930*4c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_DS_ERR_RECOVERY); 6931*4c06356bSdh return; 6932*4c06356bSdh } 6933*4c06356bSdh 6934*4c06356bSdh pwp->ds_err_recovering = 1; 6935*4c06356bSdh pptr = pwp->root_phys; 6936*4c06356bSdh mutex_exit(&pwp->lock); 6937*4c06356bSdh } else { 6938*4c06356bSdh pptr = phyp; 6939*4c06356bSdh } 6940*4c06356bSdh 6941*4c06356bSdh while (pptr) { 6942*4c06356bSdh /* 6943*4c06356bSdh * Since ds_err_recovering is set, we can be assured these 6944*4c06356bSdh * PHYs won't disappear on us while we do this. 6945*4c06356bSdh */ 6946*4c06356bSdh pmcs_lock_phy(pptr); 6947*4c06356bSdh pchild = pptr->children; 6948*4c06356bSdh pnext = pptr->sibling; 6949*4c06356bSdh pmcs_unlock_phy(pptr); 6950*4c06356bSdh 6951*4c06356bSdh if (pchild) { 6952*4c06356bSdh pmcs_dev_state_recovery(pwp, pchild); 6953*4c06356bSdh } 6954*4c06356bSdh 6955*4c06356bSdh tgt = NULL; 6956*4c06356bSdh pmcs_lock_phy(pptr); 6957*4c06356bSdh 6958*4c06356bSdh if (pptr->dead) { 6959*4c06356bSdh goto next_phy; 6960*4c06356bSdh } 6961*4c06356bSdh 6962*4c06356bSdh tgt = pptr->target; 6963*4c06356bSdh if (tgt == NULL) { 6964*4c06356bSdh if (pptr->dtype != NOTHING) { 6965*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, 6966*4c06356bSdh "%s: no target for DS error recovery for " 6967*4c06356bSdh "PHY 0x%p", __func__, (void *)pptr); 6968*4c06356bSdh } 6969*4c06356bSdh goto next_phy; 6970*4c06356bSdh } 6971*4c06356bSdh 6972*4c06356bSdh mutex_enter(&tgt->statlock); 6973*4c06356bSdh 6974*4c06356bSdh if (tgt->recover_wait == 0) { 6975*4c06356bSdh goto next_phy; 6976*4c06356bSdh } 6977*4c06356bSdh 6978*4c06356bSdh if (tgt->dying) { 6979*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, 6980*4c06356bSdh "%s: Not doing DS recovery on dying target %p", 6981*4c06356bSdh __func__, (void *)tgt); 6982*4c06356bSdh goto next_phy; 6983*4c06356bSdh } 6984*4c06356bSdh 6985*4c06356bSdh /* 6986*4c06356bSdh * Step 1: Put the device into the IN_RECOVERY state 6987*4c06356bSdh */ 6988*4c06356bSdh rc = pmcs_get_dev_state(pwp, tgt, &ds); 6989*4c06356bSdh if (rc != 0) { 6990*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 6991*4c06356bSdh "%s: pmcs_get_dev_state on PHY %s " 6992*4c06356bSdh "failed (rc=%d)", 6993*4c06356bSdh __func__, pptr->path, rc); 6994*4c06356bSdh 6995*4c06356bSdh pmcs_handle_ds_recovery_error(pptr, tgt, pwp, 6996*4c06356bSdh __func__, __LINE__, "pmcs_get_dev_state"); 6997*4c06356bSdh 6998*4c06356bSdh goto next_phy; 6999*4c06356bSdh } 7000*4c06356bSdh 7001*4c06356bSdh if (tgt->dev_state == ds) { 7002*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, 7003*4c06356bSdh "%s: Target 0x%p already IN_RECOVERY", __func__, 7004*4c06356bSdh (void *)tgt); 7005*4c06356bSdh } else { 7006*4c06356bSdh tgt->dev_state = ds; 7007*4c06356bSdh ds = PMCS_DEVICE_STATE_IN_RECOVERY; 7008*4c06356bSdh rc = pmcs_send_err_recovery_cmd(pwp, ds, tgt); 7009*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, 7010*4c06356bSdh "%s: pmcs_send_err_recovery_cmd " 7011*4c06356bSdh "result(%d) tgt(0x%p) ds(0x%x) tgt->ds(0x%x)", 7012*4c06356bSdh __func__, rc, (void *)tgt, ds, tgt->dev_state); 7013*4c06356bSdh 7014*4c06356bSdh if (rc) { 7015*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 7016*4c06356bSdh "%s: pmcs_send_err_recovery_cmd to PHY %s " 7017*4c06356bSdh "failed (rc=%d)", 7018*4c06356bSdh __func__, pptr->path, rc); 7019*4c06356bSdh 7020*4c06356bSdh pmcs_handle_ds_recovery_error(pptr, tgt, pwp, 7021*4c06356bSdh __func__, __LINE__, 7022*4c06356bSdh "pmcs_send_err_recovery_cmd"); 7023*4c06356bSdh 7024*4c06356bSdh goto next_phy; 7025*4c06356bSdh } 7026*4c06356bSdh } 7027*4c06356bSdh 7028*4c06356bSdh /* 7029*4c06356bSdh * Step 2: Perform a hard reset on the PHY 7030*4c06356bSdh */ 7031*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, 7032*4c06356bSdh "%s: Issue HARD_RESET to PHY %s", __func__, pptr->path); 7033*4c06356bSdh /* 7034*4c06356bSdh * Must release statlock here because pmcs_reset_phy will 7035*4c06356bSdh * drop and reacquire the PHY lock. 7036*4c06356bSdh */ 7037*4c06356bSdh mutex_exit(&tgt->statlock); 7038*4c06356bSdh rc = pmcs_reset_phy(pwp, pptr, PMCS_PHYOP_HARD_RESET); 7039*4c06356bSdh mutex_enter(&tgt->statlock); 7040*4c06356bSdh if (rc) { 7041*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 7042*4c06356bSdh "%s: HARD_RESET to PHY %s failed (rc=%d)", 7043*4c06356bSdh __func__, pptr->path, rc); 7044*4c06356bSdh 7045*4c06356bSdh pmcs_handle_ds_recovery_error(pptr, tgt, pwp, 7046*4c06356bSdh __func__, __LINE__, "HARD_RESET"); 7047*4c06356bSdh 7048*4c06356bSdh goto next_phy; 7049*4c06356bSdh } 7050*4c06356bSdh 7051*4c06356bSdh /* 7052*4c06356bSdh * Step 3: Abort all I/Os to the device 7053*4c06356bSdh */ 7054*4c06356bSdh if (pptr->abort_all_start) { 7055*4c06356bSdh while (pptr->abort_all_start) { 7056*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 7057*4c06356bSdh "%s: Waiting for outstanding ABORT_ALL on " 7058*4c06356bSdh "PHY 0x%p", __func__, (void *)pptr); 7059*4c06356bSdh cv_wait(&pptr->abort_all_cv, &pptr->phy_lock); 7060*4c06356bSdh } 7061*4c06356bSdh } else { 7062*4c06356bSdh mutex_exit(&tgt->statlock); 7063*4c06356bSdh rc = pmcs_abort(pwp, pptr, pptr->device_id, 1, 1); 7064*4c06356bSdh mutex_enter(&tgt->statlock); 7065*4c06356bSdh if (rc != 0) { 7066*4c06356bSdh pptr->abort_pending = 1; 7067*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 7068*4c06356bSdh "%s: pmcs_abort to PHY %s failed (rc=%d)", 7069*4c06356bSdh __func__, pptr->path, rc); 7070*4c06356bSdh 7071*4c06356bSdh pmcs_handle_ds_recovery_error(pptr, tgt, 7072*4c06356bSdh pwp, __func__, __LINE__, "pmcs_abort"); 7073*4c06356bSdh 7074*4c06356bSdh goto next_phy; 7075*4c06356bSdh } 7076*4c06356bSdh } 7077*4c06356bSdh 7078*4c06356bSdh /* 7079*4c06356bSdh * Step 4: Set the device back to OPERATIONAL state 7080*4c06356bSdh */ 7081*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, 7082*4c06356bSdh "%s: Set PHY/tgt 0x%p/0x%p to OPERATIONAL state", 7083*4c06356bSdh __func__, (void *)pptr, (void *)tgt); 7084*4c06356bSdh rc = pmcs_set_dev_state(pwp, tgt, 7085*4c06356bSdh PMCS_DEVICE_STATE_OPERATIONAL); 7086*4c06356bSdh if (rc == 0) { 7087*4c06356bSdh tgt->recover_wait = 0; 7088*4c06356bSdh pptr->ds_recovery_retries = 0; 7089*4c06356bSdh /* 7090*4c06356bSdh * Don't bother to run the work queues if the PHY 7091*4c06356bSdh * is dead. 7092*4c06356bSdh */ 7093*4c06356bSdh if (tgt->phy && !tgt->phy->dead) { 7094*4c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 7095*4c06356bSdh (void) ddi_taskq_dispatch(pwp->tq, pmcs_worker, 7096*4c06356bSdh pwp, DDI_NOSLEEP); 7097*4c06356bSdh } 7098*4c06356bSdh } else { 7099*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, 7100*4c06356bSdh "%s: Failed to SET tgt 0x%p to OPERATIONAL state", 7101*4c06356bSdh __func__, (void *)tgt); 7102*4c06356bSdh 7103*4c06356bSdh pmcs_handle_ds_recovery_error(pptr, tgt, pwp, 7104*4c06356bSdh __func__, __LINE__, "SET tgt to OPERATIONAL state"); 7105*4c06356bSdh 7106*4c06356bSdh goto next_phy; 7107*4c06356bSdh } 7108*4c06356bSdh 7109*4c06356bSdh next_phy: 7110*4c06356bSdh if (tgt) { 7111*4c06356bSdh mutex_exit(&tgt->statlock); 7112*4c06356bSdh } 7113*4c06356bSdh pmcs_unlock_phy(pptr); 7114*4c06356bSdh pptr = pnext; 7115*4c06356bSdh } 7116*4c06356bSdh 7117*4c06356bSdh /* 7118*4c06356bSdh * Only clear ds_err_recovering if we're exiting for good and not 7119*4c06356bSdh * just unwinding from recursion 7120*4c06356bSdh */ 7121*4c06356bSdh if (phyp == NULL) { 7122*4c06356bSdh mutex_enter(&pwp->lock); 7123*4c06356bSdh pwp->ds_err_recovering = 0; 7124*4c06356bSdh mutex_exit(&pwp->lock); 7125*4c06356bSdh } 7126*4c06356bSdh } 7127*4c06356bSdh 7128*4c06356bSdh /* 7129*4c06356bSdh * Called with target's statlock and PHY lock held. 7130*4c06356bSdh */ 7131*4c06356bSdh int 7132*4c06356bSdh pmcs_send_err_recovery_cmd(pmcs_hw_t *pwp, uint8_t dev_state, pmcs_xscsi_t *tgt) 7133*4c06356bSdh { 7134*4c06356bSdh pmcs_phy_t *pptr; 7135*4c06356bSdh int rc = -1; 7136*4c06356bSdh 7137*4c06356bSdh ASSERT(tgt != NULL); 7138*4c06356bSdh ASSERT(mutex_owned(&tgt->statlock)); 7139*4c06356bSdh 7140*4c06356bSdh if (tgt->recovering) { 7141*4c06356bSdh return (0); 7142*4c06356bSdh } 7143*4c06356bSdh 7144*4c06356bSdh tgt->recovering = 1; 7145*4c06356bSdh pptr = tgt->phy; 7146*4c06356bSdh 7147*4c06356bSdh if (pptr == NULL) { 7148*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, "%s: PHY is Null", 7149*4c06356bSdh __func__); 7150*4c06356bSdh return (-1); 7151*4c06356bSdh } 7152*4c06356bSdh 7153*4c06356bSdh ASSERT(mutex_owned(&pptr->phy_lock)); 7154*4c06356bSdh 7155*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, "%s: ds: 0x%x, tgt ds(0x%x)", 7156*4c06356bSdh __func__, dev_state, tgt->dev_state); 7157*4c06356bSdh 7158*4c06356bSdh switch (dev_state) { 7159*4c06356bSdh case PMCS_DEVICE_STATE_IN_RECOVERY: 7160*4c06356bSdh if (tgt->dev_state == PMCS_DEVICE_STATE_IN_RECOVERY) { 7161*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, 7162*4c06356bSdh "%s: Target 0x%p already IN_RECOVERY", __func__, 7163*4c06356bSdh (void *)tgt); 7164*4c06356bSdh rc = 0; /* This is not an error */ 7165*4c06356bSdh goto no_action; 7166*4c06356bSdh } 7167*4c06356bSdh 7168*4c06356bSdh rc = pmcs_set_dev_state(pwp, tgt, 7169*4c06356bSdh PMCS_DEVICE_STATE_IN_RECOVERY); 7170*4c06356bSdh if (rc != 0) { 7171*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, 7172*4c06356bSdh "%s(1): Failed to SET tgt(0x%p) to _IN_RECOVERY", 7173*4c06356bSdh __func__, (void *)tgt); 7174*4c06356bSdh } 7175*4c06356bSdh 7176*4c06356bSdh break; 7177*4c06356bSdh 7178*4c06356bSdh case PMCS_DEVICE_STATE_OPERATIONAL: 7179*4c06356bSdh if (tgt->dev_state != PMCS_DEVICE_STATE_IN_RECOVERY) { 7180*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, 7181*4c06356bSdh "%s: Target 0x%p not ready to go OPERATIONAL", 7182*4c06356bSdh __func__, (void *)tgt); 7183*4c06356bSdh goto no_action; 7184*4c06356bSdh } 7185*4c06356bSdh 7186*4c06356bSdh rc = pmcs_set_dev_state(pwp, tgt, 7187*4c06356bSdh PMCS_DEVICE_STATE_OPERATIONAL); 7188*4c06356bSdh tgt->reset_success = 1; 7189*4c06356bSdh if (rc != 0) { 7190*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, 7191*4c06356bSdh "%s(2): Failed to SET tgt(0x%p) to OPERATIONAL", 7192*4c06356bSdh __func__, (void *)tgt); 7193*4c06356bSdh tgt->reset_success = 0; 7194*4c06356bSdh } 7195*4c06356bSdh 7196*4c06356bSdh break; 7197*4c06356bSdh 7198*4c06356bSdh case PMCS_DEVICE_STATE_NON_OPERATIONAL: 7199*4c06356bSdh PHY_CHANGED(pwp, pptr); 7200*4c06356bSdh RESTART_DISCOVERY(pwp); 7201*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, 7202*4c06356bSdh "%s: Device at %s is non-operational", 7203*4c06356bSdh __func__, pptr->path); 7204*4c06356bSdh tgt->dev_state = PMCS_DEVICE_STATE_NON_OPERATIONAL; 7205*4c06356bSdh rc = 0; 7206*4c06356bSdh 7207*4c06356bSdh break; 7208*4c06356bSdh 7209*4c06356bSdh default: 7210*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, 7211*4c06356bSdh "%s: Invalid state requested (%d)", __func__, 7212*4c06356bSdh dev_state); 7213*4c06356bSdh break; 7214*4c06356bSdh 7215*4c06356bSdh } 7216*4c06356bSdh 7217*4c06356bSdh no_action: 7218*4c06356bSdh tgt->recovering = 0; 7219*4c06356bSdh return (rc); 7220*4c06356bSdh } 7221*4c06356bSdh 7222*4c06356bSdh /* 7223*4c06356bSdh * pmcs_lock_phy_impl 7224*4c06356bSdh * 7225*4c06356bSdh * This function is what does the actual work for pmcs_lock_phy. It will 7226*4c06356bSdh * lock all PHYs from phyp down in a top-down fashion. 7227*4c06356bSdh * 7228*4c06356bSdh * Locking notes: 7229*4c06356bSdh * 1. level starts from 0 for the PHY ("parent") that's passed in. It is 7230*4c06356bSdh * not a reflection of the actual level of the PHY in the SAS topology. 7231*4c06356bSdh * 2. If parent is an expander, then parent is locked along with all its 7232*4c06356bSdh * descendents. 7233*4c06356bSdh * 3. Expander subsidiary PHYs at level 0 are not locked. It is the 7234*4c06356bSdh * responsibility of the caller to individually lock expander subsidiary PHYs 7235*4c06356bSdh * at level 0 if necessary. 7236*4c06356bSdh * 4. Siblings at level 0 are not traversed due to the possibility that we're 7237*4c06356bSdh * locking a PHY on the dead list. The siblings could be pointing to invalid 7238*4c06356bSdh * PHYs. We don't lock siblings at level 0 anyway. 7239*4c06356bSdh */ 7240*4c06356bSdh static void 7241*4c06356bSdh pmcs_lock_phy_impl(pmcs_phy_t *phyp, int level) 7242*4c06356bSdh { 7243*4c06356bSdh pmcs_phy_t *tphyp; 7244*4c06356bSdh 7245*4c06356bSdh ASSERT((phyp->dtype == SAS) || (phyp->dtype == SATA) || 7246*4c06356bSdh (phyp->dtype == EXPANDER) || (phyp->dtype == NOTHING)); 7247*4c06356bSdh 7248*4c06356bSdh /* 7249*4c06356bSdh * Start walking the PHYs. 7250*4c06356bSdh */ 7251*4c06356bSdh tphyp = phyp; 7252*4c06356bSdh while (tphyp) { 7253*4c06356bSdh /* 7254*4c06356bSdh * If we're at the top level, only lock ourselves. For anything 7255*4c06356bSdh * at level > 0, traverse children while locking everything. 7256*4c06356bSdh */ 7257*4c06356bSdh if ((level > 0) || (tphyp == phyp)) { 7258*4c06356bSdh pmcs_prt(tphyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, 7259*4c06356bSdh "%s: PHY 0x%p parent 0x%p path %s lvl %d", 7260*4c06356bSdh __func__, (void *)tphyp, (void *)tphyp->parent, 7261*4c06356bSdh tphyp->path, level); 7262*4c06356bSdh mutex_enter(&tphyp->phy_lock); 7263*4c06356bSdh 7264*4c06356bSdh if (tphyp->children) { 7265*4c06356bSdh pmcs_lock_phy_impl(tphyp->children, level + 1); 7266*4c06356bSdh } 7267*4c06356bSdh } 7268*4c06356bSdh 7269*4c06356bSdh if (level == 0) { 7270*4c06356bSdh return; 7271*4c06356bSdh } 7272*4c06356bSdh 7273*4c06356bSdh tphyp = tphyp->sibling; 7274*4c06356bSdh } 7275*4c06356bSdh } 7276*4c06356bSdh 7277*4c06356bSdh /* 7278*4c06356bSdh * pmcs_lock_phy 7279*4c06356bSdh * 7280*4c06356bSdh * This function is responsible for locking a PHY and all its descendents 7281*4c06356bSdh */ 7282*4c06356bSdh void 7283*4c06356bSdh pmcs_lock_phy(pmcs_phy_t *phyp) 7284*4c06356bSdh { 7285*4c06356bSdh #ifdef DEBUG 7286*4c06356bSdh char *callername = NULL; 7287*4c06356bSdh ulong_t off; 7288*4c06356bSdh 7289*4c06356bSdh ASSERT(phyp != NULL); 7290*4c06356bSdh 7291*4c06356bSdh callername = modgetsymname((uintptr_t)caller(), &off); 7292*4c06356bSdh 7293*4c06356bSdh if (callername == NULL) { 7294*4c06356bSdh pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, 7295*4c06356bSdh "%s: PHY 0x%p path %s caller: unknown", __func__, 7296*4c06356bSdh (void *)phyp, phyp->path); 7297*4c06356bSdh } else { 7298*4c06356bSdh pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, 7299*4c06356bSdh "%s: PHY 0x%p path %s caller: %s+%lx", __func__, 7300*4c06356bSdh (void *)phyp, phyp->path, callername, off); 7301*4c06356bSdh } 7302*4c06356bSdh #else 7303*4c06356bSdh pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, 7304*4c06356bSdh "%s: PHY 0x%p path %s", __func__, (void *)phyp, phyp->path); 7305*4c06356bSdh #endif 7306*4c06356bSdh pmcs_lock_phy_impl(phyp, 0); 7307*4c06356bSdh } 7308*4c06356bSdh 7309*4c06356bSdh /* 7310*4c06356bSdh * pmcs_unlock_phy_impl 7311*4c06356bSdh * 7312*4c06356bSdh * Unlock all PHYs from phyp down in a bottom-up fashion. 7313*4c06356bSdh */ 7314*4c06356bSdh static void 7315*4c06356bSdh pmcs_unlock_phy_impl(pmcs_phy_t *phyp, int level) 7316*4c06356bSdh { 7317*4c06356bSdh pmcs_phy_t *phy_next; 7318*4c06356bSdh 7319*4c06356bSdh ASSERT((phyp->dtype == SAS) || (phyp->dtype == SATA) || 7320*4c06356bSdh (phyp->dtype == EXPANDER) || (phyp->dtype == NOTHING)); 7321*4c06356bSdh 7322*4c06356bSdh /* 7323*4c06356bSdh * Recurse down to the bottom PHYs 7324*4c06356bSdh */ 7325*4c06356bSdh if (level == 0) { 7326*4c06356bSdh if (phyp->children) { 7327*4c06356bSdh pmcs_unlock_phy_impl(phyp->children, level + 1); 7328*4c06356bSdh } 7329*4c06356bSdh } else { 7330*4c06356bSdh phy_next = phyp; 7331*4c06356bSdh while (phy_next) { 7332*4c06356bSdh if (phy_next->children) { 7333*4c06356bSdh pmcs_unlock_phy_impl(phy_next->children, 7334*4c06356bSdh level + 1); 7335*4c06356bSdh } 7336*4c06356bSdh phy_next = phy_next->sibling; 7337*4c06356bSdh } 7338*4c06356bSdh } 7339*4c06356bSdh 7340*4c06356bSdh /* 7341*4c06356bSdh * Iterate through PHYs unlocking all at level > 0 as well the top PHY 7342*4c06356bSdh */ 7343*4c06356bSdh phy_next = phyp; 7344*4c06356bSdh while (phy_next) { 7345*4c06356bSdh if ((level > 0) || (phy_next == phyp)) { 7346*4c06356bSdh pmcs_prt(phy_next->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, 7347*4c06356bSdh "%s: PHY 0x%p parent 0x%p path %s lvl %d", 7348*4c06356bSdh __func__, (void *)phy_next, 7349*4c06356bSdh (void *)phy_next->parent, phy_next->path, level); 7350*4c06356bSdh mutex_exit(&phy_next->phy_lock); 7351*4c06356bSdh } 7352*4c06356bSdh 7353*4c06356bSdh if (level == 0) { 7354*4c06356bSdh return; 7355*4c06356bSdh } 7356*4c06356bSdh 7357*4c06356bSdh phy_next = phy_next->sibling; 7358*4c06356bSdh } 7359*4c06356bSdh } 7360*4c06356bSdh 7361*4c06356bSdh /* 7362*4c06356bSdh * pmcs_unlock_phy 7363*4c06356bSdh * 7364*4c06356bSdh * Unlock a PHY and all its descendents 7365*4c06356bSdh */ 7366*4c06356bSdh void 7367*4c06356bSdh pmcs_unlock_phy(pmcs_phy_t *phyp) 7368*4c06356bSdh { 7369*4c06356bSdh #ifdef DEBUG 7370*4c06356bSdh char *callername = NULL; 7371*4c06356bSdh ulong_t off; 7372*4c06356bSdh 7373*4c06356bSdh ASSERT(phyp != NULL); 7374*4c06356bSdh 7375*4c06356bSdh callername = modgetsymname((uintptr_t)caller(), &off); 7376*4c06356bSdh 7377*4c06356bSdh if (callername == NULL) { 7378*4c06356bSdh pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, 7379*4c06356bSdh "%s: PHY 0x%p path %s caller: unknown", __func__, 7380*4c06356bSdh (void *)phyp, phyp->path); 7381*4c06356bSdh } else { 7382*4c06356bSdh pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, 7383*4c06356bSdh "%s: PHY 0x%p path %s caller: %s+%lx", __func__, 7384*4c06356bSdh (void *)phyp, phyp->path, callername, off); 7385*4c06356bSdh } 7386*4c06356bSdh #else 7387*4c06356bSdh pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, 7388*4c06356bSdh "%s: PHY 0x%p path %s", __func__, (void *)phyp, phyp->path); 7389*4c06356bSdh #endif 7390*4c06356bSdh pmcs_unlock_phy_impl(phyp, 0); 7391*4c06356bSdh } 7392*4c06356bSdh 7393*4c06356bSdh /* 7394*4c06356bSdh * pmcs_get_root_phy 7395*4c06356bSdh * 7396*4c06356bSdh * For a given phy pointer return its root phy. 7397*4c06356bSdh * The caller must be holding the lock on every PHY from phyp up to the root. 7398*4c06356bSdh */ 7399*4c06356bSdh pmcs_phy_t * 7400*4c06356bSdh pmcs_get_root_phy(pmcs_phy_t *phyp) 7401*4c06356bSdh { 7402*4c06356bSdh ASSERT(phyp); 7403*4c06356bSdh 7404*4c06356bSdh while (phyp) { 7405*4c06356bSdh if (IS_ROOT_PHY(phyp)) { 7406*4c06356bSdh break; 7407*4c06356bSdh } 7408*4c06356bSdh phyp = phyp->parent; 7409*4c06356bSdh } 7410*4c06356bSdh 7411*4c06356bSdh return (phyp); 7412*4c06356bSdh } 7413*4c06356bSdh 7414*4c06356bSdh /* 7415*4c06356bSdh * pmcs_free_dma_chunklist 7416*4c06356bSdh * 7417*4c06356bSdh * Free DMA S/G chunk list 7418*4c06356bSdh */ 7419*4c06356bSdh void 7420*4c06356bSdh pmcs_free_dma_chunklist(pmcs_hw_t *pwp) 7421*4c06356bSdh { 7422*4c06356bSdh pmcs_chunk_t *pchunk; 7423*4c06356bSdh 7424*4c06356bSdh while (pwp->dma_chunklist) { 7425*4c06356bSdh pchunk = pwp->dma_chunklist; 7426*4c06356bSdh pwp->dma_chunklist = pwp->dma_chunklist->next; 7427*4c06356bSdh if (pchunk->dma_handle) { 7428*4c06356bSdh if (ddi_dma_unbind_handle(pchunk->dma_handle) != 7429*4c06356bSdh DDI_SUCCESS) { 7430*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "Condition failed" 7431*4c06356bSdh " at %s():%d", __func__, __LINE__); 7432*4c06356bSdh } 7433*4c06356bSdh ddi_dma_free_handle(&pchunk->dma_handle); 7434*4c06356bSdh ddi_dma_mem_free(&pchunk->acc_handle); 7435*4c06356bSdh } 7436*4c06356bSdh kmem_free(pchunk, sizeof (pmcs_chunk_t)); 7437*4c06356bSdh } 7438*4c06356bSdh } 7439*4c06356bSdh 7440*4c06356bSdh 7441*4c06356bSdh /* 7442*4c06356bSdh * Start ssp event recovery. We have to schedule recovery operation because 7443*4c06356bSdh * it involves sending multiple commands to device and we should not do it 7444*4c06356bSdh * in the interrupt context. 7445*4c06356bSdh * If it is failure of a recovery command, let the recovery thread deal with it. 7446*4c06356bSdh * Called with pmcwork lock held. 7447*4c06356bSdh */ 7448*4c06356bSdh 7449*4c06356bSdh void 7450*4c06356bSdh pmcs_start_ssp_event_recovery(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *iomb, 7451*4c06356bSdh size_t amt) 7452*4c06356bSdh { 7453*4c06356bSdh pmcs_xscsi_t *tgt = pwrk->xp; 7454*4c06356bSdh uint32_t event = LE_32(iomb[2]); 7455*4c06356bSdh pmcs_phy_t *pptr = pwrk->phy; 7456*4c06356bSdh uint32_t tag; 7457*4c06356bSdh 7458*4c06356bSdh if (tgt != NULL) { 7459*4c06356bSdh mutex_enter(&tgt->statlock); 7460*4c06356bSdh if (tgt->dying || !tgt->assigned) { 7461*4c06356bSdh if (pptr) { 7462*4c06356bSdh pmcs_dec_phy_ref_count(pptr); 7463*4c06356bSdh } 7464*4c06356bSdh pptr = NULL; 7465*4c06356bSdh pwrk->phy = NULL; 7466*4c06356bSdh } 7467*4c06356bSdh mutex_exit(&tgt->statlock); 7468*4c06356bSdh } 7469*4c06356bSdh if (pptr == NULL) { 7470*4c06356bSdh /* 7471*4c06356bSdh * No target or dying target.Need to run RE-DISCOVERY here. 7472*4c06356bSdh */ 7473*4c06356bSdh if (pwrk->state != PMCS_WORK_STATE_TIMED_OUT) { 7474*4c06356bSdh pwrk->state = PMCS_WORK_STATE_INTR; 7475*4c06356bSdh } 7476*4c06356bSdh /* 7477*4c06356bSdh * Although we cannot mark phy to force abort nor mark phy 7478*4c06356bSdh * as changed, killing of a target would take care of aborting 7479*4c06356bSdh * commands for the device. 7480*4c06356bSdh */ 7481*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: No valid target for event " 7482*4c06356bSdh "processing found. Scheduling RECONFIGURE", __func__); 7483*4c06356bSdh pmcs_pwork(pwp, pwrk); 7484*4c06356bSdh RESTART_DISCOVERY(pwp); 7485*4c06356bSdh return; 7486*4c06356bSdh } else { 7487*4c06356bSdh pmcs_lock_phy(pptr); 7488*4c06356bSdh mutex_enter(&tgt->statlock); 7489*4c06356bSdh if (event == PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS) { 7490*4c06356bSdh if (tgt->dev_state != 7491*4c06356bSdh PMCS_DEVICE_STATE_NON_OPERATIONAL) { 7492*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Device at " 7493*4c06356bSdh "%s is non-operational", __func__, 7494*4c06356bSdh pptr->path); 7495*4c06356bSdh tgt->dev_state = 7496*4c06356bSdh PMCS_DEVICE_STATE_NON_OPERATIONAL; 7497*4c06356bSdh } 7498*4c06356bSdh pptr->abort_pending = 1; 7499*4c06356bSdh mutex_exit(&tgt->statlock); 7500*4c06356bSdh pmcs_unlock_phy(pptr); 7501*4c06356bSdh mutex_exit(&pwrk->lock); 7502*4c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE); 7503*4c06356bSdh RESTART_DISCOVERY(pwp); 7504*4c06356bSdh return; 7505*4c06356bSdh } 7506*4c06356bSdh 7507*4c06356bSdh /* 7508*4c06356bSdh * If this command is run in WAIT mode, it is a failing recovery 7509*4c06356bSdh * command. If so, just wake up recovery thread waiting for 7510*4c06356bSdh * command completion. 7511*4c06356bSdh */ 7512*4c06356bSdh tag = PMCS_TAG_TYPE(pwrk->htag); 7513*4c06356bSdh if (tag == PMCS_TAG_TYPE_WAIT) { 7514*4c06356bSdh pwrk->htag |= PMCS_TAG_DONE; 7515*4c06356bSdh if (pwrk->arg && amt) { 7516*4c06356bSdh (void) memcpy(pwrk->arg, iomb, amt); 7517*4c06356bSdh } 7518*4c06356bSdh cv_signal(&pwrk->sleep_cv); 7519*4c06356bSdh mutex_exit(&tgt->statlock); 7520*4c06356bSdh pmcs_unlock_phy(pptr); 7521*4c06356bSdh mutex_exit(&pwrk->lock); 7522*4c06356bSdh return; 7523*4c06356bSdh } 7524*4c06356bSdh 7525*4c06356bSdh /* 7526*4c06356bSdh * To recover from primary failures, 7527*4c06356bSdh * we need to schedule handling events recovery. 7528*4c06356bSdh */ 7529*4c06356bSdh tgt->event_recovery = 1; 7530*4c06356bSdh mutex_exit(&tgt->statlock); 7531*4c06356bSdh pmcs_unlock_phy(pptr); 7532*4c06356bSdh pwrk->ssp_event = event; 7533*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 7534*4c06356bSdh "%s: Scheduling SSP event recovery for tgt(0x%p) " 7535*4c06356bSdh "pwrk(%p) tag(0x%x)", __func__, (void *)tgt, (void *)pwrk, 7536*4c06356bSdh pwrk->htag); 7537*4c06356bSdh mutex_exit(&pwrk->lock); 7538*4c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_SSP_EVT_RECOVERY); 7539*4c06356bSdh } 7540*4c06356bSdh 7541*4c06356bSdh /* Work cannot be completed until event recovery is completed. */ 7542*4c06356bSdh } 7543*4c06356bSdh 7544*4c06356bSdh /* 7545*4c06356bSdh * SSP target event recovery 7546*4c06356bSdh * Entered with a phy lock held 7547*4c06356bSdh * Pwrk lock is not needed - pwrk is on the target aq and no other thread 7548*4c06356bSdh * will do anything with it until this thread starts the chain of recovery. 7549*4c06356bSdh * Statlock may be acquired and released. 7550*4c06356bSdh */ 7551*4c06356bSdh 7552*4c06356bSdh void 7553*4c06356bSdh pmcs_tgt_event_recovery(pmcs_hw_t *pwp, pmcwork_t *pwrk) 7554*4c06356bSdh { 7555*4c06356bSdh pmcs_phy_t *pptr = pwrk->phy; 7556*4c06356bSdh pmcs_cmd_t *sp = pwrk->arg; 7557*4c06356bSdh pmcs_lun_t *lun = sp->cmd_lun; 7558*4c06356bSdh pmcs_xscsi_t *tgt = pwrk->xp; 7559*4c06356bSdh uint32_t event; 7560*4c06356bSdh uint32_t htag; 7561*4c06356bSdh uint32_t status; 7562*4c06356bSdh uint8_t dstate; 7563*4c06356bSdh int rv; 7564*4c06356bSdh 7565*4c06356bSdh ASSERT(pwrk->arg != NULL); 7566*4c06356bSdh ASSERT(pwrk->xp != NULL); 7567*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: event recovery for " 7568*4c06356bSdh "target 0x%p", __func__, (void *)pwrk->xp); 7569*4c06356bSdh htag = pwrk->htag; 7570*4c06356bSdh event = pwrk->ssp_event; 7571*4c06356bSdh pwrk->ssp_event = 0xffffffff; 7572*4c06356bSdh if (event == PMCOUT_STATUS_XFER_ERR_BREAK || 7573*4c06356bSdh event == PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY || 7574*4c06356bSdh event == PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT) { 7575*4c06356bSdh /* Command may be still pending on device */ 7576*4c06356bSdh rv = pmcs_ssp_tmf(pwp, pptr, SAS_QUERY_TASK, htag, 7577*4c06356bSdh lun->lun_num, &status); 7578*4c06356bSdh if (rv != 0) { 7579*4c06356bSdh goto out; 7580*4c06356bSdh } 7581*4c06356bSdh if (status == SAS_RSP_TMF_COMPLETE) { 7582*4c06356bSdh /* Command NOT pending on a device */ 7583*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 7584*4c06356bSdh "%s: No pending command for tgt 0x%p", 7585*4c06356bSdh __func__, (void *)tgt); 7586*4c06356bSdh /* Nothing more to do, just abort it on chip */ 7587*4c06356bSdh htag = 0; 7588*4c06356bSdh } 7589*4c06356bSdh } 7590*4c06356bSdh /* 7591*4c06356bSdh * All other events left the command pending in the host 7592*4c06356bSdh * Send abort task and abort it on the chip 7593*4c06356bSdh */ 7594*4c06356bSdh if (htag != 0) { 7595*4c06356bSdh if (pmcs_ssp_tmf(pwp, pptr, SAS_ABORT_TASK, htag, 7596*4c06356bSdh lun->lun_num, &status)) 7597*4c06356bSdh goto out; 7598*4c06356bSdh } 7599*4c06356bSdh (void) pmcs_abort(pwp, pptr, pwrk->htag, 0, 1); 7600*4c06356bSdh /* 7601*4c06356bSdh * Abort either took care of work completion, or put device in 7602*4c06356bSdh * a recovery state 7603*4c06356bSdh */ 7604*4c06356bSdh return; 7605*4c06356bSdh out: 7606*4c06356bSdh /* Abort failed, do full device recovery */ 7607*4c06356bSdh mutex_enter(&tgt->statlock); 7608*4c06356bSdh if (!pmcs_get_dev_state(pwp, tgt, &dstate)) 7609*4c06356bSdh tgt->dev_state = dstate; 7610*4c06356bSdh 7611*4c06356bSdh if ((tgt->dev_state != PMCS_DEVICE_STATE_IN_RECOVERY) && 7612*4c06356bSdh (tgt->dev_state != PMCS_DEVICE_STATE_NON_OPERATIONAL)) { 7613*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 7614*4c06356bSdh "%s: Setting IN_RECOVERY for tgt 0x%p", 7615*4c06356bSdh __func__, (void *)tgt); 7616*4c06356bSdh (void) pmcs_send_err_recovery_cmd(pwp, 7617*4c06356bSdh PMCS_DEVICE_STATE_IN_RECOVERY, tgt); 7618*4c06356bSdh } 7619*4c06356bSdh mutex_exit(&tgt->statlock); 7620*4c06356bSdh } 7621*4c06356bSdh 7622*4c06356bSdh /* 7623*4c06356bSdh * SSP event recovery task. 7624*4c06356bSdh */ 7625*4c06356bSdh void 7626*4c06356bSdh pmcs_ssp_event_recovery(pmcs_hw_t *pwp) 7627*4c06356bSdh { 7628*4c06356bSdh int idx; 7629*4c06356bSdh pmcs_xscsi_t *tgt; 7630*4c06356bSdh pmcs_cmd_t *cp; 7631*4c06356bSdh pmcwork_t *pwrk; 7632*4c06356bSdh pmcs_phy_t *pphy; 7633*4c06356bSdh int er_flag; 7634*4c06356bSdh uint32_t idxpwrk; 7635*4c06356bSdh 7636*4c06356bSdh restart: 7637*4c06356bSdh for (idx = 0; idx < pwp->max_dev; idx++) { 7638*4c06356bSdh mutex_enter(&pwp->lock); 7639*4c06356bSdh tgt = pwp->targets[idx]; 7640*4c06356bSdh mutex_exit(&pwp->lock); 7641*4c06356bSdh if (tgt != NULL) { 7642*4c06356bSdh mutex_enter(&tgt->statlock); 7643*4c06356bSdh if (tgt->dying || !tgt->assigned) { 7644*4c06356bSdh mutex_exit(&tgt->statlock); 7645*4c06356bSdh continue; 7646*4c06356bSdh } 7647*4c06356bSdh pphy = tgt->phy; 7648*4c06356bSdh er_flag = tgt->event_recovery; 7649*4c06356bSdh mutex_exit(&tgt->statlock); 7650*4c06356bSdh if (pphy != NULL && er_flag != 0) { 7651*4c06356bSdh pmcs_lock_phy(pphy); 7652*4c06356bSdh mutex_enter(&tgt->statlock); 7653*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 7654*4c06356bSdh "%s: found target(0x%p)", __func__, 7655*4c06356bSdh (void *) tgt); 7656*4c06356bSdh 7657*4c06356bSdh /* Check what cmd expects recovery */ 7658*4c06356bSdh mutex_enter(&tgt->aqlock); 7659*4c06356bSdh STAILQ_FOREACH(cp, &tgt->aq, cmd_next) { 7660*4c06356bSdh /* 7661*4c06356bSdh * Since work structure is on this 7662*4c06356bSdh * target aq, and only this thread 7663*4c06356bSdh * is accessing it now, we do not need 7664*4c06356bSdh * to lock it 7665*4c06356bSdh */ 7666*4c06356bSdh idxpwrk = PMCS_TAG_INDEX(cp->cmd_tag); 7667*4c06356bSdh pwrk = &pwp->work[idxpwrk]; 7668*4c06356bSdh if (pwrk->htag != cp->cmd_tag) { 7669*4c06356bSdh /* 7670*4c06356bSdh * aq may contain TMF commands, 7671*4c06356bSdh * so we may not find work 7672*4c06356bSdh * structure with htag 7673*4c06356bSdh */ 7674*4c06356bSdh break; 7675*4c06356bSdh } 7676*4c06356bSdh if (pwrk->ssp_event != 0 && 7677*4c06356bSdh pwrk->ssp_event != 7678*4c06356bSdh PMCS_REC_EVENT) { 7679*4c06356bSdh pmcs_prt(pwp, 7680*4c06356bSdh PMCS_PRT_DEBUG, 7681*4c06356bSdh "%s: pwrk(%p) ctag(0x%x)", 7682*4c06356bSdh __func__, (void *) pwrk, 7683*4c06356bSdh cp->cmd_tag); 7684*4c06356bSdh mutex_exit(&tgt->aqlock); 7685*4c06356bSdh mutex_exit(&tgt->statlock); 7686*4c06356bSdh pmcs_tgt_event_recovery( 7687*4c06356bSdh pwp, pwrk); 7688*4c06356bSdh /* 7689*4c06356bSdh * We dropped statlock, so 7690*4c06356bSdh * restart scanning from scratch 7691*4c06356bSdh */ 7692*4c06356bSdh pmcs_unlock_phy(pphy); 7693*4c06356bSdh goto restart; 7694*4c06356bSdh } 7695*4c06356bSdh } 7696*4c06356bSdh mutex_exit(&tgt->aqlock); 7697*4c06356bSdh tgt->event_recovery = 0; 7698*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 7699*4c06356bSdh "%s: end of SSP event recovery for " 7700*4c06356bSdh "target(0x%p)", __func__, (void *) tgt); 7701*4c06356bSdh mutex_exit(&tgt->statlock); 7702*4c06356bSdh pmcs_unlock_phy(pphy); 7703*4c06356bSdh } 7704*4c06356bSdh } 7705*4c06356bSdh } 7706*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 7707*4c06356bSdh "%s: end of SSP event recovery for pwp(0x%p)", __func__, 7708*4c06356bSdh (void *) pwp); 7709*4c06356bSdh } 7710*4c06356bSdh 7711*4c06356bSdh /*ARGSUSED2*/ 7712*4c06356bSdh int 7713*4c06356bSdh pmcs_phy_constructor(void *buf, void *arg, int kmflags) 7714*4c06356bSdh { 7715*4c06356bSdh pmcs_hw_t *pwp = (pmcs_hw_t *)arg; 7716*4c06356bSdh pmcs_phy_t *phyp = (pmcs_phy_t *)buf; 7717*4c06356bSdh 7718*4c06356bSdh mutex_init(&phyp->phy_lock, NULL, MUTEX_DRIVER, 7719*4c06356bSdh DDI_INTR_PRI(pwp->intr_pri)); 7720*4c06356bSdh cv_init(&phyp->abort_all_cv, NULL, CV_DRIVER, NULL); 7721*4c06356bSdh return (0); 7722*4c06356bSdh } 7723*4c06356bSdh 7724*4c06356bSdh /*ARGSUSED1*/ 7725*4c06356bSdh void 7726*4c06356bSdh pmcs_phy_destructor(void *buf, void *arg) 7727*4c06356bSdh { 7728*4c06356bSdh pmcs_phy_t *phyp = (pmcs_phy_t *)buf; 7729*4c06356bSdh 7730*4c06356bSdh cv_destroy(&phyp->abort_all_cv); 7731*4c06356bSdh mutex_destroy(&phyp->phy_lock); 7732*4c06356bSdh } 7733*4c06356bSdh 7734*4c06356bSdh /* 7735*4c06356bSdh * Free all PHYs from the kmem_cache starting at phyp as well as everything 7736*4c06356bSdh * on the dead_phys list. 7737*4c06356bSdh * 7738*4c06356bSdh * NOTE: This function does not free root PHYs as they are not allocated 7739*4c06356bSdh * from the kmem_cache. 7740*4c06356bSdh * 7741*4c06356bSdh * No PHY locks are acquired as this should only be called during DDI_DETACH 7742*4c06356bSdh * or soft reset (while pmcs interrupts are disabled). 7743*4c06356bSdh */ 7744*4c06356bSdh void 7745*4c06356bSdh pmcs_free_all_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp) 7746*4c06356bSdh { 7747*4c06356bSdh pmcs_phy_t *tphyp, *nphyp; 7748*4c06356bSdh 7749*4c06356bSdh if (phyp == NULL) { 7750*4c06356bSdh return; 7751*4c06356bSdh } 7752*4c06356bSdh 7753*4c06356bSdh tphyp = phyp; 7754*4c06356bSdh while (tphyp) { 7755*4c06356bSdh nphyp = tphyp->sibling; 7756*4c06356bSdh 7757*4c06356bSdh if (tphyp->children) { 7758*4c06356bSdh pmcs_free_all_phys(pwp, tphyp->children); 7759*4c06356bSdh tphyp->children = NULL; 7760*4c06356bSdh } 7761*4c06356bSdh if (!IS_ROOT_PHY(tphyp)) { 7762*4c06356bSdh kmem_cache_free(pwp->phy_cache, tphyp); 7763*4c06356bSdh } 7764*4c06356bSdh 7765*4c06356bSdh tphyp = nphyp; 7766*4c06356bSdh } 7767*4c06356bSdh 7768*4c06356bSdh tphyp = pwp->dead_phys; 7769*4c06356bSdh while (tphyp) { 7770*4c06356bSdh nphyp = tphyp->sibling; 7771*4c06356bSdh kmem_cache_free(pwp->phy_cache, tphyp); 7772*4c06356bSdh tphyp = nphyp; 7773*4c06356bSdh } 7774*4c06356bSdh pwp->dead_phys = NULL; 7775*4c06356bSdh } 7776*4c06356bSdh 7777*4c06356bSdh /* 7778*4c06356bSdh * Free a list of PHYs linked together by the sibling pointer back to the 7779*4c06356bSdh * kmem cache from whence they came. This function does not recurse, so the 7780*4c06356bSdh * caller must ensure there are no children. 7781*4c06356bSdh */ 7782*4c06356bSdh void 7783*4c06356bSdh pmcs_free_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp) 7784*4c06356bSdh { 7785*4c06356bSdh pmcs_phy_t *next_phy; 7786*4c06356bSdh 7787*4c06356bSdh while (phyp) { 7788*4c06356bSdh next_phy = phyp->sibling; 7789*4c06356bSdh ASSERT(!mutex_owned(&phyp->phy_lock)); 7790*4c06356bSdh kmem_cache_free(pwp->phy_cache, phyp); 7791*4c06356bSdh phyp = next_phy; 7792*4c06356bSdh } 7793*4c06356bSdh } 7794*4c06356bSdh 7795*4c06356bSdh /* 7796*4c06356bSdh * Make a copy of an existing PHY structure. This is used primarily in 7797*4c06356bSdh * discovery to compare the contents of an existing PHY with what gets 7798*4c06356bSdh * reported back by an expander. 7799*4c06356bSdh * 7800*4c06356bSdh * This function must not be called from any context where sleeping is 7801*4c06356bSdh * not possible. 7802*4c06356bSdh * 7803*4c06356bSdh * The new PHY is returned unlocked. 7804*4c06356bSdh */ 7805*4c06356bSdh static pmcs_phy_t * 7806*4c06356bSdh pmcs_clone_phy(pmcs_phy_t *orig_phy) 7807*4c06356bSdh { 7808*4c06356bSdh pmcs_phy_t *local; 7809*4c06356bSdh 7810*4c06356bSdh local = kmem_cache_alloc(orig_phy->pwp->phy_cache, KM_SLEEP); 7811*4c06356bSdh 7812*4c06356bSdh /* 7813*4c06356bSdh * Go ahead and just copy everything... 7814*4c06356bSdh */ 7815*4c06356bSdh *local = *orig_phy; 7816*4c06356bSdh 7817*4c06356bSdh /* 7818*4c06356bSdh * But the following must be set appropriately for this copy 7819*4c06356bSdh */ 7820*4c06356bSdh local->sibling = NULL; 7821*4c06356bSdh local->children = NULL; 7822*4c06356bSdh mutex_init(&local->phy_lock, NULL, MUTEX_DRIVER, 7823*4c06356bSdh DDI_INTR_PRI(orig_phy->pwp->intr_pri)); 7824*4c06356bSdh 7825*4c06356bSdh return (local); 7826*4c06356bSdh } 7827*4c06356bSdh 7828*4c06356bSdh int 7829*4c06356bSdh pmcs_check_acc_handle(ddi_acc_handle_t handle) 7830*4c06356bSdh { 7831*4c06356bSdh ddi_fm_error_t de; 7832*4c06356bSdh 7833*4c06356bSdh if (handle == NULL) { 7834*4c06356bSdh return (DDI_FAILURE); 7835*4c06356bSdh } 7836*4c06356bSdh ddi_fm_acc_err_get(handle, &de, DDI_FME_VER0); 7837*4c06356bSdh return (de.fme_status); 7838*4c06356bSdh } 7839*4c06356bSdh 7840*4c06356bSdh int 7841*4c06356bSdh pmcs_check_dma_handle(ddi_dma_handle_t handle) 7842*4c06356bSdh { 7843*4c06356bSdh ddi_fm_error_t de; 7844*4c06356bSdh 7845*4c06356bSdh if (handle == NULL) { 7846*4c06356bSdh return (DDI_FAILURE); 7847*4c06356bSdh } 7848*4c06356bSdh ddi_fm_dma_err_get(handle, &de, DDI_FME_VER0); 7849*4c06356bSdh return (de.fme_status); 7850*4c06356bSdh } 7851*4c06356bSdh 7852*4c06356bSdh 7853*4c06356bSdh void 7854*4c06356bSdh pmcs_fm_ereport(pmcs_hw_t *pwp, char *detail) 7855*4c06356bSdh { 7856*4c06356bSdh uint64_t ena; 7857*4c06356bSdh char buf[FM_MAX_CLASS]; 7858*4c06356bSdh 7859*4c06356bSdh (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", DDI_FM_DEVICE, detail); 7860*4c06356bSdh ena = fm_ena_generate(0, FM_ENA_FMT1); 7861*4c06356bSdh if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities)) { 7862*4c06356bSdh ddi_fm_ereport_post(pwp->dip, buf, ena, DDI_NOSLEEP, 7863*4c06356bSdh FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, NULL); 7864*4c06356bSdh } 7865*4c06356bSdh } 7866*4c06356bSdh 7867*4c06356bSdh int 7868*4c06356bSdh pmcs_check_acc_dma_handle(pmcs_hw_t *pwp) 7869*4c06356bSdh { 7870*4c06356bSdh pmcs_chunk_t *pchunk; 7871*4c06356bSdh int i; 7872*4c06356bSdh 7873*4c06356bSdh /* check all acc & dma handles allocated in attach */ 7874*4c06356bSdh if ((pmcs_check_acc_handle(pwp->pci_acc_handle) != DDI_SUCCESS) || 7875*4c06356bSdh (pmcs_check_acc_handle(pwp->msg_acc_handle) != DDI_SUCCESS) || 7876*4c06356bSdh (pmcs_check_acc_handle(pwp->top_acc_handle) != DDI_SUCCESS) || 7877*4c06356bSdh (pmcs_check_acc_handle(pwp->mpi_acc_handle) != DDI_SUCCESS) || 7878*4c06356bSdh (pmcs_check_acc_handle(pwp->gsm_acc_handle) != DDI_SUCCESS)) { 7879*4c06356bSdh goto check_failed; 7880*4c06356bSdh } 7881*4c06356bSdh 7882*4c06356bSdh for (i = 0; i < PMCS_NIQ; i++) { 7883*4c06356bSdh if ((pmcs_check_dma_handle( 7884*4c06356bSdh pwp->iqp_handles[i]) != DDI_SUCCESS) || 7885*4c06356bSdh (pmcs_check_acc_handle( 7886*4c06356bSdh pwp->iqp_acchdls[i]) != DDI_SUCCESS)) { 7887*4c06356bSdh goto check_failed; 7888*4c06356bSdh } 7889*4c06356bSdh } 7890*4c06356bSdh 7891*4c06356bSdh for (i = 0; i < PMCS_NOQ; i++) { 7892*4c06356bSdh if ((pmcs_check_dma_handle( 7893*4c06356bSdh pwp->oqp_handles[i]) != DDI_SUCCESS) || 7894*4c06356bSdh (pmcs_check_acc_handle( 7895*4c06356bSdh pwp->oqp_acchdls[i]) != DDI_SUCCESS)) { 7896*4c06356bSdh goto check_failed; 7897*4c06356bSdh } 7898*4c06356bSdh } 7899*4c06356bSdh 7900*4c06356bSdh if ((pmcs_check_dma_handle(pwp->cip_handles) != DDI_SUCCESS) || 7901*4c06356bSdh (pmcs_check_acc_handle(pwp->cip_acchdls) != DDI_SUCCESS)) { 7902*4c06356bSdh goto check_failed; 7903*4c06356bSdh } 7904*4c06356bSdh 7905*4c06356bSdh if (pwp->fwlog && 7906*4c06356bSdh ((pmcs_check_dma_handle(pwp->fwlog_hndl) != DDI_SUCCESS) || 7907*4c06356bSdh (pmcs_check_acc_handle(pwp->fwlog_acchdl) != DDI_SUCCESS))) { 7908*4c06356bSdh goto check_failed; 7909*4c06356bSdh } 7910*4c06356bSdh 7911*4c06356bSdh if (pwp->regdump_hndl && pwp->regdump_acchdl && 7912*4c06356bSdh ((pmcs_check_dma_handle(pwp->regdump_hndl) != DDI_SUCCESS) || 7913*4c06356bSdh (pmcs_check_acc_handle(pwp->regdump_acchdl) 7914*4c06356bSdh != DDI_SUCCESS))) { 7915*4c06356bSdh goto check_failed; 7916*4c06356bSdh } 7917*4c06356bSdh 7918*4c06356bSdh 7919*4c06356bSdh pchunk = pwp->dma_chunklist; 7920*4c06356bSdh while (pchunk) { 7921*4c06356bSdh if ((pmcs_check_acc_handle(pchunk->acc_handle) 7922*4c06356bSdh != DDI_SUCCESS) || 7923*4c06356bSdh (pmcs_check_dma_handle(pchunk->dma_handle) 7924*4c06356bSdh != DDI_SUCCESS)) { 7925*4c06356bSdh goto check_failed; 7926*4c06356bSdh } 7927*4c06356bSdh pchunk = pchunk->next; 7928*4c06356bSdh } 7929*4c06356bSdh 7930*4c06356bSdh return (0); 7931*4c06356bSdh 7932*4c06356bSdh check_failed: 7933*4c06356bSdh 7934*4c06356bSdh return (1); 7935*4c06356bSdh } 7936*4c06356bSdh 7937*4c06356bSdh /* 7938*4c06356bSdh * pmcs_handle_dead_phys 7939*4c06356bSdh * 7940*4c06356bSdh * If the PHY has no outstanding work associated with it, remove it from 7941*4c06356bSdh * the dead PHY list and free it. 7942*4c06356bSdh * 7943*4c06356bSdh * If pwp->ds_err_recovering or pwp->configuring is set, don't run. 7944*4c06356bSdh * This keeps routines that need to submit work to the chip from having to 7945*4c06356bSdh * hold PHY locks to ensure that PHYs don't disappear while they do their work. 7946*4c06356bSdh */ 7947*4c06356bSdh void 7948*4c06356bSdh pmcs_handle_dead_phys(pmcs_hw_t *pwp) 7949*4c06356bSdh { 7950*4c06356bSdh pmcs_phy_t *phyp, *nphyp, *pphyp; 7951*4c06356bSdh 7952*4c06356bSdh mutex_enter(&pwp->lock); 7953*4c06356bSdh mutex_enter(&pwp->config_lock); 7954*4c06356bSdh 7955*4c06356bSdh if (pwp->configuring | pwp->ds_err_recovering) { 7956*4c06356bSdh mutex_exit(&pwp->config_lock); 7957*4c06356bSdh mutex_exit(&pwp->lock); 7958*4c06356bSdh return; 7959*4c06356bSdh } 7960*4c06356bSdh 7961*4c06356bSdh /* 7962*4c06356bSdh * Check every PHY in the dead PHY list 7963*4c06356bSdh */ 7964*4c06356bSdh mutex_enter(&pwp->dead_phylist_lock); 7965*4c06356bSdh phyp = pwp->dead_phys; 7966*4c06356bSdh pphyp = NULL; /* Set previous PHY to NULL */ 7967*4c06356bSdh 7968*4c06356bSdh while (phyp != NULL) { 7969*4c06356bSdh pmcs_lock_phy(phyp); 7970*4c06356bSdh ASSERT(phyp->dead); 7971*4c06356bSdh 7972*4c06356bSdh nphyp = phyp->dead_next; 7973*4c06356bSdh 7974*4c06356bSdh /* 7975*4c06356bSdh * Check for outstanding work 7976*4c06356bSdh */ 7977*4c06356bSdh if (phyp->ref_count > 0) { 7978*4c06356bSdh pmcs_unlock_phy(phyp); 7979*4c06356bSdh pphyp = phyp; /* This PHY becomes "previous" */ 7980*4c06356bSdh } else if (phyp->target) { 7981*4c06356bSdh pmcs_unlock_phy(phyp); 7982*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG1, 7983*4c06356bSdh "%s: Not freeing PHY 0x%p: target 0x%p is not free", 7984*4c06356bSdh __func__, (void *)phyp, (void *)phyp->target); 7985*4c06356bSdh pphyp = phyp; 7986*4c06356bSdh } else { 7987*4c06356bSdh /* 7988*4c06356bSdh * No outstanding work or target references. Remove it 7989*4c06356bSdh * from the list and free it 7990*4c06356bSdh */ 7991*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 7992*4c06356bSdh "%s: Freeing inactive dead PHY 0x%p @ %s " 7993*4c06356bSdh "target = 0x%p", __func__, (void *)phyp, 7994*4c06356bSdh phyp->path, (void *)phyp->target); 7995*4c06356bSdh /* 7996*4c06356bSdh * If pphyp is NULL, then phyp was the head of the list, 7997*4c06356bSdh * so just reset the head to nphyp. Otherwise, the 7998*4c06356bSdh * previous PHY will now point to nphyp (the next PHY) 7999*4c06356bSdh */ 8000*4c06356bSdh if (pphyp == NULL) { 8001*4c06356bSdh pwp->dead_phys = nphyp; 8002*4c06356bSdh } else { 8003*4c06356bSdh pphyp->dead_next = nphyp; 8004*4c06356bSdh } 8005*4c06356bSdh /* 8006*4c06356bSdh * If the target still points to this PHY, remove 8007*4c06356bSdh * that linkage now. 8008*4c06356bSdh */ 8009*4c06356bSdh if (phyp->target) { 8010*4c06356bSdh mutex_enter(&phyp->target->statlock); 8011*4c06356bSdh if (phyp->target->phy == phyp) { 8012*4c06356bSdh phyp->target->phy = NULL; 8013*4c06356bSdh } 8014*4c06356bSdh mutex_exit(&phyp->target->statlock); 8015*4c06356bSdh } 8016*4c06356bSdh kmem_cache_free(pwp->phy_cache, phyp); 8017*4c06356bSdh } 8018*4c06356bSdh 8019*4c06356bSdh phyp = nphyp; 8020*4c06356bSdh } 8021*4c06356bSdh 8022*4c06356bSdh mutex_exit(&pwp->dead_phylist_lock); 8023*4c06356bSdh mutex_exit(&pwp->config_lock); 8024*4c06356bSdh mutex_exit(&pwp->lock); 8025*4c06356bSdh } 8026*4c06356bSdh 8027*4c06356bSdh void 8028*4c06356bSdh pmcs_inc_phy_ref_count(pmcs_phy_t *phyp) 8029*4c06356bSdh { 8030*4c06356bSdh atomic_inc_32(&phyp->ref_count); 8031*4c06356bSdh } 8032*4c06356bSdh 8033*4c06356bSdh void 8034*4c06356bSdh pmcs_dec_phy_ref_count(pmcs_phy_t *phyp) 8035*4c06356bSdh { 8036*4c06356bSdh ASSERT(phyp->ref_count != 0); 8037*4c06356bSdh atomic_dec_32(&phyp->ref_count); 8038*4c06356bSdh } 8039*4c06356bSdh 8040*4c06356bSdh /* 8041*4c06356bSdh * pmcs_reap_dead_phy 8042*4c06356bSdh * 8043*4c06356bSdh * This function is called from pmcs_new_tport when we have a PHY 8044*4c06356bSdh * without a target pointer. It's possible in that case that this PHY 8045*4c06356bSdh * may have a "brother" on the dead_phys list. That is, it may be the same as 8046*4c06356bSdh * this one but with a different root PHY number (e.g. pp05 vs. pp04). If 8047*4c06356bSdh * that's the case, update the dead PHY and this new PHY. If that's not the 8048*4c06356bSdh * case, we should get a tran_tgt_init on this after it's reported to SCSA. 8049*4c06356bSdh * 8050*4c06356bSdh * Called with PHY locked. 8051*4c06356bSdh */ 8052*4c06356bSdh static void 8053*4c06356bSdh pmcs_reap_dead_phy(pmcs_phy_t *phyp) 8054*4c06356bSdh { 8055*4c06356bSdh pmcs_hw_t *pwp = phyp->pwp; 8056*4c06356bSdh pmcs_phy_t *ctmp; 8057*4c06356bSdh 8058*4c06356bSdh ASSERT(mutex_owned(&phyp->phy_lock)); 8059*4c06356bSdh 8060*4c06356bSdh /* 8061*4c06356bSdh * Check the dead PHYs list 8062*4c06356bSdh */ 8063*4c06356bSdh mutex_enter(&pwp->dead_phylist_lock); 8064*4c06356bSdh ctmp = pwp->dead_phys; 8065*4c06356bSdh while (ctmp) { 8066*4c06356bSdh if ((ctmp->iport != phyp->iport) || 8067*4c06356bSdh (memcmp((void *)&ctmp->sas_address[0], 8068*4c06356bSdh (void *)&phyp->sas_address[0], 8))) { 8069*4c06356bSdh ctmp = ctmp->dead_next; 8070*4c06356bSdh continue; 8071*4c06356bSdh } 8072*4c06356bSdh 8073*4c06356bSdh /* 8074*4c06356bSdh * Same SAS address on same iport. Now check to see if 8075*4c06356bSdh * the PHY path is the same with the possible exception 8076*4c06356bSdh * of the root PHY number. 8077*4c06356bSdh * The "5" is the string length of "pp00." 8078*4c06356bSdh */ 8079*4c06356bSdh if ((strnlen(phyp->path, 5) >= 5) && 8080*4c06356bSdh (strnlen(ctmp->path, 5) >= 5)) { 8081*4c06356bSdh if (memcmp((void *)&phyp->path[5], 8082*4c06356bSdh (void *)&ctmp->path[5], 8083*4c06356bSdh strnlen(phyp->path, 32) - 5) == 0) { 8084*4c06356bSdh break; 8085*4c06356bSdh } 8086*4c06356bSdh } 8087*4c06356bSdh 8088*4c06356bSdh ctmp = ctmp->dead_next; 8089*4c06356bSdh } 8090*4c06356bSdh mutex_exit(&pwp->dead_phylist_lock); 8091*4c06356bSdh 8092*4c06356bSdh /* 8093*4c06356bSdh * Found a match. Remove the target linkage and drop the 8094*4c06356bSdh * ref count on the old PHY. Then, increment the ref count 8095*4c06356bSdh * on the new PHY to compensate. 8096*4c06356bSdh */ 8097*4c06356bSdh if (ctmp) { 8098*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 8099*4c06356bSdh "%s: Found match in dead PHY list for new PHY %s", 8100*4c06356bSdh __func__, phyp->path); 8101*4c06356bSdh if (ctmp->target) { 8102*4c06356bSdh /* 8103*4c06356bSdh * If there is a pointer to the target in the dead 8104*4c06356bSdh * PHY, and that PHY's ref_count drops to 0, we can 8105*4c06356bSdh * clear the target linkage now. If the PHY's 8106*4c06356bSdh * ref_count is > 1, then there may be multiple 8107*4c06356bSdh * LUNs still remaining, so leave the linkage. 8108*4c06356bSdh */ 8109*4c06356bSdh pmcs_inc_phy_ref_count(phyp); 8110*4c06356bSdh pmcs_dec_phy_ref_count(ctmp); 8111*4c06356bSdh phyp->target = ctmp->target; 8112*4c06356bSdh /* 8113*4c06356bSdh * Update the target's linkage as well 8114*4c06356bSdh */ 8115*4c06356bSdh mutex_enter(&phyp->target->statlock); 8116*4c06356bSdh phyp->target->phy = phyp; 8117*4c06356bSdh phyp->target->dtype = phyp->dtype; 8118*4c06356bSdh mutex_exit(&phyp->target->statlock); 8119*4c06356bSdh 8120*4c06356bSdh if (ctmp->ref_count == 0) { 8121*4c06356bSdh ctmp->target = NULL; 8122*4c06356bSdh } 8123*4c06356bSdh } 8124*4c06356bSdh } 8125*4c06356bSdh } 8126*4c06356bSdh 8127*4c06356bSdh /* 8128*4c06356bSdh * Called with iport lock held 8129*4c06356bSdh */ 8130*4c06356bSdh void 8131*4c06356bSdh pmcs_add_phy_to_iport(pmcs_iport_t *iport, pmcs_phy_t *phyp) 8132*4c06356bSdh { 8133*4c06356bSdh ASSERT(mutex_owned(&iport->lock)); 8134*4c06356bSdh ASSERT(phyp); 8135*4c06356bSdh ASSERT(!list_link_active(&phyp->list_node)); 8136*4c06356bSdh iport->nphy++; 8137*4c06356bSdh pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS, 8138*4c06356bSdh &iport->nphy); 8139*4c06356bSdh list_insert_tail(&iport->phys, phyp); 8140*4c06356bSdh mutex_enter(&iport->refcnt_lock); 8141*4c06356bSdh iport->refcnt++; 8142*4c06356bSdh mutex_exit(&iport->refcnt_lock); 8143*4c06356bSdh } 8144*4c06356bSdh 8145*4c06356bSdh /* 8146*4c06356bSdh * Called with the iport lock held 8147*4c06356bSdh */ 8148*4c06356bSdh void 8149*4c06356bSdh pmcs_remove_phy_from_iport(pmcs_iport_t *iport, pmcs_phy_t *phyp) 8150*4c06356bSdh { 8151*4c06356bSdh pmcs_phy_t *pptr, *next_pptr; 8152*4c06356bSdh 8153*4c06356bSdh ASSERT(mutex_owned(&iport->lock)); 8154*4c06356bSdh 8155*4c06356bSdh /* 8156*4c06356bSdh * If phyp is NULL, remove all PHYs from the iport 8157*4c06356bSdh */ 8158*4c06356bSdh if (phyp == NULL) { 8159*4c06356bSdh for (pptr = list_head(&iport->phys); pptr != NULL; 8160*4c06356bSdh pptr = next_pptr) { 8161*4c06356bSdh next_pptr = list_next(&iport->phys, pptr); 8162*4c06356bSdh mutex_enter(&pptr->phy_lock); 8163*4c06356bSdh pptr->iport = NULL; 8164*4c06356bSdh mutex_exit(&pptr->phy_lock); 8165*4c06356bSdh pmcs_rele_iport(iport); 8166*4c06356bSdh list_remove(&iport->phys, pptr); 8167*4c06356bSdh } 8168*4c06356bSdh iport->nphy = 0; 8169*4c06356bSdh return; 8170*4c06356bSdh } 8171*4c06356bSdh 8172*4c06356bSdh ASSERT(phyp); 8173*4c06356bSdh ASSERT(iport->nphy > 0); 8174*4c06356bSdh ASSERT(list_link_active(&phyp->list_node)); 8175*4c06356bSdh iport->nphy--; 8176*4c06356bSdh pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS, 8177*4c06356bSdh &iport->nphy); 8178*4c06356bSdh list_remove(&iport->phys, phyp); 8179*4c06356bSdh pmcs_rele_iport(iport); 8180*4c06356bSdh } 8181*4c06356bSdh 8182*4c06356bSdh /* 8183*4c06356bSdh * This function checks to see if the target pointed to by phyp is still 8184*4c06356bSdh * correct. This is done by comparing the target's unit address with the 8185*4c06356bSdh * SAS address in phyp. 8186*4c06356bSdh * 8187*4c06356bSdh * Called with PHY locked and target statlock held 8188*4c06356bSdh */ 8189*4c06356bSdh static boolean_t 8190*4c06356bSdh pmcs_phy_target_match(pmcs_phy_t *phyp) 8191*4c06356bSdh { 8192*4c06356bSdh uint64_t wwn; 8193*4c06356bSdh char unit_address[PMCS_MAX_UA_SIZE]; 8194*4c06356bSdh boolean_t rval = B_FALSE; 8195*4c06356bSdh 8196*4c06356bSdh ASSERT(phyp); 8197*4c06356bSdh ASSERT(phyp->target); 8198*4c06356bSdh ASSERT(mutex_owned(&phyp->phy_lock)); 8199*4c06356bSdh ASSERT(mutex_owned(&phyp->target->statlock)); 8200*4c06356bSdh 8201*4c06356bSdh wwn = pmcs_barray2wwn(phyp->sas_address); 8202*4c06356bSdh (void) scsi_wwn_to_wwnstr(wwn, 1, unit_address); 8203*4c06356bSdh 8204*4c06356bSdh if (memcmp((void *)unit_address, (void *)phyp->target->unit_address, 8205*4c06356bSdh strnlen(phyp->target->unit_address, PMCS_MAX_UA_SIZE)) == 0) { 8206*4c06356bSdh rval = B_TRUE; 8207*4c06356bSdh } 8208*4c06356bSdh 8209*4c06356bSdh return (rval); 8210*4c06356bSdh } 8211*4c06356bSdh 8212*4c06356bSdh void 8213*4c06356bSdh pmcs_start_dev_state_recovery(pmcs_xscsi_t *xp, pmcs_phy_t *phyp) 8214*4c06356bSdh { 8215*4c06356bSdh ASSERT(mutex_owned(&xp->statlock)); 8216*4c06356bSdh ASSERT(xp->pwp != NULL); 8217*4c06356bSdh 8218*4c06356bSdh if (xp->recover_wait == 0) { 8219*4c06356bSdh pmcs_prt(xp->pwp, PMCS_PRT_DEBUG_DEV_STATE, 8220*4c06356bSdh "%s: Start ds_recovery for tgt 0x%p/PHY 0x%p (%s)", 8221*4c06356bSdh __func__, (void *)xp, (void *)phyp, phyp->path); 8222*4c06356bSdh xp->recover_wait = 1; 8223*4c06356bSdh 8224*4c06356bSdh /* 8225*4c06356bSdh * Rather than waiting for the watchdog timer, we'll 8226*4c06356bSdh * kick it right now. 8227*4c06356bSdh */ 8228*4c06356bSdh SCHEDULE_WORK(xp->pwp, PMCS_WORK_DS_ERR_RECOVERY); 8229*4c06356bSdh (void) ddi_taskq_dispatch(xp->pwp->tq, pmcs_worker, xp->pwp, 8230*4c06356bSdh DDI_NOSLEEP); 8231*4c06356bSdh } 8232*4c06356bSdh } 8233*4c06356bSdh 8234*4c06356bSdh /* 8235*4c06356bSdh * Increment the phy ds error retry count. 8236*4c06356bSdh * If too many retries, mark phy dead and restart discovery; 8237*4c06356bSdh * otherwise schedule ds recovery. 8238*4c06356bSdh */ 8239*4c06356bSdh static void 8240*4c06356bSdh pmcs_handle_ds_recovery_error(pmcs_phy_t *phyp, pmcs_xscsi_t *tgt, 8241*4c06356bSdh pmcs_hw_t *pwp, const char *func_name, int line, char *reason_string) 8242*4c06356bSdh { 8243*4c06356bSdh ASSERT(mutex_owned(&phyp->phy_lock)); 8244*4c06356bSdh 8245*4c06356bSdh phyp->ds_recovery_retries++; 8246*4c06356bSdh 8247*4c06356bSdh if (phyp->ds_recovery_retries > PMCS_MAX_DS_RECOVERY_RETRIES) { 8248*4c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, 8249*4c06356bSdh "%s: retry limit reached after %s to PHY %s failed", 8250*4c06356bSdh func_name, reason_string, phyp->path); 8251*4c06356bSdh tgt->recover_wait = 0; 8252*4c06356bSdh phyp->dead = 1; 8253*4c06356bSdh PHY_CHANGED_AT_LOCATION(pwp, phyp, func_name, line); 8254*4c06356bSdh RESTART_DISCOVERY(pwp); 8255*4c06356bSdh } else { 8256*4c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_DS_ERR_RECOVERY); 8257*4c06356bSdh } 8258*4c06356bSdh } 8259