14c06356bSdh /* 24c06356bSdh * CDDL HEADER START 34c06356bSdh * 44c06356bSdh * The contents of this file are subject to the terms of the 54c06356bSdh * Common Development and Distribution License (the "License"). 64c06356bSdh * You may not use this file except in compliance with the License. 74c06356bSdh * 84c06356bSdh * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 94c06356bSdh * or http://www.opensolaris.org/os/licensing. 104c06356bSdh * See the License for the specific language governing permissions 114c06356bSdh * and limitations under the License. 124c06356bSdh * 134c06356bSdh * When distributing Covered Code, include this CDDL HEADER in each 144c06356bSdh * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 154c06356bSdh * If applicable, add the following below this CDDL HEADER, with the 164c06356bSdh * fields enclosed by brackets "[]" replaced with your own identifying 174c06356bSdh * information: Portions Copyright [yyyy] [name of copyright owner] 184c06356bSdh * 194c06356bSdh * CDDL HEADER END 20658280b6SDavid Hollister */ 21658280b6SDavid Hollister /* 22658280b6SDavid Hollister * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 234c06356bSdh */ 244c06356bSdh 254c06356bSdh /* 264c06356bSdh * This file contains various support routines. 274c06356bSdh */ 284c06356bSdh 294c06356bSdh #include <sys/scsi/adapters/pmcs/pmcs.h> 304c06356bSdh 314c06356bSdh /* 324c06356bSdh * Local static data 334c06356bSdh */ 344c06356bSdh static int tgtmap_usec = MICROSEC; 354c06356bSdh 364c06356bSdh /* 374c06356bSdh * SAS Topology Configuration 384c06356bSdh */ 394c06356bSdh static void pmcs_new_tport(pmcs_hw_t *, pmcs_phy_t *); 404c06356bSdh static void pmcs_configure_expander(pmcs_hw_t *, pmcs_phy_t *, pmcs_iport_t *); 414c06356bSdh 4273a3eccdSDavid Hollister static void pmcs_check_expanders(pmcs_hw_t *, pmcs_phy_t *); 434c06356bSdh static void pmcs_check_expander(pmcs_hw_t *, pmcs_phy_t *); 444c06356bSdh static void pmcs_clear_expander(pmcs_hw_t *, pmcs_phy_t *, int); 454c06356bSdh 464c06356bSdh static int pmcs_expander_get_nphy(pmcs_hw_t *, pmcs_phy_t *); 474c06356bSdh static int pmcs_expander_content_discover(pmcs_hw_t *, pmcs_phy_t *, 484c06356bSdh pmcs_phy_t *); 494c06356bSdh 504c06356bSdh static int pmcs_smp_function_result(pmcs_hw_t *, smp_response_frame_t *); 514c06356bSdh static boolean_t pmcs_validate_devid(pmcs_phy_t *, pmcs_phy_t *, uint32_t); 524c06356bSdh static void pmcs_clear_phys(pmcs_hw_t *, pmcs_phy_t *); 534c06356bSdh static int pmcs_configure_new_devices(pmcs_hw_t *, pmcs_phy_t *); 54601c90f1SSrikanth, Ramana static void pmcs_begin_observations(pmcs_hw_t *); 550b53804eSReed static void pmcs_flush_observations(pmcs_hw_t *); 564c06356bSdh static boolean_t pmcs_report_observations(pmcs_hw_t *); 574c06356bSdh static boolean_t pmcs_report_iport_observations(pmcs_hw_t *, pmcs_iport_t *, 584c06356bSdh pmcs_phy_t *); 594c06356bSdh static pmcs_phy_t *pmcs_find_phy_needing_work(pmcs_hw_t *, pmcs_phy_t *); 604c06356bSdh static int pmcs_kill_devices(pmcs_hw_t *, pmcs_phy_t *); 614c06356bSdh static void pmcs_lock_phy_impl(pmcs_phy_t *, int); 624c06356bSdh static void pmcs_unlock_phy_impl(pmcs_phy_t *, int); 634c06356bSdh static pmcs_phy_t *pmcs_clone_phy(pmcs_phy_t *); 644c06356bSdh static boolean_t pmcs_configure_phy(pmcs_hw_t *, pmcs_phy_t *); 654c06356bSdh static void pmcs_reap_dead_phy(pmcs_phy_t *); 664c06356bSdh static pmcs_iport_t *pmcs_get_iport_by_ua(pmcs_hw_t *, char *); 674c06356bSdh static boolean_t pmcs_phy_target_match(pmcs_phy_t *); 689aed1621SDavid Hollister static void pmcs_iport_active(pmcs_iport_t *); 699aed1621SDavid Hollister static void pmcs_tgtmap_activate_cb(void *, char *, scsi_tgtmap_tgt_type_t, 709aed1621SDavid Hollister void **); 719aed1621SDavid Hollister static boolean_t pmcs_tgtmap_deactivate_cb(void *, char *, 729aed1621SDavid Hollister scsi_tgtmap_tgt_type_t, void *, scsi_tgtmap_deact_rsn_t); 739aed1621SDavid Hollister static void pmcs_add_dead_phys(pmcs_hw_t *, pmcs_phy_t *); 742ac4abe8SDavid Hollister static void pmcs_get_fw_version(pmcs_hw_t *); 75*1f81b464SDavid Hollister static int pmcs_get_time_stamp(pmcs_hw_t *, uint64_t *, hrtime_t *); 764c06356bSdh 774c06356bSdh /* 784c06356bSdh * Often used strings 794c06356bSdh */ 804c06356bSdh const char pmcs_nowrk[] = "%s: unable to get work structure"; 814c06356bSdh const char pmcs_nomsg[] = "%s: unable to get Inbound Message entry"; 826745c559SJesse Butler const char pmcs_timeo[] = "%s: command timed out"; 834c06356bSdh 844c06356bSdh extern const ddi_dma_attr_t pmcs_dattr; 85*1f81b464SDavid Hollister extern kmutex_t pmcs_trace_lock; 864c06356bSdh 874c06356bSdh /* 884c06356bSdh * Some Initial setup steps. 894c06356bSdh */ 904c06356bSdh 914c06356bSdh int 924c06356bSdh pmcs_setup(pmcs_hw_t *pwp) 934c06356bSdh { 944c06356bSdh uint32_t barval = pwp->mpibar; 954c06356bSdh uint32_t i, scratch, regbar, regoff, barbar, baroff; 964c06356bSdh uint32_t new_ioq_depth, ferr = 0; 974c06356bSdh 984c06356bSdh /* 994c06356bSdh * Check current state. If we're not at READY state, 1004c06356bSdh * we can't go further. 1014c06356bSdh */ 1024c06356bSdh scratch = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1); 1034c06356bSdh if ((scratch & PMCS_MSGU_AAP_STATE_MASK) == PMCS_MSGU_AAP_STATE_ERROR) { 104c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 105c3bc407cSdh "%s: AAP Error State (0x%x)", 1064c06356bSdh __func__, pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) & 1074c06356bSdh PMCS_MSGU_AAP_ERROR_MASK); 1084c06356bSdh pmcs_fm_ereport(pwp, DDI_FM_DEVICE_INVAL_STATE); 1094c06356bSdh ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST); 1104c06356bSdh return (-1); 1114c06356bSdh } 1124c06356bSdh if ((scratch & PMCS_MSGU_AAP_STATE_MASK) != PMCS_MSGU_AAP_STATE_READY) { 113c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1144c06356bSdh "%s: AAP unit not ready (state 0x%x)", 1154c06356bSdh __func__, scratch & PMCS_MSGU_AAP_STATE_MASK); 1164c06356bSdh pmcs_fm_ereport(pwp, DDI_FM_DEVICE_INVAL_STATE); 1174c06356bSdh ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST); 1184c06356bSdh return (-1); 1194c06356bSdh } 1204c06356bSdh 1214c06356bSdh /* 1224c06356bSdh * Read the offset from the Message Unit scratchpad 0 register. 1234c06356bSdh * This allows us to read the MPI Configuration table. 1244c06356bSdh * 1254c06356bSdh * Check its signature for validity. 1264c06356bSdh */ 1274c06356bSdh baroff = barval; 1284c06356bSdh barbar = barval >> PMCS_MSGU_MPI_BAR_SHIFT; 1294c06356bSdh baroff &= PMCS_MSGU_MPI_OFFSET_MASK; 1304c06356bSdh 1314c06356bSdh regoff = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH0); 1324c06356bSdh regbar = regoff >> PMCS_MSGU_MPI_BAR_SHIFT; 1334c06356bSdh regoff &= PMCS_MSGU_MPI_OFFSET_MASK; 1344c06356bSdh 1354c06356bSdh if (regoff > baroff) { 136c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 137c3bc407cSdh "%s: bad MPI Table Length (register offset=0x%08x, " 138c3bc407cSdh "passed offset=0x%08x)", __func__, regoff, baroff); 1394c06356bSdh return (-1); 1404c06356bSdh } 1414c06356bSdh if (regbar != barbar) { 142c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 143c3bc407cSdh "%s: bad MPI BAR (register BAROFF=0x%08x, " 144c3bc407cSdh "passed BAROFF=0x%08x)", __func__, regbar, barbar); 1454c06356bSdh return (-1); 1464c06356bSdh } 1474c06356bSdh pwp->mpi_offset = regoff; 1484c06356bSdh if (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_AS) != PMCS_SIGNATURE) { 149c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1504c06356bSdh "%s: Bad MPI Configuration Table Signature 0x%x", __func__, 1514c06356bSdh pmcs_rd_mpi_tbl(pwp, PMCS_MPI_AS)); 1524c06356bSdh return (-1); 1534c06356bSdh } 1544c06356bSdh 1554c06356bSdh if (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IR) != PMCS_MPI_REVISION1) { 156c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1574c06356bSdh "%s: Bad MPI Configuration Revision 0x%x", __func__, 1584c06356bSdh pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IR)); 1594c06356bSdh return (-1); 1604c06356bSdh } 1614c06356bSdh 1624c06356bSdh /* 1634c06356bSdh * Generate offsets for the General System, Inbound Queue Configuration 1644c06356bSdh * and Outbound Queue configuration tables. This way the macros to 1654c06356bSdh * access those tables will work correctly. 1664c06356bSdh */ 1674c06356bSdh pwp->mpi_gst_offset = 1684c06356bSdh pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_GSTO); 1694c06356bSdh pwp->mpi_iqc_offset = 1704c06356bSdh pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IQCTO); 1714c06356bSdh pwp->mpi_oqc_offset = 1724c06356bSdh pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_OQCTO); 1734c06356bSdh 1742ac4abe8SDavid Hollister pmcs_get_fw_version(pwp); 1754c06356bSdh 1764c06356bSdh pwp->max_cmd = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_MOIO); 1774c06356bSdh pwp->max_dev = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO0) >> 16; 1784c06356bSdh 1794c06356bSdh pwp->max_iq = PMCS_MNIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1)); 1804c06356bSdh pwp->max_oq = PMCS_MNOQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1)); 1814c06356bSdh pwp->nphy = PMCS_NPHY(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1)); 1824c06356bSdh if (pwp->max_iq <= PMCS_NIQ) { 183c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 184c3bc407cSdh "%s: not enough Inbound Queues supported " 185c3bc407cSdh "(need %d, max_oq=%d)", __func__, pwp->max_iq, PMCS_NIQ); 1864c06356bSdh return (-1); 1874c06356bSdh } 1884c06356bSdh if (pwp->max_oq <= PMCS_NOQ) { 189c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 190c3bc407cSdh "%s: not enough Outbound Queues supported " 191c3bc407cSdh "(need %d, max_oq=%d)", __func__, pwp->max_oq, PMCS_NOQ); 1924c06356bSdh return (-1); 1934c06356bSdh } 1944c06356bSdh if (pwp->nphy == 0) { 195c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 196c3bc407cSdh "%s: zero phys reported", __func__); 1974c06356bSdh return (-1); 1984c06356bSdh } 1994c06356bSdh if (PMCS_HPIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1))) { 2004c06356bSdh pwp->hipri_queue = (1 << PMCS_IQ_OTHER); 2014c06356bSdh } 2024c06356bSdh 2034c06356bSdh 2044c06356bSdh for (i = 0; i < pwp->nphy; i++) { 2054c06356bSdh PMCS_MPI_EVQSET(pwp, PMCS_OQ_EVENTS, i); 2064c06356bSdh PMCS_MPI_NCQSET(pwp, PMCS_OQ_EVENTS, i); 2074c06356bSdh } 2084c06356bSdh 2094c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_INFO2, 2104c06356bSdh (PMCS_OQ_EVENTS << GENERAL_EVENT_OQ_SHIFT) | 2114c06356bSdh (PMCS_OQ_EVENTS << DEVICE_HANDLE_REMOVED_SHIFT)); 2124c06356bSdh 2134c06356bSdh /* 2144c06356bSdh * Verify that ioq_depth is valid (> 0 and not so high that it 2154c06356bSdh * would cause us to overrun the chip with commands). 2164c06356bSdh */ 2174c06356bSdh if (pwp->ioq_depth == 0) { 218c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2194c06356bSdh "%s: I/O queue depth set to 0. Setting to %d", 2204c06356bSdh __func__, PMCS_NQENTRY); 2214c06356bSdh pwp->ioq_depth = PMCS_NQENTRY; 2224c06356bSdh } 2234c06356bSdh 2244c06356bSdh if (pwp->ioq_depth < PMCS_MIN_NQENTRY) { 225c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2264c06356bSdh "%s: I/O queue depth set too low (%d). Setting to %d", 2274c06356bSdh __func__, pwp->ioq_depth, PMCS_MIN_NQENTRY); 2284c06356bSdh pwp->ioq_depth = PMCS_MIN_NQENTRY; 2294c06356bSdh } 2304c06356bSdh 2314c06356bSdh if (pwp->ioq_depth > (pwp->max_cmd / (PMCS_IO_IQ_MASK + 1))) { 2324c06356bSdh new_ioq_depth = pwp->max_cmd / (PMCS_IO_IQ_MASK + 1); 233c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2344c06356bSdh "%s: I/O queue depth set too high (%d). Setting to %d", 2354c06356bSdh __func__, pwp->ioq_depth, new_ioq_depth); 2364c06356bSdh pwp->ioq_depth = new_ioq_depth; 2374c06356bSdh } 2384c06356bSdh 2394c06356bSdh /* 2404c06356bSdh * Allocate consistent memory for OQs and IQs. 2414c06356bSdh */ 2424c06356bSdh pwp->iqp_dma_attr = pwp->oqp_dma_attr = pmcs_dattr; 2434c06356bSdh pwp->iqp_dma_attr.dma_attr_align = 2444c06356bSdh pwp->oqp_dma_attr.dma_attr_align = PMCS_QENTRY_SIZE; 2454c06356bSdh 2464c06356bSdh /* 2474c06356bSdh * The Rev C chip has the ability to do PIO to or from consistent 2484c06356bSdh * memory anywhere in a 64 bit address space, but the firmware is 2494c06356bSdh * not presently set up to do so. 2504c06356bSdh */ 2514c06356bSdh pwp->iqp_dma_attr.dma_attr_addr_hi = 2524c06356bSdh pwp->oqp_dma_attr.dma_attr_addr_hi = 0x000000FFFFFFFFFFull; 2534c06356bSdh 2544c06356bSdh for (i = 0; i < PMCS_NIQ; i++) { 2554c06356bSdh if (pmcs_dma_setup(pwp, &pwp->iqp_dma_attr, 2564c06356bSdh &pwp->iqp_acchdls[i], 2574c06356bSdh &pwp->iqp_handles[i], PMCS_QENTRY_SIZE * pwp->ioq_depth, 2584c06356bSdh (caddr_t *)&pwp->iqp[i], &pwp->iqaddr[i]) == B_FALSE) { 259c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2604c06356bSdh "Failed to setup DMA for iqp[%d]", i); 2614c06356bSdh return (-1); 2624c06356bSdh } 2634c06356bSdh bzero(pwp->iqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth); 2644c06356bSdh } 2654c06356bSdh 2664c06356bSdh for (i = 0; i < PMCS_NOQ; i++) { 2674c06356bSdh if (pmcs_dma_setup(pwp, &pwp->oqp_dma_attr, 2684c06356bSdh &pwp->oqp_acchdls[i], 2694c06356bSdh &pwp->oqp_handles[i], PMCS_QENTRY_SIZE * pwp->ioq_depth, 2704c06356bSdh (caddr_t *)&pwp->oqp[i], &pwp->oqaddr[i]) == B_FALSE) { 271c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2724c06356bSdh "Failed to setup DMA for oqp[%d]", i); 2734c06356bSdh return (-1); 2744c06356bSdh } 2754c06356bSdh bzero(pwp->oqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth); 2764c06356bSdh } 2774c06356bSdh 2784c06356bSdh /* 2794c06356bSdh * Install the IQ and OQ addresses (and null out the rest). 2804c06356bSdh */ 2814c06356bSdh for (i = 0; i < pwp->max_iq; i++) { 2824c06356bSdh pwp->iqpi_offset[i] = pmcs_rd_iqc_tbl(pwp, PMCS_IQPIOFFX(i)); 2834c06356bSdh if (i < PMCS_NIQ) { 2844c06356bSdh if (i != PMCS_IQ_OTHER) { 2854c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 2864c06356bSdh pwp->ioq_depth | (PMCS_QENTRY_SIZE << 16)); 2874c06356bSdh } else { 2884c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 2894c06356bSdh (1 << 30) | pwp->ioq_depth | 2904c06356bSdh (PMCS_QENTRY_SIZE << 16)); 2914c06356bSdh } 2924c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 2934c06356bSdh DWORD1(pwp->iqaddr[i])); 2944c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 2954c06356bSdh DWORD0(pwp->iqaddr[i])); 2964c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 2974c06356bSdh DWORD1(pwp->ciaddr+IQ_OFFSET(i))); 2984c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 2994c06356bSdh DWORD0(pwp->ciaddr+IQ_OFFSET(i))); 3004c06356bSdh } else { 3014c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 0); 3024c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 0); 3034c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 0); 3044c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 0); 3054c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 0); 3064c06356bSdh } 3074c06356bSdh } 3084c06356bSdh 3094c06356bSdh for (i = 0; i < pwp->max_oq; i++) { 3104c06356bSdh pwp->oqci_offset[i] = pmcs_rd_oqc_tbl(pwp, PMCS_OQCIOFFX(i)); 3114c06356bSdh if (i < PMCS_NOQ) { 3124c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), pwp->ioq_depth | 3134c06356bSdh (PMCS_QENTRY_SIZE << 16) | OQIEX); 3144c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 3154c06356bSdh DWORD1(pwp->oqaddr[i])); 3164c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 3174c06356bSdh DWORD0(pwp->oqaddr[i])); 3184c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 3194c06356bSdh DWORD1(pwp->ciaddr+OQ_OFFSET(i))); 3204c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 3214c06356bSdh DWORD0(pwp->ciaddr+OQ_OFFSET(i))); 3224c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 3234c06356bSdh pwp->oqvec[i] << 24); 3244c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0); 3254c06356bSdh } else { 3264c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), 0); 3274c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 0); 3284c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 0); 3294c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 0); 3304c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 0); 3314c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 0); 3324c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0); 3334c06356bSdh } 3344c06356bSdh } 3354c06356bSdh 3364c06356bSdh /* 3374c06356bSdh * Set up logging, if defined. 3384c06356bSdh */ 3394c06356bSdh if (pwp->fwlog) { 3404c06356bSdh uint64_t logdma = pwp->fwaddr; 3414c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBAH, DWORD1(logdma)); 3424c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBAL, DWORD0(logdma)); 3434c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBS, PMCS_FWLOG_SIZE >> 1); 3444c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELSEV, pwp->fwlog); 3454c06356bSdh logdma += (PMCS_FWLOG_SIZE >> 1); 3464c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBAH, DWORD1(logdma)); 3474c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBAL, DWORD0(logdma)); 3484c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBS, PMCS_FWLOG_SIZE >> 1); 3494c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELSEV, pwp->fwlog); 3504c06356bSdh } 3514c06356bSdh 3524c06356bSdh /* 3534c06356bSdh * Interrupt vectors, outbound queues, and odb_auto_clear 3544c06356bSdh * 3554c06356bSdh * MSI/MSI-X: 3564c06356bSdh * If we got 4 interrupt vectors, we'll assign one to each outbound 3574c06356bSdh * queue as well as the fatal interrupt, and auto clear can be set 3584c06356bSdh * for each. 3594c06356bSdh * 3604c06356bSdh * If we only got 2 vectors, one will be used for I/O completions 3614c06356bSdh * and the other for the other two vectors. In this case, auto_ 3624c06356bSdh * clear can only be set for I/Os, which is fine. The fatal 3634c06356bSdh * interrupt will be mapped to the PMCS_FATAL_INTERRUPT bit, which 3644c06356bSdh * is not an interrupt vector. 3654c06356bSdh * 3664c06356bSdh * MSI/MSI-X/INT-X: 3674c06356bSdh * If we only got 1 interrupt vector, auto_clear must be set to 0, 3684c06356bSdh * and again the fatal interrupt will be mapped to the 3694c06356bSdh * PMCS_FATAL_INTERRUPT bit (again, not an interrupt vector). 3704c06356bSdh */ 3714c06356bSdh 3724c06356bSdh switch (pwp->int_type) { 3734c06356bSdh case PMCS_INT_MSIX: 3744c06356bSdh case PMCS_INT_MSI: 3754c06356bSdh switch (pwp->intr_cnt) { 3764c06356bSdh case 1: 3774c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE | 3784c06356bSdh (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT)); 3794c06356bSdh pwp->odb_auto_clear = 0; 3804c06356bSdh break; 3814c06356bSdh case 2: 3824c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE | 3834c06356bSdh (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT)); 3844c06356bSdh pwp->odb_auto_clear = (1 << PMCS_FATAL_INTERRUPT) | 3854c06356bSdh (1 << PMCS_MSIX_IODONE); 3864c06356bSdh break; 3874c06356bSdh case 4: 3884c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE | 3894c06356bSdh (PMCS_MSIX_FATAL << PMCS_FERIV_SHIFT)); 3904c06356bSdh pwp->odb_auto_clear = (1 << PMCS_MSIX_FATAL) | 3914c06356bSdh (1 << PMCS_MSIX_GENERAL) | (1 << PMCS_MSIX_IODONE) | 3924c06356bSdh (1 << PMCS_MSIX_EVENTS); 3934c06356bSdh break; 3944c06356bSdh } 3954c06356bSdh break; 3964c06356bSdh 3974c06356bSdh case PMCS_INT_FIXED: 3984c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, 3994c06356bSdh PMCS_FERRIE | (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT)); 4004c06356bSdh pwp->odb_auto_clear = 0; 4014c06356bSdh break; 4024c06356bSdh } 4034c06356bSdh 404658280b6SDavid Hollister /* 405658280b6SDavid Hollister * If the open retry interval is non-zero, set it. 406658280b6SDavid Hollister */ 407658280b6SDavid Hollister if (pwp->open_retry_interval != 0) { 408658280b6SDavid Hollister int phynum; 409658280b6SDavid Hollister 410658280b6SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 411658280b6SDavid Hollister "%s: Setting open retry interval to %d usecs", __func__, 412658280b6SDavid Hollister pwp->open_retry_interval); 413658280b6SDavid Hollister for (phynum = 0; phynum < pwp->nphy; phynum ++) { 414658280b6SDavid Hollister pmcs_wr_gsm_reg(pwp, OPEN_RETRY_INTERVAL(phynum), 415658280b6SDavid Hollister pwp->open_retry_interval); 416658280b6SDavid Hollister } 417658280b6SDavid Hollister } 418658280b6SDavid Hollister 4194c06356bSdh /* 4204c06356bSdh * Enable Interrupt Reassertion 4214c06356bSdh * Default Delay 1000us 4224c06356bSdh */ 4234c06356bSdh ferr = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_FERR); 4244c06356bSdh if ((ferr & PMCS_MPI_IRAE) == 0) { 4254c06356bSdh ferr &= ~(PMCS_MPI_IRAU | PMCS_MPI_IRAD_MASK); 4264c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, ferr | PMCS_MPI_IRAE); 4274c06356bSdh } 4284c06356bSdh 4294c06356bSdh pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR, pwp->odb_auto_clear); 4304c06356bSdh pwp->mpi_table_setup = 1; 4314c06356bSdh return (0); 4324c06356bSdh } 4334c06356bSdh 4344c06356bSdh /* 4354c06356bSdh * Start the Message Passing protocol with the PMC chip. 4364c06356bSdh */ 4374c06356bSdh int 4384c06356bSdh pmcs_start_mpi(pmcs_hw_t *pwp) 4394c06356bSdh { 4404c06356bSdh int i; 4414c06356bSdh 4424c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_IBDB, PMCS_MSGU_IBDB_MPIINI); 4434c06356bSdh for (i = 0; i < 1000; i++) { 4444c06356bSdh if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & 4454c06356bSdh PMCS_MSGU_IBDB_MPIINI) == 0) { 4464c06356bSdh break; 4474c06356bSdh } 4484c06356bSdh drv_usecwait(1000); 4494c06356bSdh } 4504c06356bSdh if (pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & PMCS_MSGU_IBDB_MPIINI) { 4514c06356bSdh return (-1); 4524c06356bSdh } 4534c06356bSdh drv_usecwait(500000); 4544c06356bSdh 4554c06356bSdh /* 4564c06356bSdh * Check to make sure we got to INIT state. 4574c06356bSdh */ 4584c06356bSdh if (PMCS_MPI_S(pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE)) != 4594c06356bSdh PMCS_MPI_STATE_INIT) { 460c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 461c3bc407cSdh "%s: MPI launch failed (GST 0x%x DBCLR 0x%x)", __func__, 4624c06356bSdh pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE), 4634c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB_CLEAR)); 4644c06356bSdh return (-1); 4654c06356bSdh } 4664c06356bSdh return (0); 4674c06356bSdh } 4684c06356bSdh 4694c06356bSdh /* 4704c06356bSdh * Stop the Message Passing protocol with the PMC chip. 4714c06356bSdh */ 4724c06356bSdh int 4734c06356bSdh pmcs_stop_mpi(pmcs_hw_t *pwp) 4744c06356bSdh { 4754c06356bSdh int i; 4764c06356bSdh 4774c06356bSdh for (i = 0; i < pwp->max_iq; i++) { 4784c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 0); 4794c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 0); 4804c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 0); 4814c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 0); 4824c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 0); 4834c06356bSdh } 4844c06356bSdh for (i = 0; i < pwp->max_oq; i++) { 4854c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), 0); 4864c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 0); 4874c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 0); 4884c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 0); 4894c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 0); 4904c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 0); 4914c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0); 4924c06356bSdh } 4934c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, 0); 4944c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_IBDB, PMCS_MSGU_IBDB_MPICTU); 4954c06356bSdh for (i = 0; i < 2000; i++) { 4964c06356bSdh if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & 4974c06356bSdh PMCS_MSGU_IBDB_MPICTU) == 0) { 4984c06356bSdh break; 4994c06356bSdh } 5004c06356bSdh drv_usecwait(1000); 5014c06356bSdh } 5024c06356bSdh if (pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & PMCS_MSGU_IBDB_MPICTU) { 503c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 504c3bc407cSdh "%s: MPI stop failed", __func__); 5054c06356bSdh return (-1); 5064c06356bSdh } 5074c06356bSdh return (0); 5084c06356bSdh } 5094c06356bSdh 5104c06356bSdh /* 5114c06356bSdh * Do a sequence of ECHO messages to test for MPI functionality, 5124c06356bSdh * all inbound and outbound queue functionality and interrupts. 5134c06356bSdh */ 5144c06356bSdh int 5154c06356bSdh pmcs_echo_test(pmcs_hw_t *pwp) 5164c06356bSdh { 5174c06356bSdh echo_test_t fred; 5184c06356bSdh struct pmcwork *pwrk; 5194c06356bSdh uint32_t *msg, count; 5204c06356bSdh int iqe = 0, iqo = 0, result, rval = 0; 5214c06356bSdh int iterations; 5224c06356bSdh hrtime_t echo_start, echo_end, echo_total; 5234c06356bSdh 5244c06356bSdh ASSERT(pwp->max_cmd > 0); 5254c06356bSdh 5264c06356bSdh /* 5274c06356bSdh * We want iterations to be max_cmd * 3 to ensure that we run the 5284c06356bSdh * echo test enough times to iterate through every inbound queue 5294c06356bSdh * at least twice. 5304c06356bSdh */ 5314c06356bSdh iterations = pwp->max_cmd * 3; 5324c06356bSdh 5334c06356bSdh echo_total = 0; 5344c06356bSdh count = 0; 5354c06356bSdh 5364c06356bSdh while (count < iterations) { 5374c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL); 5384c06356bSdh if (pwrk == NULL) { 539c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 540c3bc407cSdh pmcs_nowrk, __func__); 5414c06356bSdh rval = -1; 5424c06356bSdh break; 5434c06356bSdh } 5444c06356bSdh 5454c06356bSdh mutex_enter(&pwp->iqp_lock[iqe]); 5464c06356bSdh msg = GET_IQ_ENTRY(pwp, iqe); 5474c06356bSdh if (msg == NULL) { 5484c06356bSdh mutex_exit(&pwp->iqp_lock[iqe]); 5494c06356bSdh pmcs_pwork(pwp, pwrk); 550c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 551c3bc407cSdh pmcs_nomsg, __func__); 5524c06356bSdh rval = -1; 5534c06356bSdh break; 5544c06356bSdh } 5554c06356bSdh 5564c06356bSdh bzero(msg, PMCS_QENTRY_SIZE); 5574c06356bSdh 5584c06356bSdh if (iqe == PMCS_IQ_OTHER) { 5594c06356bSdh /* This is on the high priority queue */ 5604c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, iqo, PMCIN_ECHO)); 5614c06356bSdh } else { 5624c06356bSdh msg[0] = LE_32(PMCS_IOMB_IN_SAS(iqo, PMCIN_ECHO)); 5634c06356bSdh } 5644c06356bSdh msg[1] = LE_32(pwrk->htag); 5654c06356bSdh fred.signature = 0xdeadbeef; 5664c06356bSdh fred.count = count; 5674c06356bSdh fred.ptr = &count; 5684c06356bSdh (void) memcpy(&msg[2], &fred, sizeof (fred)); 5694c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 5704c06356bSdh 5714c06356bSdh INC_IQ_ENTRY(pwp, iqe); 5724c06356bSdh 5734c06356bSdh echo_start = gethrtime(); 5744c06356bSdh DTRACE_PROBE2(pmcs__echo__test__wait__start, 5754c06356bSdh hrtime_t, echo_start, uint32_t, pwrk->htag); 5764c06356bSdh 5774c06356bSdh if (++iqe == PMCS_NIQ) { 5784c06356bSdh iqe = 0; 5794c06356bSdh } 5804c06356bSdh if (++iqo == PMCS_NOQ) { 5814c06356bSdh iqo = 0; 5824c06356bSdh } 5834c06356bSdh 5844c06356bSdh WAIT_FOR(pwrk, 250, result); 5854c06356bSdh 5864c06356bSdh echo_end = gethrtime(); 5874c06356bSdh DTRACE_PROBE2(pmcs__echo__test__wait__end, 5884c06356bSdh hrtime_t, echo_end, int, result); 5894c06356bSdh 5904c06356bSdh echo_total += (echo_end - echo_start); 5914c06356bSdh 5924c06356bSdh pmcs_pwork(pwp, pwrk); 5934c06356bSdh if (result) { 594c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 5954c06356bSdh "%s: command timed out on echo test #%d", 5964c06356bSdh __func__, count); 5974c06356bSdh rval = -1; 5984c06356bSdh break; 5994c06356bSdh } 6004c06356bSdh } 6014c06356bSdh 6024c06356bSdh /* 6034c06356bSdh * The intr_threshold is adjusted by PMCS_INTR_THRESHOLD in order to 6044c06356bSdh * remove the overhead of things like the delay in getting signaled 6054c06356bSdh * for completion. 6064c06356bSdh */ 6074c06356bSdh if (echo_total != 0) { 6084c06356bSdh pwp->io_intr_coal.intr_latency = 6094c06356bSdh (echo_total / iterations) / 2; 6104c06356bSdh pwp->io_intr_coal.intr_threshold = 6114c06356bSdh PMCS_INTR_THRESHOLD(PMCS_QUANTUM_TIME_USECS * 1000 / 6124c06356bSdh pwp->io_intr_coal.intr_latency); 6134c06356bSdh } 6144c06356bSdh 6154c06356bSdh return (rval); 6164c06356bSdh } 6174c06356bSdh 6184c06356bSdh /* 6194c06356bSdh * Start the (real) phys 6204c06356bSdh */ 6214c06356bSdh int 6224c06356bSdh pmcs_start_phy(pmcs_hw_t *pwp, int phynum, int linkmode, int speed) 6234c06356bSdh { 6244c06356bSdh int result; 6254c06356bSdh uint32_t *msg; 6264c06356bSdh struct pmcwork *pwrk; 6274c06356bSdh pmcs_phy_t *pptr; 6284c06356bSdh sas_identify_af_t sap; 6294c06356bSdh 6304c06356bSdh mutex_enter(&pwp->lock); 6314c06356bSdh pptr = pwp->root_phys + phynum; 6324c06356bSdh if (pptr == NULL) { 6334c06356bSdh mutex_exit(&pwp->lock); 634c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 635c3bc407cSdh "%s: cannot find port %d", __func__, phynum); 6364c06356bSdh return (0); 6374c06356bSdh } 6384c06356bSdh 6394c06356bSdh pmcs_lock_phy(pptr); 6404c06356bSdh mutex_exit(&pwp->lock); 6414c06356bSdh 6424c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 6434c06356bSdh if (pwrk == NULL) { 6444c06356bSdh pmcs_unlock_phy(pptr); 645c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__); 6464c06356bSdh return (-1); 6474c06356bSdh } 6484c06356bSdh 6494c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 6504c06356bSdh msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 6514c06356bSdh 6524c06356bSdh if (msg == NULL) { 6534c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 6544c06356bSdh pmcs_unlock_phy(pptr); 6554c06356bSdh pmcs_pwork(pwp, pwrk); 656c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__); 6574c06356bSdh return (-1); 6584c06356bSdh } 6594c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_PHY_START)); 6604c06356bSdh msg[1] = LE_32(pwrk->htag); 6614c06356bSdh msg[2] = LE_32(linkmode | speed | phynum); 6624c06356bSdh bzero(&sap, sizeof (sap)); 6634c06356bSdh sap.device_type = SAS_IF_DTYPE_ENDPOINT; 6644c06356bSdh sap.ssp_ini_port = 1; 6654c06356bSdh 6664c06356bSdh if (pwp->separate_ports) { 6674c06356bSdh pmcs_wwn2barray(pwp->sas_wwns[phynum], sap.sas_address); 6684c06356bSdh } else { 6694c06356bSdh pmcs_wwn2barray(pwp->sas_wwns[0], sap.sas_address); 6704c06356bSdh } 6714c06356bSdh 6724c06356bSdh ASSERT(phynum < SAS2_PHYNUM_MAX); 6734c06356bSdh sap.phy_identifier = phynum & SAS2_PHYNUM_MASK; 6744c06356bSdh (void) memcpy(&msg[3], &sap, sizeof (sas_identify_af_t)); 6754c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 6764c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 6774c06356bSdh 6784c06356bSdh pptr->state.prog_min_rate = (lowbit((ulong_t)speed) - 1); 6794c06356bSdh pptr->state.prog_max_rate = (highbit((ulong_t)speed) - 1); 6804c06356bSdh pptr->state.hw_min_rate = PMCS_HW_MIN_LINK_RATE; 6814c06356bSdh pptr->state.hw_max_rate = PMCS_HW_MAX_LINK_RATE; 6824c06356bSdh 6834c06356bSdh pmcs_unlock_phy(pptr); 6844c06356bSdh WAIT_FOR(pwrk, 1000, result); 6854c06356bSdh pmcs_pwork(pwp, pwrk); 6864c06356bSdh 6874c06356bSdh if (result) { 6886745c559SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__); 6894c06356bSdh } else { 6904c06356bSdh mutex_enter(&pwp->lock); 6914c06356bSdh pwp->phys_started |= (1 << phynum); 6924c06356bSdh mutex_exit(&pwp->lock); 6934c06356bSdh } 6944c06356bSdh 6954c06356bSdh return (0); 6964c06356bSdh } 6974c06356bSdh 6984c06356bSdh int 6994c06356bSdh pmcs_start_phys(pmcs_hw_t *pwp) 7004c06356bSdh { 701*1f81b464SDavid Hollister int i, rval; 7024c06356bSdh 7034c06356bSdh for (i = 0; i < pwp->nphy; i++) { 7044c06356bSdh if ((pwp->phyid_block_mask & (1 << i)) == 0) { 7054c06356bSdh if (pmcs_start_phy(pwp, i, 7064c06356bSdh (pwp->phymode << PHY_MODE_SHIFT), 7074c06356bSdh pwp->physpeed << PHY_LINK_SHIFT)) { 7084c06356bSdh return (-1); 7094c06356bSdh } 7104c06356bSdh if (pmcs_clear_diag_counters(pwp, i)) { 711c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 712c3bc407cSdh "%s: failed to reset counters on PHY (%d)", 713c3bc407cSdh __func__, i); 7144c06356bSdh } 7154c06356bSdh } 7164c06356bSdh } 717*1f81b464SDavid Hollister 718*1f81b464SDavid Hollister rval = pmcs_get_time_stamp(pwp, &pwp->fw_timestamp, &pwp->hrtimestamp); 719*1f81b464SDavid Hollister if (rval) { 720*1f81b464SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 721*1f81b464SDavid Hollister "%s: Failed to obtain firmware timestamp", __func__); 722*1f81b464SDavid Hollister } else { 723*1f81b464SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 724*1f81b464SDavid Hollister "Firmware timestamp: 0x%" PRIx64, pwp->fw_timestamp); 725*1f81b464SDavid Hollister } 726*1f81b464SDavid Hollister 7274c06356bSdh return (0); 7284c06356bSdh } 7294c06356bSdh 7304c06356bSdh /* 7314c06356bSdh * Called with PHY locked 7324c06356bSdh */ 7334c06356bSdh int 7344c06356bSdh pmcs_reset_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint8_t type) 7354c06356bSdh { 7364c06356bSdh uint32_t *msg; 7374c06356bSdh uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2]; 7384c06356bSdh const char *mbar; 7394c06356bSdh uint32_t amt; 7404c06356bSdh uint32_t pdevid; 7414c06356bSdh uint32_t stsoff; 7424c06356bSdh uint32_t status; 7434c06356bSdh int result, level, phynum; 7444c06356bSdh struct pmcwork *pwrk; 7454c06356bSdh uint32_t htag; 7464c06356bSdh 7474c06356bSdh ASSERT(mutex_owned(&pptr->phy_lock)); 7484c06356bSdh 7494c06356bSdh bzero(iomb, PMCS_QENTRY_SIZE); 7504c06356bSdh phynum = pptr->phynum; 7514c06356bSdh level = pptr->level; 7524c06356bSdh if (level > 0) { 7534c06356bSdh pdevid = pptr->parent->device_id; 754601c90f1SSrikanth, Ramana } else if ((level == 0) && (pptr->dtype == EXPANDER)) { 755601c90f1SSrikanth, Ramana pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, pptr->target, 756601c90f1SSrikanth, Ramana "%s: Not resetting HBA PHY @ %s", __func__, pptr->path); 757601c90f1SSrikanth, Ramana return (0); 7584c06356bSdh } 7594c06356bSdh 7609aed1621SDavid Hollister if (!pptr->iport || !pptr->valid_device_id) { 7619aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, pptr->target, 7629aed1621SDavid Hollister "%s: Can't reach PHY %s", __func__, pptr->path); 7639aed1621SDavid Hollister return (0); 7649aed1621SDavid Hollister } 7659aed1621SDavid Hollister 7664c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 7674c06356bSdh 7684c06356bSdh if (pwrk == NULL) { 769c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__); 7704c06356bSdh return (ENOMEM); 7714c06356bSdh } 7724c06356bSdh 7734c06356bSdh pwrk->arg = iomb; 7744c06356bSdh 7754c06356bSdh /* 7764c06356bSdh * If level > 0, we need to issue an SMP_REQUEST with a PHY_CONTROL 7774c06356bSdh * function to do either a link reset or hard reset. If level == 0, 7784c06356bSdh * then we do a LOCAL_PHY_CONTROL IOMB to do link/hard reset to the 7794c06356bSdh * root (local) PHY 7804c06356bSdh */ 7814c06356bSdh if (level) { 7824c06356bSdh stsoff = 2; 7834c06356bSdh iomb[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 7844c06356bSdh PMCIN_SMP_REQUEST)); 7854c06356bSdh iomb[1] = LE_32(pwrk->htag); 7864c06356bSdh iomb[2] = LE_32(pdevid); 7874c06356bSdh iomb[3] = LE_32(40 << SMP_REQUEST_LENGTH_SHIFT); 7884c06356bSdh /* 7894c06356bSdh * Send SMP PHY CONTROL/HARD or LINK RESET 7904c06356bSdh */ 7914c06356bSdh iomb[4] = BE_32(0x40910000); 7924c06356bSdh iomb[5] = 0; 7934c06356bSdh 7944c06356bSdh if (type == PMCS_PHYOP_HARD_RESET) { 7954c06356bSdh mbar = "SMP PHY CONTROL/HARD RESET"; 7964c06356bSdh iomb[6] = BE_32((phynum << 24) | 7974c06356bSdh (PMCS_PHYOP_HARD_RESET << 16)); 7984c06356bSdh } else { 7994c06356bSdh mbar = "SMP PHY CONTROL/LINK RESET"; 8004c06356bSdh iomb[6] = BE_32((phynum << 24) | 8014c06356bSdh (PMCS_PHYOP_LINK_RESET << 16)); 8024c06356bSdh } 803c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 8044c06356bSdh "%s: sending %s to %s for phy 0x%x", 8054c06356bSdh __func__, mbar, pptr->parent->path, pptr->phynum); 8064c06356bSdh amt = 7; 8074c06356bSdh } else { 8084c06356bSdh /* 8094c06356bSdh * Unlike most other Outbound messages, status for 8104c06356bSdh * a local phy operation is in DWORD 3. 8114c06356bSdh */ 8124c06356bSdh stsoff = 3; 8134c06356bSdh iomb[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 8144c06356bSdh PMCIN_LOCAL_PHY_CONTROL)); 8154c06356bSdh iomb[1] = LE_32(pwrk->htag); 8164c06356bSdh if (type == PMCS_PHYOP_LINK_RESET) { 8174c06356bSdh mbar = "LOCAL PHY LINK RESET"; 8184c06356bSdh iomb[2] = LE_32((PMCS_PHYOP_LINK_RESET << 8) | phynum); 8194c06356bSdh } else { 8204c06356bSdh mbar = "LOCAL PHY HARD RESET"; 8214c06356bSdh iomb[2] = LE_32((PMCS_PHYOP_HARD_RESET << 8) | phynum); 8224c06356bSdh } 823c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 8244c06356bSdh "%s: sending %s to %s", __func__, mbar, pptr->path); 8254c06356bSdh amt = 3; 8264c06356bSdh } 8274c06356bSdh 8284c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 8294c06356bSdh msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 8304c06356bSdh if (msg == NULL) { 8314c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 8324c06356bSdh pmcs_pwork(pwp, pwrk); 833c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__); 8344c06356bSdh return (ENOMEM); 8354c06356bSdh } 8364c06356bSdh COPY_MESSAGE(msg, iomb, amt); 8374c06356bSdh htag = pwrk->htag; 8386745c559SJesse Butler 8396745c559SJesse Butler /* SMP serialization */ 8406745c559SJesse Butler pmcs_smp_acquire(pptr->iport); 8416745c559SJesse Butler 8424c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 8434c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 8444c06356bSdh 8454c06356bSdh pmcs_unlock_phy(pptr); 8464c06356bSdh WAIT_FOR(pwrk, 1000, result); 8474c06356bSdh pmcs_pwork(pwp, pwrk); 848601c90f1SSrikanth, Ramana /* Release SMP lock before reacquiring PHY lock */ 8496745c559SJesse Butler pmcs_smp_release(pptr->iport); 850601c90f1SSrikanth, Ramana pmcs_lock_phy(pptr); 8516745c559SJesse Butler 8524c06356bSdh if (result) { 8536745c559SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__); 8544c06356bSdh 8554c06356bSdh if (pmcs_abort(pwp, pptr, htag, 0, 0)) { 856c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 8574c06356bSdh "%s: Unable to issue SMP abort for htag 0x%08x", 8584c06356bSdh __func__, htag); 8594c06356bSdh } else { 860c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 8614c06356bSdh "%s: Issuing SMP ABORT for htag 0x%08x", 8624c06356bSdh __func__, htag); 8634c06356bSdh } 8644c06356bSdh return (EIO); 8654c06356bSdh } 8664c06356bSdh status = LE_32(iomb[stsoff]); 8674c06356bSdh 8684c06356bSdh if (status != PMCOUT_STATUS_OK) { 8694c06356bSdh char buf[32]; 8704c06356bSdh const char *es = pmcs_status_str(status); 8714c06356bSdh if (es == NULL) { 8724c06356bSdh (void) snprintf(buf, sizeof (buf), "Status 0x%x", 8734c06356bSdh status); 8744c06356bSdh es = buf; 8754c06356bSdh } 876c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 8774c06356bSdh "%s: %s action returned %s for %s", __func__, mbar, es, 8784c06356bSdh pptr->path); 879601c90f1SSrikanth, Ramana return (status); 8804c06356bSdh } 8814c06356bSdh 8824c06356bSdh return (0); 8834c06356bSdh } 8844c06356bSdh 8854c06356bSdh /* 8864c06356bSdh * Stop the (real) phys. No PHY or softstate locks are required as this only 8874c06356bSdh * happens during detach. 8884c06356bSdh */ 8894c06356bSdh void 8904c06356bSdh pmcs_stop_phy(pmcs_hw_t *pwp, int phynum) 8914c06356bSdh { 8924c06356bSdh int result; 8934c06356bSdh pmcs_phy_t *pptr; 8944c06356bSdh uint32_t *msg; 8954c06356bSdh struct pmcwork *pwrk; 8964c06356bSdh 8974c06356bSdh pptr = pwp->root_phys + phynum; 8984c06356bSdh if (pptr == NULL) { 899c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 9004c06356bSdh "%s: unable to find port %d", __func__, phynum); 9014c06356bSdh return; 9024c06356bSdh } 9034c06356bSdh 9044c06356bSdh if (pwp->phys_started & (1 << phynum)) { 9054c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 9064c06356bSdh 9074c06356bSdh if (pwrk == NULL) { 908c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, 909c3bc407cSdh pmcs_nowrk, __func__); 9104c06356bSdh return; 9114c06356bSdh } 9124c06356bSdh 9134c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 9144c06356bSdh msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 9154c06356bSdh 9164c06356bSdh if (msg == NULL) { 9174c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 9184c06356bSdh pmcs_pwork(pwp, pwrk); 919c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, 920c3bc407cSdh pmcs_nomsg, __func__); 9214c06356bSdh return; 9224c06356bSdh } 9234c06356bSdh 9244c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_PHY_STOP)); 9254c06356bSdh msg[1] = LE_32(pwrk->htag); 9264c06356bSdh msg[2] = LE_32(phynum); 9274c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 9284c06356bSdh /* 9294c06356bSdh * Make this unconfigured now. 9304c06356bSdh */ 9314c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 9324c06356bSdh WAIT_FOR(pwrk, 1000, result); 9334c06356bSdh 9344c06356bSdh pmcs_pwork(pwp, pwrk); 9354c06356bSdh if (result) { 9366745c559SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG, 937c3bc407cSdh pptr, NULL, pmcs_timeo, __func__); 9384c06356bSdh } 9394c06356bSdh 9404c06356bSdh pwp->phys_started &= ~(1 << phynum); 9414c06356bSdh } 9424c06356bSdh 9434c06356bSdh pptr->configured = 0; 9444c06356bSdh } 9454c06356bSdh 9464c06356bSdh /* 9474c06356bSdh * No locks should be required as this is only called during detach 9484c06356bSdh */ 9494c06356bSdh void 9504c06356bSdh pmcs_stop_phys(pmcs_hw_t *pwp) 9514c06356bSdh { 9524c06356bSdh int i; 9534c06356bSdh for (i = 0; i < pwp->nphy; i++) { 9544c06356bSdh if ((pwp->phyid_block_mask & (1 << i)) == 0) { 9554c06356bSdh pmcs_stop_phy(pwp, i); 9564c06356bSdh } 9574c06356bSdh } 9584c06356bSdh } 9594c06356bSdh 9604c06356bSdh /* 9614c06356bSdh * Run SAS_DIAG_EXECUTE with cmd and cmd_desc passed. 9624c06356bSdh * ERR_CNT_RESET: return status of cmd 9634c06356bSdh * DIAG_REPORT_GET: return value of the counter 9644c06356bSdh */ 9654c06356bSdh int 9664c06356bSdh pmcs_sas_diag_execute(pmcs_hw_t *pwp, uint32_t cmd, uint32_t cmd_desc, 9674c06356bSdh uint8_t phynum) 9684c06356bSdh { 9694c06356bSdh uint32_t htag, *ptr, status, msg[PMCS_MSG_SIZE << 1]; 9704c06356bSdh int result; 9714c06356bSdh struct pmcwork *pwrk; 9724c06356bSdh 9734c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL); 9744c06356bSdh if (pwrk == NULL) { 975c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nowrk, __func__); 9764c06356bSdh return (DDI_FAILURE); 9774c06356bSdh } 9784c06356bSdh pwrk->arg = msg; 9794c06356bSdh htag = pwrk->htag; 9804c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_SAS_DIAG_EXECUTE)); 9814c06356bSdh msg[1] = LE_32(htag); 9824c06356bSdh msg[2] = LE_32((cmd << PMCS_DIAG_CMD_SHIFT) | 9834c06356bSdh (cmd_desc << PMCS_DIAG_CMD_DESC_SHIFT) | phynum); 9844c06356bSdh 9854c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 9864c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 9874c06356bSdh if (ptr == NULL) { 9884c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 9894c06356bSdh pmcs_pwork(pwp, pwrk); 990c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nomsg, __func__); 9914c06356bSdh return (DDI_FAILURE); 9924c06356bSdh } 9934c06356bSdh COPY_MESSAGE(ptr, msg, 3); 9944c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 9954c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 9964c06356bSdh 9974c06356bSdh WAIT_FOR(pwrk, 1000, result); 9984c06356bSdh 9994c06356bSdh pmcs_pwork(pwp, pwrk); 10004c06356bSdh 10014c06356bSdh if (result) { 10024c06356bSdh pmcs_timed_out(pwp, htag, __func__); 10034c06356bSdh return (DDI_FAILURE); 10044c06356bSdh } 10054c06356bSdh 10064c06356bSdh status = LE_32(msg[3]); 10074c06356bSdh 10084c06356bSdh /* Return for counter reset */ 10094c06356bSdh if (cmd == PMCS_ERR_CNT_RESET) 10104c06356bSdh return (status); 10114c06356bSdh 10124c06356bSdh /* Return for counter value */ 10134c06356bSdh if (status) { 1014c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1015c3bc407cSdh "%s: failed, status (0x%x)", __func__, status); 10164c06356bSdh return (DDI_FAILURE); 10174c06356bSdh } 10184c06356bSdh return (LE_32(msg[4])); 10194c06356bSdh } 10204c06356bSdh 10214c06356bSdh /* Get the current value of the counter for desc on phynum and return it. */ 10224c06356bSdh int 10234c06356bSdh pmcs_get_diag_report(pmcs_hw_t *pwp, uint32_t desc, uint8_t phynum) 10244c06356bSdh { 10254c06356bSdh return (pmcs_sas_diag_execute(pwp, PMCS_DIAG_REPORT_GET, desc, phynum)); 10264c06356bSdh } 10274c06356bSdh 10284c06356bSdh /* Clear all of the counters for phynum. Returns the status of the command. */ 10294c06356bSdh int 10304c06356bSdh pmcs_clear_diag_counters(pmcs_hw_t *pwp, uint8_t phynum) 10314c06356bSdh { 10324c06356bSdh uint32_t cmd = PMCS_ERR_CNT_RESET; 10334c06356bSdh uint32_t cmd_desc; 10344c06356bSdh 10354c06356bSdh cmd_desc = PMCS_INVALID_DWORD_CNT; 10364c06356bSdh if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum)) 10374c06356bSdh return (DDI_FAILURE); 10384c06356bSdh 10394c06356bSdh cmd_desc = PMCS_DISPARITY_ERR_CNT; 10404c06356bSdh if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum)) 10414c06356bSdh return (DDI_FAILURE); 10424c06356bSdh 10434c06356bSdh cmd_desc = PMCS_LOST_DWORD_SYNC_CNT; 10444c06356bSdh if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum)) 10454c06356bSdh return (DDI_FAILURE); 10464c06356bSdh 10474c06356bSdh cmd_desc = PMCS_RESET_FAILED_CNT; 10484c06356bSdh if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum)) 10494c06356bSdh return (DDI_FAILURE); 10504c06356bSdh 10514c06356bSdh return (DDI_SUCCESS); 10524c06356bSdh } 10534c06356bSdh 10544c06356bSdh /* 10554c06356bSdh * Get firmware timestamp 10564c06356bSdh */ 1057*1f81b464SDavid Hollister static int 1058*1f81b464SDavid Hollister pmcs_get_time_stamp(pmcs_hw_t *pwp, uint64_t *fw_ts, hrtime_t *sys_hr_ts) 10594c06356bSdh { 10604c06356bSdh uint32_t htag, *ptr, msg[PMCS_MSG_SIZE << 1]; 10614c06356bSdh int result; 10624c06356bSdh struct pmcwork *pwrk; 10634c06356bSdh 10644c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL); 10654c06356bSdh if (pwrk == NULL) { 1066c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nowrk, __func__); 10674c06356bSdh return (-1); 10684c06356bSdh } 10694c06356bSdh pwrk->arg = msg; 10704c06356bSdh htag = pwrk->htag; 10714c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_GET_TIME_STAMP)); 10724c06356bSdh msg[1] = LE_32(pwrk->htag); 10734c06356bSdh 10744c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 10754c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 10764c06356bSdh if (ptr == NULL) { 10774c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 10784c06356bSdh pmcs_pwork(pwp, pwrk); 1079c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nomsg, __func__); 10804c06356bSdh return (-1); 10814c06356bSdh } 10824c06356bSdh COPY_MESSAGE(ptr, msg, 2); 10834c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 10844c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 10854c06356bSdh 10864c06356bSdh WAIT_FOR(pwrk, 1000, result); 10874c06356bSdh 10884c06356bSdh pmcs_pwork(pwp, pwrk); 10894c06356bSdh 10904c06356bSdh if (result) { 10914c06356bSdh pmcs_timed_out(pwp, htag, __func__); 10924c06356bSdh return (-1); 10934c06356bSdh } 1094*1f81b464SDavid Hollister 1095*1f81b464SDavid Hollister mutex_enter(&pmcs_trace_lock); 1096*1f81b464SDavid Hollister *sys_hr_ts = gethrtime(); 1097*1f81b464SDavid Hollister gethrestime(&pwp->sys_timestamp); 1098*1f81b464SDavid Hollister *fw_ts = LE_32(msg[2]) | (((uint64_t)LE_32(msg[3])) << 32); 1099*1f81b464SDavid Hollister mutex_exit(&pmcs_trace_lock); 11004c06356bSdh return (0); 11014c06356bSdh } 11024c06356bSdh 11034c06356bSdh /* 11044c06356bSdh * Dump all pertinent registers 11054c06356bSdh */ 11064c06356bSdh 11074c06356bSdh void 11084c06356bSdh pmcs_register_dump(pmcs_hw_t *pwp) 11094c06356bSdh { 11104c06356bSdh int i; 11114c06356bSdh uint32_t val; 11124c06356bSdh 1113c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "pmcs%d: Register dump start", 11144c06356bSdh ddi_get_instance(pwp->dip)); 1115c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, 11164c06356bSdh "OBDB (intr): 0x%08x (mask): 0x%08x (clear): 0x%08x", 11174c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB), 11184c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB_MASK), 11194c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR)); 1120c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH0: 0x%08x", 11214c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH0)); 1122c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH1: 0x%08x", 11234c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1)); 1124c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH2: 0x%08x", 11254c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2)); 1126c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH3: 0x%08x", 11274c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH3)); 11284c06356bSdh for (i = 0; i < PMCS_NIQ; i++) { 1129c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "IQ %d: CI %u PI %u", 11304c06356bSdh i, pmcs_rd_iqci(pwp, i), pmcs_rd_iqpi(pwp, i)); 11314c06356bSdh } 11324c06356bSdh for (i = 0; i < PMCS_NOQ; i++) { 1133c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "OQ %d: CI %u PI %u", 11344c06356bSdh i, pmcs_rd_oqci(pwp, i), pmcs_rd_oqpi(pwp, i)); 11354c06356bSdh } 11364c06356bSdh val = pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE); 1137c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, 11384c06356bSdh "GST TABLE BASE: 0x%08x (STATE=0x%x QF=%d GSTLEN=%d HMI_ERR=0x%x)", 11394c06356bSdh val, PMCS_MPI_S(val), PMCS_QF(val), PMCS_GSTLEN(val) * 4, 11404c06356bSdh PMCS_HMI_ERR(val)); 1141c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE IQFRZ0: 0x%08x", 11424c06356bSdh pmcs_rd_gst_tbl(pwp, PMCS_GST_IQFRZ0)); 1143c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE IQFRZ1: 0x%08x", 11444c06356bSdh pmcs_rd_gst_tbl(pwp, PMCS_GST_IQFRZ1)); 1145c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE MSGU TICK: 0x%08x", 11464c06356bSdh pmcs_rd_gst_tbl(pwp, PMCS_GST_MSGU_TICK)); 1147c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE IOP TICK: 0x%08x", 11484c06356bSdh pmcs_rd_gst_tbl(pwp, PMCS_GST_IOP_TICK)); 11494c06356bSdh for (i = 0; i < pwp->nphy; i++) { 11504c06356bSdh uint32_t rerrf, pinfo, started = 0, link = 0; 11514c06356bSdh pinfo = pmcs_rd_gst_tbl(pwp, PMCS_GST_PHY_INFO(i)); 11524c06356bSdh if (pinfo & 1) { 11534c06356bSdh started = 1; 11544c06356bSdh link = pinfo & 2; 11554c06356bSdh } 11564c06356bSdh rerrf = pmcs_rd_gst_tbl(pwp, PMCS_GST_RERR_INFO(i)); 1157c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, 11584c06356bSdh "GST TABLE PHY%d STARTED=%d LINK=%d RERR=0x%08x", 11594c06356bSdh i, started, link, rerrf); 11604c06356bSdh } 1161c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "pmcs%d: Register dump end", 11624c06356bSdh ddi_get_instance(pwp->dip)); 11634c06356bSdh } 11644c06356bSdh 11654c06356bSdh /* 11664c06356bSdh * Handle SATA Abort and other error processing 11674c06356bSdh */ 11684c06356bSdh int 11694c06356bSdh pmcs_abort_handler(pmcs_hw_t *pwp) 11704c06356bSdh { 11714c06356bSdh pmcs_phy_t *pptr, *pnext, *pnext_uplevel[PMCS_MAX_XPND]; 1172601c90f1SSrikanth, Ramana pmcs_xscsi_t *tgt; 11734c06356bSdh int r, level = 0; 11744c06356bSdh 1175c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s", __func__); 11764c06356bSdh 11774c06356bSdh mutex_enter(&pwp->lock); 11784c06356bSdh pptr = pwp->root_phys; 11794c06356bSdh mutex_exit(&pwp->lock); 11804c06356bSdh 11814c06356bSdh while (pptr) { 11824c06356bSdh /* 11834c06356bSdh * XXX: Need to make sure this doesn't happen 11844c06356bSdh * XXX: when non-NCQ commands are running. 11854c06356bSdh */ 11864c06356bSdh pmcs_lock_phy(pptr); 11874c06356bSdh if (pptr->need_rl_ext) { 11884c06356bSdh ASSERT(pptr->dtype == SATA); 11894c06356bSdh if (pmcs_acquire_scratch(pwp, B_FALSE)) { 11904c06356bSdh goto next_phy; 11914c06356bSdh } 11924c06356bSdh r = pmcs_sata_abort_ncq(pwp, pptr); 11934c06356bSdh pmcs_release_scratch(pwp); 11944c06356bSdh if (r == ENOMEM) { 11954c06356bSdh goto next_phy; 11964c06356bSdh } 11974c06356bSdh if (r) { 11984c06356bSdh r = pmcs_reset_phy(pwp, pptr, 11994c06356bSdh PMCS_PHYOP_LINK_RESET); 12004c06356bSdh if (r == ENOMEM) { 12014c06356bSdh goto next_phy; 12024c06356bSdh } 12034c06356bSdh /* what if other failures happened? */ 12044c06356bSdh pptr->abort_pending = 1; 12054c06356bSdh pptr->abort_sent = 0; 12064c06356bSdh } 12074c06356bSdh } 12084c06356bSdh if (pptr->abort_pending == 0 || pptr->abort_sent) { 12094c06356bSdh goto next_phy; 12104c06356bSdh } 12114c06356bSdh pptr->abort_pending = 0; 12124c06356bSdh if (pmcs_abort(pwp, pptr, pptr->device_id, 1, 1) == ENOMEM) { 12134c06356bSdh pptr->abort_pending = 1; 12144c06356bSdh goto next_phy; 12154c06356bSdh } 12164c06356bSdh pptr->abort_sent = 1; 12174c06356bSdh 1218601c90f1SSrikanth, Ramana /* 1219601c90f1SSrikanth, Ramana * If the iport is no longer active, flush the queues 1220601c90f1SSrikanth, Ramana */ 1221601c90f1SSrikanth, Ramana if ((pptr->iport == NULL) || 1222601c90f1SSrikanth, Ramana (pptr->iport->ua_state != UA_ACTIVE)) { 1223601c90f1SSrikanth, Ramana tgt = pptr->target; 1224601c90f1SSrikanth, Ramana if (tgt) { 1225601c90f1SSrikanth, Ramana pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, tgt, 1226601c90f1SSrikanth, Ramana "%s: Clearing target 0x%p, inactive iport", 1227601c90f1SSrikanth, Ramana __func__, (void *) tgt); 1228601c90f1SSrikanth, Ramana mutex_enter(&tgt->statlock); 1229601c90f1SSrikanth, Ramana pmcs_clear_xp(pwp, tgt); 1230601c90f1SSrikanth, Ramana mutex_exit(&tgt->statlock); 1231601c90f1SSrikanth, Ramana } 1232601c90f1SSrikanth, Ramana } 1233601c90f1SSrikanth, Ramana 12344c06356bSdh next_phy: 12354c06356bSdh if (pptr->children) { 12364c06356bSdh pnext = pptr->children; 12374c06356bSdh pnext_uplevel[level++] = pptr->sibling; 12384c06356bSdh } else { 12394c06356bSdh pnext = pptr->sibling; 12404c06356bSdh while ((pnext == NULL) && (level > 0)) { 12414c06356bSdh pnext = pnext_uplevel[--level]; 12424c06356bSdh } 12434c06356bSdh } 12444c06356bSdh 12454c06356bSdh pmcs_unlock_phy(pptr); 12464c06356bSdh pptr = pnext; 12474c06356bSdh } 12484c06356bSdh 12494c06356bSdh return (0); 12504c06356bSdh } 12514c06356bSdh 12524c06356bSdh /* 12534c06356bSdh * Register a device (get a device handle for it). 12544c06356bSdh * Called with PHY lock held. 12554c06356bSdh */ 12564c06356bSdh int 12574c06356bSdh pmcs_register_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 12584c06356bSdh { 12594c06356bSdh struct pmcwork *pwrk; 12604c06356bSdh int result = 0; 12614c06356bSdh uint32_t *msg; 12624c06356bSdh uint32_t tmp, status; 12634c06356bSdh uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2]; 12644c06356bSdh 12654c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 12664c06356bSdh msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 12674c06356bSdh 12684c06356bSdh if (msg == NULL || 12694c06356bSdh (pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr)) == NULL) { 12704c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 12714c06356bSdh result = ENOMEM; 12724c06356bSdh goto out; 12734c06356bSdh } 12744c06356bSdh 12754c06356bSdh pwrk->arg = iomb; 12764c06356bSdh pwrk->dtype = pptr->dtype; 12774c06356bSdh 12784c06356bSdh msg[1] = LE_32(pwrk->htag); 12794c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_REGISTER_DEVICE)); 12804c06356bSdh tmp = PMCS_DEVREG_TLR | 12814c06356bSdh (pptr->link_rate << PMCS_DEVREG_LINK_RATE_SHIFT); 12824c06356bSdh if (IS_ROOT_PHY(pptr)) { 12834c06356bSdh msg[2] = LE_32(pptr->portid | 12844c06356bSdh (pptr->phynum << PMCS_PHYID_SHIFT)); 12854c06356bSdh } else { 12864c06356bSdh msg[2] = LE_32(pptr->portid); 12874c06356bSdh } 12884c06356bSdh if (pptr->dtype == SATA) { 12894c06356bSdh if (IS_ROOT_PHY(pptr)) { 12904c06356bSdh tmp |= PMCS_DEVREG_TYPE_SATA_DIRECT; 12914c06356bSdh } else { 12924c06356bSdh tmp |= PMCS_DEVREG_TYPE_SATA; 12934c06356bSdh } 12944c06356bSdh } else { 12954c06356bSdh tmp |= PMCS_DEVREG_TYPE_SAS; 12964c06356bSdh } 12974c06356bSdh msg[3] = LE_32(tmp); 12984c06356bSdh msg[4] = LE_32(PMCS_DEVREG_IT_NEXUS_TIMEOUT); 12994c06356bSdh (void) memcpy(&msg[5], pptr->sas_address, 8); 13004c06356bSdh 13014c06356bSdh CLEAN_MESSAGE(msg, 7); 13024c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 13034c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 13044c06356bSdh 13054c06356bSdh pmcs_unlock_phy(pptr); 13064c06356bSdh WAIT_FOR(pwrk, 250, result); 13074c06356bSdh pmcs_lock_phy(pptr); 13084c06356bSdh pmcs_pwork(pwp, pwrk); 13094c06356bSdh 13104c06356bSdh if (result) { 13116745c559SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__); 13124c06356bSdh result = ETIMEDOUT; 13134c06356bSdh goto out; 13144c06356bSdh } 13154c06356bSdh status = LE_32(iomb[2]); 13164c06356bSdh tmp = LE_32(iomb[3]); 13174c06356bSdh switch (status) { 13184c06356bSdh case PMCS_DEVREG_OK: 13194c06356bSdh case PMCS_DEVREG_DEVICE_ALREADY_REGISTERED: 13204c06356bSdh case PMCS_DEVREG_PHY_ALREADY_REGISTERED: 13214c06356bSdh if (pmcs_validate_devid(pwp->root_phys, pptr, tmp) == B_FALSE) { 13224c06356bSdh result = EEXIST; 13234c06356bSdh goto out; 13244c06356bSdh } else if (status != PMCS_DEVREG_OK) { 13254c06356bSdh if (tmp == 0xffffffff) { /* F/W bug */ 1326c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, pptr, NULL, 13274c06356bSdh "%s: phy %s already has bogus devid 0x%x", 13284c06356bSdh __func__, pptr->path, tmp); 13294c06356bSdh result = EIO; 13304c06356bSdh goto out; 13314c06356bSdh } else { 1332c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, pptr, NULL, 13334c06356bSdh "%s: phy %s already has a device id 0x%x", 13344c06356bSdh __func__, pptr->path, tmp); 13354c06356bSdh } 13364c06356bSdh } 13374c06356bSdh break; 13384c06356bSdh default: 1339c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 1340c3bc407cSdh "%s: status 0x%x when trying to register device %s", 1341c3bc407cSdh __func__, status, pptr->path); 13424c06356bSdh result = EIO; 13434c06356bSdh goto out; 13444c06356bSdh } 13454c06356bSdh pptr->device_id = tmp; 13464c06356bSdh pptr->valid_device_id = 1; 1347c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "Phy %s/" SAS_ADDR_FMT 13484c06356bSdh " registered with device_id 0x%x (portid %d)", pptr->path, 13494c06356bSdh SAS_ADDR_PRT(pptr->sas_address), tmp, pptr->portid); 13504c06356bSdh out: 13514c06356bSdh return (result); 13524c06356bSdh } 13534c06356bSdh 13544c06356bSdh /* 13554c06356bSdh * Deregister a device (remove a device handle). 13564c06356bSdh * Called with PHY locked. 13574c06356bSdh */ 13584c06356bSdh void 13594c06356bSdh pmcs_deregister_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 13604c06356bSdh { 13614c06356bSdh struct pmcwork *pwrk; 13624c06356bSdh uint32_t msg[PMCS_MSG_SIZE], *ptr, status; 13634c06356bSdh uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2]; 13644c06356bSdh int result; 13654c06356bSdh 13664c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 13674c06356bSdh if (pwrk == NULL) { 13684c06356bSdh return; 13694c06356bSdh } 13704c06356bSdh 13714c06356bSdh pwrk->arg = iomb; 13724c06356bSdh pwrk->dtype = pptr->dtype; 13734c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 13744c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 13754c06356bSdh if (ptr == NULL) { 13764c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 13774c06356bSdh pmcs_pwork(pwp, pwrk); 13784c06356bSdh return; 13794c06356bSdh } 13804c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 13814c06356bSdh PMCIN_DEREGISTER_DEVICE_HANDLE)); 13824c06356bSdh msg[1] = LE_32(pwrk->htag); 13834c06356bSdh msg[2] = LE_32(pptr->device_id); 13844c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 13854c06356bSdh COPY_MESSAGE(ptr, msg, 3); 13864c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 13874c06356bSdh 13884c06356bSdh pmcs_unlock_phy(pptr); 13894c06356bSdh WAIT_FOR(pwrk, 250, result); 13904c06356bSdh pmcs_pwork(pwp, pwrk); 13914c06356bSdh pmcs_lock_phy(pptr); 13924c06356bSdh 13934c06356bSdh if (result) { 13946745c559SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__); 13954c06356bSdh return; 13964c06356bSdh } 13974c06356bSdh status = LE_32(iomb[2]); 13984c06356bSdh if (status != PMCOUT_STATUS_OK) { 1399c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 1400c3bc407cSdh "%s: status 0x%x when trying to deregister device %s", 1401c3bc407cSdh __func__, status, pptr->path); 14024c06356bSdh } else { 1403c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 1404c3bc407cSdh "%s: device %s deregistered", __func__, pptr->path); 14054c06356bSdh pptr->valid_device_id = 0; 14064c06356bSdh pptr->device_id = PMCS_INVALID_DEVICE_ID; 1407601c90f1SSrikanth, Ramana pptr->configured = 0; 1408601c90f1SSrikanth, Ramana pptr->deregister_wait = 0; 14094c06356bSdh } 14104c06356bSdh } 14114c06356bSdh 14124c06356bSdh /* 14134c06356bSdh * Deregister all registered devices. 14144c06356bSdh */ 14154c06356bSdh void 14164c06356bSdh pmcs_deregister_devices(pmcs_hw_t *pwp, pmcs_phy_t *phyp) 14174c06356bSdh { 14184c06356bSdh /* 14194c06356bSdh * Start at the maximum level and walk back to level 0. This only 14204c06356bSdh * gets done during detach after all threads and timers have been 14214c06356bSdh * destroyed, so there's no need to hold the softstate or PHY lock. 14224c06356bSdh */ 14234c06356bSdh while (phyp) { 14244c06356bSdh if (phyp->children) { 14254c06356bSdh pmcs_deregister_devices(pwp, phyp->children); 14264c06356bSdh } 14274c06356bSdh if (phyp->valid_device_id) { 14284c06356bSdh pmcs_deregister_device(pwp, phyp); 14294c06356bSdh } 14304c06356bSdh phyp = phyp->sibling; 14314c06356bSdh } 14324c06356bSdh } 14334c06356bSdh 14344c06356bSdh /* 14354c06356bSdh * Perform a 'soft' reset on the PMC chip 14364c06356bSdh */ 14374c06356bSdh int 14384c06356bSdh pmcs_soft_reset(pmcs_hw_t *pwp, boolean_t no_restart) 14394c06356bSdh { 14404c06356bSdh uint32_t s2, sfrbits, gsm, rapchk, wapchk, wdpchk, spc, tsmode; 14414c06356bSdh pmcs_phy_t *pptr; 14424c06356bSdh char *msg = NULL; 14434c06356bSdh int i; 14444c06356bSdh 14454c06356bSdh /* 14464c06356bSdh * Disable interrupts 14474c06356bSdh */ 14484c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff); 14494c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff); 14504c06356bSdh 1451c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "%s", __func__); 14524c06356bSdh 14534c06356bSdh if (pwp->locks_initted) { 14544c06356bSdh mutex_enter(&pwp->lock); 14554c06356bSdh } 14564c06356bSdh pwp->blocked = 1; 14574c06356bSdh 14585c45adf0SJesse Butler /* 14595c45adf0SJesse Butler * Clear our softstate copies of the MSGU and IOP heartbeats. 14605c45adf0SJesse Butler */ 14615c45adf0SJesse Butler pwp->last_msgu_tick = pwp->last_iop_tick = 0; 14625c45adf0SJesse Butler 14634c06356bSdh /* 14644c06356bSdh * Step 1 14654c06356bSdh */ 14664c06356bSdh s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2); 14674c06356bSdh if ((s2 & PMCS_MSGU_HOST_SOFT_RESET_READY) == 0) { 14684c06356bSdh pmcs_wr_gsm_reg(pwp, RB6_ACCESS, RB6_NMI_SIGNATURE); 14694c06356bSdh pmcs_wr_gsm_reg(pwp, RB6_ACCESS, RB6_NMI_SIGNATURE); 14704c06356bSdh for (i = 0; i < 100; i++) { 14714c06356bSdh s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) & 14724c06356bSdh PMCS_MSGU_HOST_SOFT_RESET_READY; 14734c06356bSdh if (s2) { 14744c06356bSdh break; 14754c06356bSdh } 14764c06356bSdh drv_usecwait(10000); 14774c06356bSdh } 14784c06356bSdh s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) & 14794c06356bSdh PMCS_MSGU_HOST_SOFT_RESET_READY; 14804c06356bSdh if (s2 == 0) { 1481c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1482c3bc407cSdh "%s: PMCS_MSGU_HOST_SOFT_RESET_READY never came " 1483c3bc407cSdh "ready", __func__); 14844c06356bSdh pmcs_register_dump(pwp); 14854c06356bSdh if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) & 14864c06356bSdh PMCS_MSGU_CPU_SOFT_RESET_READY) == 0 || 14874c06356bSdh (pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) & 14884c06356bSdh PMCS_MSGU_CPU_SOFT_RESET_READY) == 0) { 14894c06356bSdh pwp->state = STATE_DEAD; 14904c06356bSdh pwp->blocked = 0; 14914c06356bSdh if (pwp->locks_initted) { 14924c06356bSdh mutex_exit(&pwp->lock); 14934c06356bSdh } 14944c06356bSdh return (-1); 14954c06356bSdh } 14964c06356bSdh } 14974c06356bSdh } 14984c06356bSdh 14994c06356bSdh /* 15004c06356bSdh * Step 2 15014c06356bSdh */ 15024c06356bSdh pmcs_wr_gsm_reg(pwp, NMI_EN_VPE0_IOP, 0); 15034c06356bSdh drv_usecwait(10); 15044c06356bSdh pmcs_wr_gsm_reg(pwp, NMI_EN_VPE0_AAP1, 0); 15054c06356bSdh drv_usecwait(10); 15064c06356bSdh pmcs_wr_topunit(pwp, PMCS_EVENT_INT_ENABLE, 0); 15074c06356bSdh drv_usecwait(10); 15084c06356bSdh pmcs_wr_topunit(pwp, PMCS_EVENT_INT_STAT, 15094c06356bSdh pmcs_rd_topunit(pwp, PMCS_EVENT_INT_STAT)); 15104c06356bSdh drv_usecwait(10); 15114c06356bSdh pmcs_wr_topunit(pwp, PMCS_ERROR_INT_ENABLE, 0); 15124c06356bSdh drv_usecwait(10); 15134c06356bSdh pmcs_wr_topunit(pwp, PMCS_ERROR_INT_STAT, 15144c06356bSdh pmcs_rd_topunit(pwp, PMCS_ERROR_INT_STAT)); 15154c06356bSdh drv_usecwait(10); 15164c06356bSdh 15174c06356bSdh sfrbits = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) & 15184c06356bSdh PMCS_MSGU_AAP_SFR_PROGRESS; 15194c06356bSdh sfrbits ^= PMCS_MSGU_AAP_SFR_PROGRESS; 1520c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "PMCS_MSGU_HOST_SCRATCH0 " 1521c3bc407cSdh "%08x -> %08x", pmcs_rd_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH0), 1522c3bc407cSdh HST_SFT_RESET_SIG); 15234c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH0, HST_SFT_RESET_SIG); 15244c06356bSdh 15254c06356bSdh /* 15264c06356bSdh * Step 3 15274c06356bSdh */ 15282ac4abe8SDavid Hollister gsm = pmcs_rd_gsm_reg(pwp, 0, GSM_CFG_AND_RESET); 1529c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "GSM %08x -> %08x", gsm, 15304c06356bSdh gsm & ~PMCS_SOFT_RESET_BITS); 15314c06356bSdh pmcs_wr_gsm_reg(pwp, GSM_CFG_AND_RESET, gsm & ~PMCS_SOFT_RESET_BITS); 15324c06356bSdh 15334c06356bSdh /* 15344c06356bSdh * Step 4 15354c06356bSdh */ 15362ac4abe8SDavid Hollister rapchk = pmcs_rd_gsm_reg(pwp, 0, READ_ADR_PARITY_CHK_EN); 1537c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "READ_ADR_PARITY_CHK_EN " 1538c3bc407cSdh "%08x -> %08x", rapchk, 0); 15394c06356bSdh pmcs_wr_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN, 0); 15402ac4abe8SDavid Hollister wapchk = pmcs_rd_gsm_reg(pwp, 0, WRITE_ADR_PARITY_CHK_EN); 1541c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_ADR_PARITY_CHK_EN " 1542c3bc407cSdh "%08x -> %08x", wapchk, 0); 15434c06356bSdh pmcs_wr_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN, 0); 15442ac4abe8SDavid Hollister wdpchk = pmcs_rd_gsm_reg(pwp, 0, WRITE_DATA_PARITY_CHK_EN); 1545c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_DATA_PARITY_CHK_EN " 1546c3bc407cSdh "%08x -> %08x", wdpchk, 0); 15474c06356bSdh pmcs_wr_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN, 0); 15484c06356bSdh 15494c06356bSdh /* 15504c06356bSdh * Step 5 15514c06356bSdh */ 15524c06356bSdh drv_usecwait(100); 15534c06356bSdh 15544c06356bSdh /* 15554c06356bSdh * Step 5.5 (Temporary workaround for 1.07.xx Beta) 15564c06356bSdh */ 15572ac4abe8SDavid Hollister tsmode = pmcs_rd_gsm_reg(pwp, 0, PMCS_GPIO_TRISTATE_MODE_ADDR); 1558c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "GPIO TSMODE %08x -> %08x", 1559c3bc407cSdh tsmode, tsmode & ~(PMCS_GPIO_TSMODE_BIT0|PMCS_GPIO_TSMODE_BIT1)); 15604c06356bSdh pmcs_wr_gsm_reg(pwp, PMCS_GPIO_TRISTATE_MODE_ADDR, 15614c06356bSdh tsmode & ~(PMCS_GPIO_TSMODE_BIT0|PMCS_GPIO_TSMODE_BIT1)); 15624c06356bSdh drv_usecwait(10); 15634c06356bSdh 15644c06356bSdh /* 15654c06356bSdh * Step 6 15664c06356bSdh */ 15674c06356bSdh spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET); 1568c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x", 1569c3bc407cSdh spc, spc & ~(PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB)); 15704c06356bSdh pmcs_wr_topunit(pwp, PMCS_SPC_RESET, 15714c06356bSdh spc & ~(PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB)); 15724c06356bSdh drv_usecwait(10); 15734c06356bSdh 15744c06356bSdh /* 15754c06356bSdh * Step 7 15764c06356bSdh */ 15774c06356bSdh spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET); 1578c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x", 1579c3bc407cSdh spc, spc & ~(BDMA_CORE_RSTB|OSSP_RSTB)); 15804c06356bSdh pmcs_wr_topunit(pwp, PMCS_SPC_RESET, spc & ~(BDMA_CORE_RSTB|OSSP_RSTB)); 15814c06356bSdh 15824c06356bSdh /* 15834c06356bSdh * Step 8 15844c06356bSdh */ 15854c06356bSdh drv_usecwait(100); 15864c06356bSdh 15874c06356bSdh /* 15884c06356bSdh * Step 9 15894c06356bSdh */ 15904c06356bSdh spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET); 1591c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x", 1592c3bc407cSdh spc, spc | (BDMA_CORE_RSTB|OSSP_RSTB)); 15934c06356bSdh pmcs_wr_topunit(pwp, PMCS_SPC_RESET, spc | (BDMA_CORE_RSTB|OSSP_RSTB)); 15944c06356bSdh 15954c06356bSdh /* 15964c06356bSdh * Step 10 15974c06356bSdh */ 15984c06356bSdh drv_usecwait(100); 15994c06356bSdh 16004c06356bSdh /* 16014c06356bSdh * Step 11 16024c06356bSdh */ 16032ac4abe8SDavid Hollister gsm = pmcs_rd_gsm_reg(pwp, 0, GSM_CFG_AND_RESET); 1604c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "GSM %08x -> %08x", gsm, 16054c06356bSdh gsm | PMCS_SOFT_RESET_BITS); 16064c06356bSdh pmcs_wr_gsm_reg(pwp, GSM_CFG_AND_RESET, gsm | PMCS_SOFT_RESET_BITS); 16074c06356bSdh drv_usecwait(10); 16084c06356bSdh 16094c06356bSdh /* 16104c06356bSdh * Step 12 16114c06356bSdh */ 1612c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "READ_ADR_PARITY_CHK_EN " 16132ac4abe8SDavid Hollister "%08x -> %08x", pmcs_rd_gsm_reg(pwp, 0, READ_ADR_PARITY_CHK_EN), 1614c3bc407cSdh rapchk); 16154c06356bSdh pmcs_wr_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN, rapchk); 16164c06356bSdh drv_usecwait(10); 1617c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_ADR_PARITY_CHK_EN " 16182ac4abe8SDavid Hollister "%08x -> %08x", pmcs_rd_gsm_reg(pwp, 0, WRITE_ADR_PARITY_CHK_EN), 1619c3bc407cSdh wapchk); 16204c06356bSdh pmcs_wr_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN, wapchk); 16214c06356bSdh drv_usecwait(10); 1622c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_DATA_PARITY_CHK_EN " 16232ac4abe8SDavid Hollister "%08x -> %08x", pmcs_rd_gsm_reg(pwp, 0, WRITE_DATA_PARITY_CHK_EN), 1624c3bc407cSdh wapchk); 16254c06356bSdh pmcs_wr_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN, wdpchk); 16264c06356bSdh drv_usecwait(10); 16274c06356bSdh 16284c06356bSdh /* 16294c06356bSdh * Step 13 16304c06356bSdh */ 16314c06356bSdh spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET); 1632c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x", 1633c3bc407cSdh spc, spc | (PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB)); 16344c06356bSdh pmcs_wr_topunit(pwp, PMCS_SPC_RESET, 16354c06356bSdh spc | (PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB)); 16364c06356bSdh 16374c06356bSdh /* 16384c06356bSdh * Step 14 16394c06356bSdh */ 16404c06356bSdh drv_usecwait(100); 16414c06356bSdh 16424c06356bSdh /* 16434c06356bSdh * Step 15 16444c06356bSdh */ 16454c06356bSdh for (spc = 0, i = 0; i < 1000; i++) { 16464c06356bSdh drv_usecwait(1000); 16474c06356bSdh spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1); 16484c06356bSdh if ((spc & PMCS_MSGU_AAP_SFR_PROGRESS) == sfrbits) { 16494c06356bSdh break; 16504c06356bSdh } 16514c06356bSdh } 16524c06356bSdh 16534c06356bSdh if ((spc & PMCS_MSGU_AAP_SFR_PROGRESS) != sfrbits) { 1654c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 16554c06356bSdh "SFR didn't toggle (sfr 0x%x)", spc); 16564c06356bSdh pwp->state = STATE_DEAD; 16574c06356bSdh pwp->blocked = 0; 16584c06356bSdh if (pwp->locks_initted) { 16594c06356bSdh mutex_exit(&pwp->lock); 16604c06356bSdh } 16614c06356bSdh return (-1); 16624c06356bSdh } 16634c06356bSdh 16644c06356bSdh /* 16654c06356bSdh * Step 16 16664c06356bSdh */ 16674c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff); 16684c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff); 16694c06356bSdh 16704c06356bSdh /* 16714c06356bSdh * Wait for up to 5 seconds for AAP state to come either ready or error. 16724c06356bSdh */ 16734c06356bSdh for (i = 0; i < 50; i++) { 16744c06356bSdh spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) & 16754c06356bSdh PMCS_MSGU_AAP_STATE_MASK; 16764c06356bSdh if (spc == PMCS_MSGU_AAP_STATE_ERROR || 16774c06356bSdh spc == PMCS_MSGU_AAP_STATE_READY) { 16784c06356bSdh break; 16794c06356bSdh } 16804c06356bSdh drv_usecwait(100000); 16814c06356bSdh } 16824c06356bSdh spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1); 16834c06356bSdh if ((spc & PMCS_MSGU_AAP_STATE_MASK) != PMCS_MSGU_AAP_STATE_READY) { 1684c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 16854c06356bSdh "soft reset failed (state 0x%x)", spc); 16864c06356bSdh pwp->state = STATE_DEAD; 16874c06356bSdh pwp->blocked = 0; 16884c06356bSdh if (pwp->locks_initted) { 16894c06356bSdh mutex_exit(&pwp->lock); 16904c06356bSdh } 16914c06356bSdh return (-1); 16924c06356bSdh } 16934c06356bSdh 16945c45adf0SJesse Butler /* Clear the firmware log */ 16955c45adf0SJesse Butler if (pwp->fwlogp) { 16965c45adf0SJesse Butler bzero(pwp->fwlogp, PMCS_FWLOG_SIZE); 16975c45adf0SJesse Butler } 16985c45adf0SJesse Butler 16995c45adf0SJesse Butler /* Reset our queue indices and entries */ 17005c45adf0SJesse Butler bzero(pwp->shadow_iqpi, sizeof (pwp->shadow_iqpi)); 17015c45adf0SJesse Butler bzero(pwp->last_iqci, sizeof (pwp->last_iqci)); 1702d78a6b7eSJesse Butler bzero(pwp->last_htag, sizeof (pwp->last_htag)); 17035c45adf0SJesse Butler for (i = 0; i < PMCS_NIQ; i++) { 17045c45adf0SJesse Butler if (pwp->iqp[i]) { 17055c45adf0SJesse Butler bzero(pwp->iqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth); 17065c45adf0SJesse Butler pmcs_wr_iqpi(pwp, i, 0); 17075c45adf0SJesse Butler pmcs_wr_iqci(pwp, i, 0); 17085c45adf0SJesse Butler } 17095c45adf0SJesse Butler } 17105c45adf0SJesse Butler for (i = 0; i < PMCS_NOQ; i++) { 17115c45adf0SJesse Butler if (pwp->oqp[i]) { 17125c45adf0SJesse Butler bzero(pwp->oqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth); 17135c45adf0SJesse Butler pmcs_wr_oqpi(pwp, i, 0); 17145c45adf0SJesse Butler pmcs_wr_oqci(pwp, i, 0); 17155c45adf0SJesse Butler } 17165c45adf0SJesse Butler 17175c45adf0SJesse Butler } 17184c06356bSdh 17194c06356bSdh if (pwp->state == STATE_DEAD || pwp->state == STATE_UNPROBING || 17204c06356bSdh pwp->state == STATE_PROBING || pwp->locks_initted == 0) { 17214c06356bSdh pwp->blocked = 0; 17224c06356bSdh if (pwp->locks_initted) { 17234c06356bSdh mutex_exit(&pwp->lock); 17244c06356bSdh } 17254c06356bSdh return (0); 17264c06356bSdh } 17274c06356bSdh 17284c06356bSdh /* 17294c06356bSdh * Return at this point if we dont need to startup. 17304c06356bSdh */ 17314c06356bSdh if (no_restart) { 17324c06356bSdh return (0); 17334c06356bSdh } 17344c06356bSdh 17354c06356bSdh ASSERT(pwp->locks_initted != 0); 17364c06356bSdh 17374c06356bSdh /* 17385c45adf0SJesse Butler * Flush the target queues and clear each target's PHY 17394c06356bSdh */ 17404c06356bSdh if (pwp->targets) { 17414c06356bSdh for (i = 0; i < pwp->max_dev; i++) { 17424c06356bSdh pmcs_xscsi_t *xp = pwp->targets[i]; 17434c06356bSdh 17444c06356bSdh if (xp == NULL) { 17454c06356bSdh continue; 17464c06356bSdh } 17475c45adf0SJesse Butler 17484c06356bSdh mutex_enter(&xp->statlock); 17495c45adf0SJesse Butler pmcs_flush_target_queues(pwp, xp, PMCS_TGT_ALL_QUEUES); 17505c45adf0SJesse Butler xp->phy = NULL; 17514c06356bSdh mutex_exit(&xp->statlock); 17524c06356bSdh } 17534c06356bSdh } 17544c06356bSdh 17554c06356bSdh /* 17565c45adf0SJesse Butler * Zero out the ports list, free non root phys, clear root phys 17574c06356bSdh */ 17585c45adf0SJesse Butler bzero(pwp->ports, sizeof (pwp->ports)); 17595c45adf0SJesse Butler pmcs_free_all_phys(pwp, pwp->root_phys); 17605c45adf0SJesse Butler for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) { 17615c45adf0SJesse Butler pmcs_lock_phy(pptr); 17625c45adf0SJesse Butler pmcs_clear_phy(pwp, pptr); 17635c45adf0SJesse Butler pptr->target = NULL; 17645c45adf0SJesse Butler pmcs_unlock_phy(pptr); 17654c06356bSdh } 17664c06356bSdh 17674c06356bSdh /* 17684c06356bSdh * Restore Interrupt Mask 17694c06356bSdh */ 17704c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, pwp->intr_mask); 17714c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff); 17724c06356bSdh 17734c06356bSdh pwp->mpi_table_setup = 0; 17744c06356bSdh mutex_exit(&pwp->lock); 17754c06356bSdh 17764c06356bSdh /* 17774c06356bSdh * Set up MPI again. 17784c06356bSdh */ 17794c06356bSdh if (pmcs_setup(pwp)) { 17804c06356bSdh msg = "unable to setup MPI tables again"; 17814c06356bSdh goto fail_restart; 17824c06356bSdh } 17834c06356bSdh pmcs_report_fwversion(pwp); 17844c06356bSdh 17854c06356bSdh /* 17864c06356bSdh * Restart MPI 17874c06356bSdh */ 17884c06356bSdh if (pmcs_start_mpi(pwp)) { 17894c06356bSdh msg = "unable to restart MPI again"; 17904c06356bSdh goto fail_restart; 17914c06356bSdh } 17924c06356bSdh 17934c06356bSdh mutex_enter(&pwp->lock); 17944c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 17954c06356bSdh mutex_exit(&pwp->lock); 17964c06356bSdh 17974c06356bSdh /* 17984c06356bSdh * Run any completions 17994c06356bSdh */ 18004c06356bSdh PMCS_CQ_RUN(pwp); 18014c06356bSdh 18024c06356bSdh /* 18034c06356bSdh * Delay 18044c06356bSdh */ 18054c06356bSdh drv_usecwait(1000000); 18064c06356bSdh return (0); 18074c06356bSdh 18084c06356bSdh fail_restart: 18094c06356bSdh mutex_enter(&pwp->lock); 18104c06356bSdh pwp->state = STATE_DEAD; 18114c06356bSdh mutex_exit(&pwp->lock); 1812c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 1813c3bc407cSdh "%s: Failed: %s", __func__, msg); 18144c06356bSdh return (-1); 18154c06356bSdh } 18164c06356bSdh 18175c45adf0SJesse Butler 18185c45adf0SJesse Butler /* 18195c45adf0SJesse Butler * Perform a 'hot' reset, which will soft reset the chip and 18205c45adf0SJesse Butler * restore the state back to pre-reset context. Called with pwp 18215c45adf0SJesse Butler * lock held. 18225c45adf0SJesse Butler */ 18235c45adf0SJesse Butler int 18245c45adf0SJesse Butler pmcs_hot_reset(pmcs_hw_t *pwp) 18255c45adf0SJesse Butler { 18265c45adf0SJesse Butler pmcs_iport_t *iport; 18275c45adf0SJesse Butler 18285c45adf0SJesse Butler ASSERT(mutex_owned(&pwp->lock)); 18295c45adf0SJesse Butler pwp->state = STATE_IN_RESET; 18305c45adf0SJesse Butler 18315c45adf0SJesse Butler /* 18325c45adf0SJesse Butler * For any iports on this HBA, report empty target sets and 18335c45adf0SJesse Butler * then tear them down. 18345c45adf0SJesse Butler */ 18355c45adf0SJesse Butler rw_enter(&pwp->iports_lock, RW_READER); 18365c45adf0SJesse Butler for (iport = list_head(&pwp->iports); iport != NULL; 18375c45adf0SJesse Butler iport = list_next(&pwp->iports, iport)) { 18385c45adf0SJesse Butler mutex_enter(&iport->lock); 18395c45adf0SJesse Butler (void) scsi_hba_tgtmap_set_begin(iport->iss_tgtmap); 18405c45adf0SJesse Butler (void) scsi_hba_tgtmap_set_end(iport->iss_tgtmap, 0); 18415c45adf0SJesse Butler pmcs_iport_teardown_phys(iport); 18425c45adf0SJesse Butler mutex_exit(&iport->lock); 18435c45adf0SJesse Butler } 18445c45adf0SJesse Butler rw_exit(&pwp->iports_lock); 18455c45adf0SJesse Butler 18465c45adf0SJesse Butler /* Grab a register dump, in the event that reset fails */ 18475c45adf0SJesse Butler pmcs_register_dump_int(pwp); 18485c45adf0SJesse Butler mutex_exit(&pwp->lock); 18495c45adf0SJesse Butler 185032b54db7SJesse Butler /* Ensure discovery is not running before we proceed */ 185132b54db7SJesse Butler mutex_enter(&pwp->config_lock); 185232b54db7SJesse Butler while (pwp->configuring) { 185332b54db7SJesse Butler cv_wait(&pwp->config_cv, &pwp->config_lock); 185432b54db7SJesse Butler } 185532b54db7SJesse Butler mutex_exit(&pwp->config_lock); 185632b54db7SJesse Butler 18575c45adf0SJesse Butler /* Issue soft reset and clean up related softstate */ 18585c45adf0SJesse Butler if (pmcs_soft_reset(pwp, B_FALSE)) { 18595c45adf0SJesse Butler /* 18605c45adf0SJesse Butler * Disable interrupts, in case we got far enough along to 18615c45adf0SJesse Butler * enable them, then fire off ereport and service impact. 18625c45adf0SJesse Butler */ 18635c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 18645c45adf0SJesse Butler "%s: failed soft reset", __func__); 18655c45adf0SJesse Butler pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff); 18665c45adf0SJesse Butler pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff); 18675c45adf0SJesse Butler pmcs_fm_ereport(pwp, DDI_FM_DEVICE_NO_RESPONSE); 18685c45adf0SJesse Butler ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST); 18695c45adf0SJesse Butler mutex_enter(&pwp->lock); 18705c45adf0SJesse Butler pwp->state = STATE_DEAD; 18715c45adf0SJesse Butler return (DDI_FAILURE); 18725c45adf0SJesse Butler } 18735c45adf0SJesse Butler 18745c45adf0SJesse Butler mutex_enter(&pwp->lock); 18755c45adf0SJesse Butler pwp->state = STATE_RUNNING; 18765c45adf0SJesse Butler mutex_exit(&pwp->lock); 18775c45adf0SJesse Butler 18785c45adf0SJesse Butler /* 18795c45adf0SJesse Butler * Finally, restart the phys, which will bring the iports back 18805c45adf0SJesse Butler * up and eventually result in discovery running. 18815c45adf0SJesse Butler */ 18825c45adf0SJesse Butler if (pmcs_start_phys(pwp)) { 18835c45adf0SJesse Butler /* We should be up and running now, so retry */ 18845c45adf0SJesse Butler if (pmcs_start_phys(pwp)) { 18855c45adf0SJesse Butler /* Apparently unable to restart PHYs, fail */ 18865c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 18875c45adf0SJesse Butler "%s: failed to restart PHYs after soft reset", 18885c45adf0SJesse Butler __func__); 18895c45adf0SJesse Butler mutex_enter(&pwp->lock); 18905c45adf0SJesse Butler return (DDI_FAILURE); 18915c45adf0SJesse Butler } 18925c45adf0SJesse Butler } 18935c45adf0SJesse Butler 18945c45adf0SJesse Butler mutex_enter(&pwp->lock); 18955c45adf0SJesse Butler return (DDI_SUCCESS); 18965c45adf0SJesse Butler } 18975c45adf0SJesse Butler 18984c06356bSdh /* 18994c06356bSdh * Reset a device or a logical unit. 19004c06356bSdh */ 19014c06356bSdh int 19024c06356bSdh pmcs_reset_dev(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint64_t lun) 19034c06356bSdh { 19044c06356bSdh int rval = 0; 19054c06356bSdh 19064c06356bSdh if (pptr == NULL) { 19074c06356bSdh return (ENXIO); 19084c06356bSdh } 19094c06356bSdh 19104c06356bSdh pmcs_lock_phy(pptr); 19114c06356bSdh if (pptr->dtype == SAS) { 19124c06356bSdh /* 19134c06356bSdh * Some devices do not support SAS_I_T_NEXUS_RESET as 19144c06356bSdh * it is not a mandatory (in SAM4) task management 19154c06356bSdh * function, while LOGIC_UNIT_RESET is mandatory. 19164c06356bSdh * 19174c06356bSdh * The problem here is that we need to iterate over 19184c06356bSdh * all known LUNs to emulate the semantics of 19194c06356bSdh * "RESET_TARGET". 19204c06356bSdh * 19214c06356bSdh * XXX: FIX ME 19224c06356bSdh */ 19234c06356bSdh if (lun == (uint64_t)-1) { 19244c06356bSdh lun = 0; 19254c06356bSdh } 19264c06356bSdh rval = pmcs_ssp_tmf(pwp, pptr, SAS_LOGICAL_UNIT_RESET, 0, lun, 19274c06356bSdh NULL); 19284c06356bSdh } else if (pptr->dtype == SATA) { 19294c06356bSdh if (lun != 0ull) { 19304c06356bSdh pmcs_unlock_phy(pptr); 19314c06356bSdh return (EINVAL); 19324c06356bSdh } 19334c06356bSdh rval = pmcs_reset_phy(pwp, pptr, PMCS_PHYOP_LINK_RESET); 19344c06356bSdh } else { 19354c06356bSdh pmcs_unlock_phy(pptr); 1936c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 19374c06356bSdh "%s: cannot reset a SMP device yet (%s)", 19384c06356bSdh __func__, pptr->path); 19394c06356bSdh return (EINVAL); 19404c06356bSdh } 19414c06356bSdh 19424c06356bSdh /* 19434c06356bSdh * Now harvest any commands killed by this action 19444c06356bSdh * by issuing an ABORT for all commands on this device. 19454c06356bSdh * 19464c06356bSdh * We do this even if the the tmf or reset fails (in case there 19474c06356bSdh * are any dead commands around to be harvested *anyway*). 19484c06356bSdh * We don't have to await for the abort to complete. 19494c06356bSdh */ 19504c06356bSdh if (pmcs_abort(pwp, pptr, 0, 1, 0)) { 19514c06356bSdh pptr->abort_pending = 1; 19524c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE); 19534c06356bSdh } 19544c06356bSdh 19554c06356bSdh pmcs_unlock_phy(pptr); 19564c06356bSdh return (rval); 19574c06356bSdh } 19584c06356bSdh 19594c06356bSdh /* 19604c06356bSdh * Called with PHY locked. 19614c06356bSdh */ 19624c06356bSdh static int 19634c06356bSdh pmcs_get_device_handle(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 19644c06356bSdh { 19654c06356bSdh if (pptr->valid_device_id == 0) { 19664c06356bSdh int result = pmcs_register_device(pwp, pptr); 19674c06356bSdh 19684c06356bSdh /* 19694c06356bSdh * If we changed while registering, punt 19704c06356bSdh */ 19714c06356bSdh if (pptr->changed) { 19724c06356bSdh RESTART_DISCOVERY(pwp); 19734c06356bSdh return (-1); 19744c06356bSdh } 19754c06356bSdh 19764c06356bSdh /* 19774c06356bSdh * If we had a failure to register, check against errors. 19784c06356bSdh * An ENOMEM error means we just retry (temp resource shortage). 19794c06356bSdh */ 19804c06356bSdh if (result == ENOMEM) { 19814c06356bSdh PHY_CHANGED(pwp, pptr); 19824c06356bSdh RESTART_DISCOVERY(pwp); 19834c06356bSdh return (-1); 19844c06356bSdh } 19854c06356bSdh 19864c06356bSdh /* 19874c06356bSdh * An ETIMEDOUT error means we retry (if our counter isn't 19884c06356bSdh * exhausted) 19894c06356bSdh */ 19904c06356bSdh if (result == ETIMEDOUT) { 19914c06356bSdh if (ddi_get_lbolt() < pptr->config_stop) { 19924c06356bSdh PHY_CHANGED(pwp, pptr); 19934c06356bSdh RESTART_DISCOVERY(pwp); 19944c06356bSdh } else { 1995c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 19964c06356bSdh "%s: Retries exhausted for %s, killing", 19974c06356bSdh __func__, pptr->path); 19984c06356bSdh pptr->config_stop = 0; 19994c06356bSdh pmcs_kill_changed(pwp, pptr, 0); 20004c06356bSdh } 20014c06356bSdh return (-1); 20024c06356bSdh } 20034c06356bSdh /* 20044c06356bSdh * Other errors or no valid device id is fatal, but don't 20054c06356bSdh * preclude a future action. 20064c06356bSdh */ 20074c06356bSdh if (result || pptr->valid_device_id == 0) { 2008c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 2009c3bc407cSdh "%s: %s could not be registered", __func__, 2010c3bc407cSdh pptr->path); 20114c06356bSdh return (-1); 20124c06356bSdh } 20134c06356bSdh } 20144c06356bSdh return (0); 20154c06356bSdh } 20164c06356bSdh 20174c06356bSdh int 20184c06356bSdh pmcs_iport_tgtmap_create(pmcs_iport_t *iport) 20194c06356bSdh { 20204c06356bSdh ASSERT(iport); 20214c06356bSdh if (iport == NULL) 20224c06356bSdh return (B_FALSE); 20234c06356bSdh 2024c3bc407cSdh pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s", __func__); 20254c06356bSdh 20264c06356bSdh /* create target map */ 20274c06356bSdh if (scsi_hba_tgtmap_create(iport->dip, SCSI_TM_FULLSET, tgtmap_usec, 20289aed1621SDavid Hollister (void *)iport, pmcs_tgtmap_activate_cb, pmcs_tgtmap_deactivate_cb, 20299aed1621SDavid Hollister &iport->iss_tgtmap) != DDI_SUCCESS) { 2030c3bc407cSdh pmcs_prt(iport->pwp, PMCS_PRT_DEBUG, NULL, NULL, 20314c06356bSdh "%s: failed to create tgtmap", __func__); 20324c06356bSdh return (B_FALSE); 20334c06356bSdh } 20344c06356bSdh return (B_TRUE); 20354c06356bSdh } 20364c06356bSdh 20374c06356bSdh int 20384c06356bSdh pmcs_iport_tgtmap_destroy(pmcs_iport_t *iport) 20394c06356bSdh { 20404c06356bSdh ASSERT(iport && iport->iss_tgtmap); 20414c06356bSdh if ((iport == NULL) || (iport->iss_tgtmap == NULL)) 20424c06356bSdh return (B_FALSE); 20434c06356bSdh 2044c3bc407cSdh pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s", __func__); 20454c06356bSdh 20464c06356bSdh /* destroy target map */ 20474c06356bSdh scsi_hba_tgtmap_destroy(iport->iss_tgtmap); 20484c06356bSdh return (B_TRUE); 20494c06356bSdh } 20504c06356bSdh 2051f73ae3dbSJesse Butler /* 2052f73ae3dbSJesse Butler * Remove all phys from an iport's phymap and empty it's phylist. 20535c45adf0SJesse Butler * Called when a port has been reset by the host (see pmcs_intr.c) 20545c45adf0SJesse Butler * or prior to issuing a soft reset if we detect a stall on the chip 20555c45adf0SJesse Butler * (see pmcs_attach.c). 2056f73ae3dbSJesse Butler */ 2057f73ae3dbSJesse Butler void 2058f73ae3dbSJesse Butler pmcs_iport_teardown_phys(pmcs_iport_t *iport) 2059f73ae3dbSJesse Butler { 2060f73ae3dbSJesse Butler pmcs_hw_t *pwp; 2061f73ae3dbSJesse Butler sas_phymap_phys_t *phys; 2062f73ae3dbSJesse Butler int phynum; 2063f73ae3dbSJesse Butler 2064f73ae3dbSJesse Butler ASSERT(iport); 2065f73ae3dbSJesse Butler ASSERT(mutex_owned(&iport->lock)); 2066f73ae3dbSJesse Butler pwp = iport->pwp; 2067f73ae3dbSJesse Butler ASSERT(pwp); 2068f73ae3dbSJesse Butler 2069f73ae3dbSJesse Butler /* 2070f73ae3dbSJesse Butler * Remove all phys from the iport handle's phy list, unset its 2071f73ae3dbSJesse Butler * primary phy and update its state. 2072f73ae3dbSJesse Butler */ 2073f73ae3dbSJesse Butler pmcs_remove_phy_from_iport(iport, NULL); 2074f73ae3dbSJesse Butler iport->pptr = NULL; 2075f73ae3dbSJesse Butler iport->ua_state = UA_PEND_DEACTIVATE; 2076f73ae3dbSJesse Butler 2077f73ae3dbSJesse Butler /* Remove all phys from the phymap */ 2078f73ae3dbSJesse Butler phys = sas_phymap_ua2phys(pwp->hss_phymap, iport->ua); 20795c45adf0SJesse Butler if (phys) { 20805c45adf0SJesse Butler while ((phynum = sas_phymap_phys_next(phys)) != -1) { 20815c45adf0SJesse Butler (void) sas_phymap_phy_rem(pwp->hss_phymap, phynum); 20825c45adf0SJesse Butler } 20835c45adf0SJesse Butler sas_phymap_phys_free(phys); 2084f73ae3dbSJesse Butler } 2085f73ae3dbSJesse Butler } 2086f73ae3dbSJesse Butler 20874c06356bSdh /* 20884c06356bSdh * Query the phymap and populate the iport handle passed in. 20894c06356bSdh * Called with iport lock held. 20904c06356bSdh */ 20914c06356bSdh int 20924c06356bSdh pmcs_iport_configure_phys(pmcs_iport_t *iport) 20934c06356bSdh { 20944c06356bSdh pmcs_hw_t *pwp; 20954c06356bSdh pmcs_phy_t *pptr; 20964c06356bSdh sas_phymap_phys_t *phys; 20974c06356bSdh int phynum; 20984c06356bSdh int inst; 20994c06356bSdh 21004c06356bSdh ASSERT(iport); 21014c06356bSdh ASSERT(mutex_owned(&iport->lock)); 21024c06356bSdh pwp = iport->pwp; 21034c06356bSdh ASSERT(pwp); 21044c06356bSdh inst = ddi_get_instance(iport->dip); 21054c06356bSdh 21064c06356bSdh mutex_enter(&pwp->lock); 21074c06356bSdh ASSERT(pwp->root_phys != NULL); 21084c06356bSdh 21094c06356bSdh /* 21104c06356bSdh * Query the phymap regarding the phys in this iport and populate 21114c06356bSdh * the iport's phys list. Hereafter this list is maintained via 21124c06356bSdh * port up and down events in pmcs_intr.c 21134c06356bSdh */ 21144c06356bSdh ASSERT(list_is_empty(&iport->phys)); 21154c06356bSdh phys = sas_phymap_ua2phys(pwp->hss_phymap, iport->ua); 21165c45adf0SJesse Butler ASSERT(phys != NULL); 21174c06356bSdh while ((phynum = sas_phymap_phys_next(phys)) != -1) { 21184c06356bSdh /* Grab the phy pointer from root_phys */ 21194c06356bSdh pptr = pwp->root_phys + phynum; 21204c06356bSdh ASSERT(pptr); 21214c06356bSdh pmcs_lock_phy(pptr); 21224c06356bSdh ASSERT(pptr->phynum == phynum); 21234c06356bSdh 21244c06356bSdh /* 21254c06356bSdh * Set a back pointer in the phy to this iport. 21264c06356bSdh */ 21274c06356bSdh pptr->iport = iport; 21284c06356bSdh 21294c06356bSdh /* 21304c06356bSdh * If this phy is the primary, set a pointer to it on our 21314c06356bSdh * iport handle, and set our portid from it. 21324c06356bSdh */ 21334c06356bSdh if (!pptr->subsidiary) { 21344c06356bSdh iport->pptr = pptr; 21354c06356bSdh iport->portid = pptr->portid; 21364c06356bSdh } 21374c06356bSdh 21384c06356bSdh /* 21394c06356bSdh * Finally, insert the phy into our list 21404c06356bSdh */ 21414c06356bSdh pmcs_unlock_phy(pptr); 2142145e0143Sdh pmcs_add_phy_to_iport(iport, pptr); 21434c06356bSdh 2144c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "%s: found " 2145c3bc407cSdh "phy %d [0x%p] on iport%d, refcnt(%d)", __func__, phynum, 21464c06356bSdh (void *)pptr, inst, iport->refcnt); 21474c06356bSdh } 21484c06356bSdh mutex_exit(&pwp->lock); 21494c06356bSdh sas_phymap_phys_free(phys); 21504c06356bSdh RESTART_DISCOVERY(pwp); 21514c06356bSdh return (DDI_SUCCESS); 21524c06356bSdh } 21534c06356bSdh 21544c06356bSdh /* 21554c06356bSdh * Return the iport that ua is associated with, or NULL. If an iport is 21564c06356bSdh * returned, it will be held and the caller must release the hold. 21574c06356bSdh */ 21584c06356bSdh static pmcs_iport_t * 21594c06356bSdh pmcs_get_iport_by_ua(pmcs_hw_t *pwp, char *ua) 21604c06356bSdh { 21614c06356bSdh pmcs_iport_t *iport = NULL; 21624c06356bSdh 21634c06356bSdh rw_enter(&pwp->iports_lock, RW_READER); 21644c06356bSdh for (iport = list_head(&pwp->iports); 21654c06356bSdh iport != NULL; 21664c06356bSdh iport = list_next(&pwp->iports, iport)) { 21674c06356bSdh mutex_enter(&iport->lock); 21684c06356bSdh if (strcmp(iport->ua, ua) == 0) { 21694c06356bSdh mutex_exit(&iport->lock); 21704c06356bSdh mutex_enter(&iport->refcnt_lock); 21714c06356bSdh iport->refcnt++; 21724c06356bSdh mutex_exit(&iport->refcnt_lock); 21734c06356bSdh break; 21744c06356bSdh } 21754c06356bSdh mutex_exit(&iport->lock); 21764c06356bSdh } 21774c06356bSdh rw_exit(&pwp->iports_lock); 21784c06356bSdh 21794c06356bSdh return (iport); 21804c06356bSdh } 21814c06356bSdh 21824c06356bSdh /* 21834c06356bSdh * Return the iport that pptr is associated with, or NULL. 21844c06356bSdh * If an iport is returned, there is a hold that the caller must release. 21854c06356bSdh */ 21864c06356bSdh pmcs_iport_t * 21879aed1621SDavid Hollister pmcs_get_iport_by_wwn(pmcs_hw_t *pwp, uint64_t wwn) 21884c06356bSdh { 21894c06356bSdh pmcs_iport_t *iport = NULL; 21904c06356bSdh char *ua; 21914c06356bSdh 21929aed1621SDavid Hollister ua = sas_phymap_lookup_ua(pwp->hss_phymap, pwp->sas_wwns[0], wwn); 21934c06356bSdh if (ua) { 21944c06356bSdh iport = pmcs_get_iport_by_ua(pwp, ua); 21954c06356bSdh if (iport) { 21964c06356bSdh mutex_enter(&iport->lock); 21979aed1621SDavid Hollister pmcs_iport_active(iport); 21989aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: " 21999aed1621SDavid Hollister "found iport [0x%p] on ua (%s), refcnt (%d)", 22009aed1621SDavid Hollister __func__, (void *)iport, ua, iport->refcnt); 22014c06356bSdh mutex_exit(&iport->lock); 22024c06356bSdh } 22034c06356bSdh } 22044c06356bSdh 22054c06356bSdh return (iport); 22064c06356bSdh } 22074c06356bSdh 2208f73ae3dbSJesse Butler /* 22096745c559SJesse Butler * Promote the next phy on this port to primary, and return it. 2210f73ae3dbSJesse Butler * Called when the primary PHY on a port is going down, but the port 2211f73ae3dbSJesse Butler * remains up (see pmcs_intr.c). 2212f73ae3dbSJesse Butler */ 2213f73ae3dbSJesse Butler pmcs_phy_t * 2214f73ae3dbSJesse Butler pmcs_promote_next_phy(pmcs_phy_t *prev_primary) 2215f73ae3dbSJesse Butler { 22166745c559SJesse Butler pmcs_hw_t *pwp; 22176745c559SJesse Butler pmcs_iport_t *iport; 22186745c559SJesse Butler pmcs_phy_t *pptr, *child; 22196745c559SJesse Butler int portid; 2220f73ae3dbSJesse Butler 22216745c559SJesse Butler pmcs_lock_phy(prev_primary); 22226745c559SJesse Butler portid = prev_primary->portid; 22236745c559SJesse Butler iport = prev_primary->iport; 22246745c559SJesse Butler pwp = prev_primary->pwp; 2225f73ae3dbSJesse Butler 22266745c559SJesse Butler /* Use the first available phy in this port */ 22276745c559SJesse Butler for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) { 22286745c559SJesse Butler if ((pptr->portid == portid) && (pptr != prev_primary)) { 22296745c559SJesse Butler mutex_enter(&pptr->phy_lock); 2230f73ae3dbSJesse Butler break; 2231f73ae3dbSJesse Butler } 2232f73ae3dbSJesse Butler } 2233f73ae3dbSJesse Butler 2234f73ae3dbSJesse Butler if (pptr == NULL) { 22356745c559SJesse Butler pmcs_unlock_phy(prev_primary); 2236f73ae3dbSJesse Butler return (NULL); 2237f73ae3dbSJesse Butler } 2238f73ae3dbSJesse Butler 22396745c559SJesse Butler if (iport) { 22406745c559SJesse Butler mutex_enter(&iport->lock); 22416745c559SJesse Butler iport->pptr = pptr; 22426745c559SJesse Butler mutex_exit(&iport->lock); 22436745c559SJesse Butler } 2244f73ae3dbSJesse Butler 22456745c559SJesse Butler /* Update the phy handle with the data from the previous primary */ 2246f73ae3dbSJesse Butler pptr->children = prev_primary->children; 2247f73ae3dbSJesse Butler child = pptr->children; 2248f73ae3dbSJesse Butler while (child) { 2249f73ae3dbSJesse Butler child->parent = pptr; 2250f73ae3dbSJesse Butler child = child->sibling; 2251f73ae3dbSJesse Butler } 2252f73ae3dbSJesse Butler pptr->ncphy = prev_primary->ncphy; 2253f73ae3dbSJesse Butler pptr->width = prev_primary->width; 2254f73ae3dbSJesse Butler pptr->dtype = prev_primary->dtype; 2255f73ae3dbSJesse Butler pptr->pend_dtype = prev_primary->pend_dtype; 2256f73ae3dbSJesse Butler pptr->tolerates_sas2 = prev_primary->tolerates_sas2; 2257f73ae3dbSJesse Butler pptr->atdt = prev_primary->atdt; 2258f73ae3dbSJesse Butler pptr->portid = prev_primary->portid; 2259f73ae3dbSJesse Butler pptr->link_rate = prev_primary->link_rate; 2260f73ae3dbSJesse Butler pptr->configured = prev_primary->configured; 2261f73ae3dbSJesse Butler pptr->iport = prev_primary->iport; 2262f73ae3dbSJesse Butler pptr->target = prev_primary->target; 22636745c559SJesse Butler if (pptr->target) { 22646745c559SJesse Butler pptr->target->phy = pptr; 22656745c559SJesse Butler } 2266499cfd15SDavid Hollister 2267499cfd15SDavid Hollister /* Update the phy mask properties for the affected PHYs */ 2268499cfd15SDavid Hollister /* Clear the current values... */ 2269499cfd15SDavid Hollister pmcs_update_phy_pm_props(pptr, pptr->att_port_pm_tmp, 2270499cfd15SDavid Hollister pptr->tgt_port_pm_tmp, B_FALSE); 2271499cfd15SDavid Hollister /* ...replace with the values from prev_primary... */ 2272499cfd15SDavid Hollister pmcs_update_phy_pm_props(pptr, prev_primary->att_port_pm_tmp, 2273499cfd15SDavid Hollister prev_primary->tgt_port_pm_tmp, B_TRUE); 2274499cfd15SDavid Hollister /* ...then clear prev_primary's PHY values from the new primary */ 2275499cfd15SDavid Hollister pmcs_update_phy_pm_props(pptr, prev_primary->att_port_pm, 2276499cfd15SDavid Hollister prev_primary->tgt_port_pm, B_FALSE); 2277499cfd15SDavid Hollister /* Clear the prev_primary's values */ 2278499cfd15SDavid Hollister pmcs_update_phy_pm_props(prev_primary, prev_primary->att_port_pm_tmp, 2279499cfd15SDavid Hollister prev_primary->tgt_port_pm_tmp, B_FALSE); 2280499cfd15SDavid Hollister 2281f73ae3dbSJesse Butler pptr->subsidiary = 0; 2282f73ae3dbSJesse Butler 2283f73ae3dbSJesse Butler prev_primary->subsidiary = 1; 2284f73ae3dbSJesse Butler prev_primary->children = NULL; 22856745c559SJesse Butler prev_primary->target = NULL; 2286601c90f1SSrikanth, Ramana pptr->device_id = prev_primary->device_id; 2287601c90f1SSrikanth, Ramana pptr->valid_device_id = 1; 2288f73ae3dbSJesse Butler pmcs_unlock_phy(prev_primary); 2289f73ae3dbSJesse Butler 2290f73ae3dbSJesse Butler /* 2291f73ae3dbSJesse Butler * We call pmcs_unlock_phy() on pptr because it now contains the 2292f73ae3dbSJesse Butler * list of children. 2293f73ae3dbSJesse Butler */ 2294f73ae3dbSJesse Butler pmcs_unlock_phy(pptr); 2295f73ae3dbSJesse Butler 2296f73ae3dbSJesse Butler return (pptr); 2297f73ae3dbSJesse Butler } 2298f73ae3dbSJesse Butler 22994c06356bSdh void 23004c06356bSdh pmcs_rele_iport(pmcs_iport_t *iport) 23014c06356bSdh { 23024c06356bSdh /* 23034c06356bSdh * Release a refcnt on this iport. If this is the last reference, 23044c06356bSdh * signal the potential waiter in pmcs_iport_unattach(). 23054c06356bSdh */ 23064c06356bSdh ASSERT(iport->refcnt > 0); 23074c06356bSdh mutex_enter(&iport->refcnt_lock); 23084c06356bSdh iport->refcnt--; 23094c06356bSdh mutex_exit(&iport->refcnt_lock); 23104c06356bSdh if (iport->refcnt == 0) { 23114c06356bSdh cv_signal(&iport->refcnt_cv); 23124c06356bSdh } 2313c3bc407cSdh pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: iport " 2314c3bc407cSdh "[0x%p] refcnt (%d)", __func__, (void *)iport, iport->refcnt); 23154c06356bSdh } 23164c06356bSdh 23174c06356bSdh void 23184c06356bSdh pmcs_phymap_activate(void *arg, char *ua, void **privp) 23194c06356bSdh { 23204c06356bSdh _NOTE(ARGUNUSED(privp)); 23214c06356bSdh pmcs_hw_t *pwp = arg; 23224c06356bSdh pmcs_iport_t *iport = NULL; 23234c06356bSdh 23244c06356bSdh mutex_enter(&pwp->lock); 23254c06356bSdh if ((pwp->state == STATE_UNPROBING) || (pwp->state == STATE_DEAD)) { 23264c06356bSdh mutex_exit(&pwp->lock); 23274c06356bSdh return; 23284c06356bSdh } 23294c06356bSdh pwp->phymap_active++; 23304c06356bSdh mutex_exit(&pwp->lock); 23314c06356bSdh 23324c06356bSdh if (scsi_hba_iportmap_iport_add(pwp->hss_iportmap, ua, NULL) != 23334c06356bSdh DDI_SUCCESS) { 2334c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: failed to " 2335c3bc407cSdh "add iport handle on unit address [%s]", __func__, ua); 23364c06356bSdh } else { 2337c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: " 2338c3bc407cSdh "phymap_active count (%d), added iport handle on unit " 2339c3bc407cSdh "address [%s]", __func__, pwp->phymap_active, ua); 23404c06356bSdh } 23414c06356bSdh 23424c06356bSdh /* Set the HBA softstate as our private data for this unit address */ 23434c06356bSdh *privp = (void *)pwp; 23444c06356bSdh 23454c06356bSdh /* 23464c06356bSdh * We are waiting on attach for this iport node, unless it is still 23474c06356bSdh * attached. This can happen if a consumer has an outstanding open 23484c06356bSdh * on our iport node, but the port is down. If this is the case, we 23494c06356bSdh * need to configure our iport here for reuse. 23504c06356bSdh */ 23514c06356bSdh iport = pmcs_get_iport_by_ua(pwp, ua); 23524c06356bSdh if (iport) { 23534c06356bSdh mutex_enter(&iport->lock); 23544c06356bSdh if (pmcs_iport_configure_phys(iport) != DDI_SUCCESS) { 2355c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: " 23564c06356bSdh "failed to configure phys on iport [0x%p] at " 23574c06356bSdh "unit address (%s)", __func__, (void *)iport, ua); 23584c06356bSdh } 23599aed1621SDavid Hollister pmcs_iport_active(iport); 23604c06356bSdh pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS, 23614c06356bSdh &iport->nphy); 23624c06356bSdh mutex_exit(&iport->lock); 23634c06356bSdh pmcs_rele_iport(iport); 23644c06356bSdh } 23654c06356bSdh 23664c06356bSdh } 23674c06356bSdh 23684c06356bSdh void 23694c06356bSdh pmcs_phymap_deactivate(void *arg, char *ua, void *privp) 23704c06356bSdh { 23714c06356bSdh _NOTE(ARGUNUSED(privp)); 23724c06356bSdh pmcs_hw_t *pwp = arg; 23734c06356bSdh pmcs_iport_t *iport; 23744c06356bSdh 23754c06356bSdh mutex_enter(&pwp->lock); 23764c06356bSdh pwp->phymap_active--; 23774c06356bSdh mutex_exit(&pwp->lock); 23784c06356bSdh 23794c06356bSdh if (scsi_hba_iportmap_iport_remove(pwp->hss_iportmap, ua) != 23804c06356bSdh DDI_SUCCESS) { 2381c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: failed to " 2382c3bc407cSdh "remove iport handle on unit address [%s]", __func__, ua); 23834c06356bSdh } else { 2384c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: " 2385c3bc407cSdh "phymap_active count (%d), removed iport handle on unit " 2386c3bc407cSdh "address [%s]", __func__, pwp->phymap_active, ua); 23874c06356bSdh } 23884c06356bSdh 23894c06356bSdh iport = pmcs_get_iport_by_ua(pwp, ua); 23904c06356bSdh 23914c06356bSdh if (iport == NULL) { 2392c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: failed " 2393c3bc407cSdh "lookup of iport handle on unit addr (%s)", __func__, ua); 23944c06356bSdh return; 23954c06356bSdh } 23964c06356bSdh 23974c06356bSdh mutex_enter(&iport->lock); 23984c06356bSdh iport->ua_state = UA_INACTIVE; 23994c06356bSdh iport->portid = PMCS_IPORT_INVALID_PORT_ID; 24004c06356bSdh pmcs_remove_phy_from_iport(iport, NULL); 24014c06356bSdh mutex_exit(&iport->lock); 24024c06356bSdh pmcs_rele_iport(iport); 24034c06356bSdh } 24044c06356bSdh 24054c06356bSdh /* 24064c06356bSdh * Top-level discovery function 24074c06356bSdh */ 24084c06356bSdh void 24094c06356bSdh pmcs_discover(pmcs_hw_t *pwp) 24104c06356bSdh { 24114c06356bSdh pmcs_phy_t *pptr; 24124c06356bSdh pmcs_phy_t *root_phy; 24134c06356bSdh 24144c06356bSdh DTRACE_PROBE2(pmcs__discover__entry, ulong_t, pwp->work_flags, 24154c06356bSdh boolean_t, pwp->config_changed); 24164c06356bSdh 24174c06356bSdh mutex_enter(&pwp->lock); 24184c06356bSdh 24194c06356bSdh if (pwp->state != STATE_RUNNING) { 24204c06356bSdh mutex_exit(&pwp->lock); 24214c06356bSdh return; 24224c06356bSdh } 24234c06356bSdh 24244c06356bSdh /* Ensure we have at least one phymap active */ 24254c06356bSdh if (pwp->phymap_active == 0) { 24264c06356bSdh mutex_exit(&pwp->lock); 2427c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 24284c06356bSdh "%s: phymap inactive, exiting", __func__); 24294c06356bSdh return; 24304c06356bSdh } 24314c06356bSdh 24324c06356bSdh mutex_exit(&pwp->lock); 24334c06356bSdh 24344c06356bSdh /* 24354c06356bSdh * If no iports have attached, but we have PHYs that are up, we 24364c06356bSdh * are waiting for iport attach to complete. Restart discovery. 24374c06356bSdh */ 24384c06356bSdh rw_enter(&pwp->iports_lock, RW_READER); 24394c06356bSdh if (!pwp->iports_attached) { 24404c06356bSdh rw_exit(&pwp->iports_lock); 2441c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 24424c06356bSdh "%s: no iports attached, retry discovery", __func__); 24434c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER); 24444c06356bSdh return; 24454c06356bSdh } 24464c06356bSdh rw_exit(&pwp->iports_lock); 24474c06356bSdh 24484c06356bSdh mutex_enter(&pwp->config_lock); 24494c06356bSdh if (pwp->configuring) { 24504c06356bSdh mutex_exit(&pwp->config_lock); 2451c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 24524c06356bSdh "%s: configuration already in progress", __func__); 24534c06356bSdh return; 24544c06356bSdh } 24554c06356bSdh 24564c06356bSdh if (pmcs_acquire_scratch(pwp, B_FALSE)) { 24574c06356bSdh mutex_exit(&pwp->config_lock); 2458c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 24594c06356bSdh "%s: cannot allocate scratch", __func__); 24604c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER); 24614c06356bSdh return; 24624c06356bSdh } 24634c06356bSdh 24644c06356bSdh pwp->configuring = 1; 24654c06356bSdh pwp->config_changed = B_FALSE; 24664c06356bSdh mutex_exit(&pwp->config_lock); 24674c06356bSdh 2468c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "Discovery begin"); 24694c06356bSdh 2470601c90f1SSrikanth, Ramana /* 2471601c90f1SSrikanth, Ramana * First, tell SCSA that we're beginning set operations. 2472601c90f1SSrikanth, Ramana */ 2473601c90f1SSrikanth, Ramana pmcs_begin_observations(pwp); 2474601c90f1SSrikanth, Ramana 24754c06356bSdh /* 24764c06356bSdh * The order of the following traversals is important. 24774c06356bSdh * 24784c06356bSdh * The first one checks for changed expanders. 24794c06356bSdh * 24804c06356bSdh * The second one aborts commands for dead devices and deregisters them. 24814c06356bSdh * 24824c06356bSdh * The third one clears the contents of dead expanders from the tree 24834c06356bSdh * 24844c06356bSdh * The fourth one clears now dead devices in expanders that remain. 24854c06356bSdh */ 24864c06356bSdh 24874c06356bSdh /* 24884c06356bSdh * 1. Check expanders marked changed (but not dead) to see if they still 24894c06356bSdh * have the same number of phys and the same SAS address. Mark them, 24904c06356bSdh * their subsidiary phys (if wide) and their descendents dead if 24914c06356bSdh * anything has changed. Check the devices they contain to see if 24924c06356bSdh * *they* have changed. If they've changed from type NOTHING we leave 24934c06356bSdh * them marked changed to be configured later (picking up a new SAS 24944c06356bSdh * address and link rate if possible). Otherwise, any change in type, 24954c06356bSdh * SAS address or removal of target role will cause us to mark them 24964c06356bSdh * (and their descendents) as dead (and cause any pending commands 24974c06356bSdh * and associated devices to be removed). 2498ec2c44b8Sdh * 2499ec2c44b8Sdh * NOTE: We don't want to bail on discovery if the config has 2500ec2c44b8Sdh * changed until *after* we run pmcs_kill_devices. 25014c06356bSdh */ 25024c06356bSdh root_phy = pwp->root_phys; 250373a3eccdSDavid Hollister pmcs_check_expanders(pwp, root_phy); 25044c06356bSdh 25054c06356bSdh /* 25064c06356bSdh * 2. Descend the tree looking for dead devices and kill them 25074c06356bSdh * by aborting all active commands and then deregistering them. 25084c06356bSdh */ 250973a3eccdSDavid Hollister if (pmcs_kill_devices(pwp, root_phy)) { 251073a3eccdSDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 251173a3eccdSDavid Hollister "%s: pmcs_kill_devices failed!", __func__); 25124c06356bSdh } 25134c06356bSdh 25144c06356bSdh /* 25154c06356bSdh * 3. Check for dead expanders and remove their children from the tree. 25164c06356bSdh * By the time we get here, the devices and commands for them have 25174c06356bSdh * already been terminated and removed. 25184c06356bSdh * 25194c06356bSdh * We do this independent of the configuration count changing so we can 25204c06356bSdh * free any dead device PHYs that were discovered while checking 25214c06356bSdh * expanders. We ignore any subsidiary phys as pmcs_clear_expander 25224c06356bSdh * will take care of those. 25234c06356bSdh * 25244c06356bSdh * NOTE: pmcs_clear_expander requires softstate lock 25254c06356bSdh */ 25264c06356bSdh mutex_enter(&pwp->lock); 25274c06356bSdh for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) { 25284c06356bSdh /* 25294c06356bSdh * Call pmcs_clear_expander for every root PHY. It will 25304c06356bSdh * recurse and determine which (if any) expanders actually 25314c06356bSdh * need to be cleared. 25324c06356bSdh */ 25334c06356bSdh pmcs_lock_phy(pptr); 25344c06356bSdh pmcs_clear_expander(pwp, pptr, 0); 25354c06356bSdh pmcs_unlock_phy(pptr); 25364c06356bSdh } 25374c06356bSdh mutex_exit(&pwp->lock); 25384c06356bSdh 25394c06356bSdh /* 25404c06356bSdh * 4. Check for dead devices and nullify them. By the time we get here, 25414c06356bSdh * the devices and commands for them have already been terminated 25424c06356bSdh * and removed. This is different from step 2 in that this just nulls 25434c06356bSdh * phys that are part of expanders that are still here but used to 25444c06356bSdh * be something but are no longer something (e.g., after a pulled 25454c06356bSdh * disk drive). Note that dead expanders had their contained phys 25464c06356bSdh * removed from the tree- here, the expanders themselves are 25474c06356bSdh * nullified (unless they were removed by being contained in another 25484c06356bSdh * expander phy). 25494c06356bSdh */ 25504c06356bSdh pmcs_clear_phys(pwp, root_phy); 25514c06356bSdh 25524c06356bSdh /* 25534c06356bSdh * 5. Now check for and configure new devices. 25544c06356bSdh */ 25554c06356bSdh if (pmcs_configure_new_devices(pwp, root_phy)) { 25564c06356bSdh goto restart; 25574c06356bSdh } 25584c06356bSdh 25594c06356bSdh out: 25604c06356bSdh DTRACE_PROBE2(pmcs__discover__exit, ulong_t, pwp->work_flags, 25614c06356bSdh boolean_t, pwp->config_changed); 2562c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "Discovery end"); 25634c06356bSdh 25644c06356bSdh mutex_enter(&pwp->config_lock); 25654c06356bSdh 25664c06356bSdh if (pwp->config_changed == B_FALSE) { 25674c06356bSdh /* 25684c06356bSdh * Observation is stable, report what we currently see to 25694c06356bSdh * the tgtmaps for delta processing. Start by setting 25704c06356bSdh * BEGIN on all tgtmaps. 25714c06356bSdh */ 25724c06356bSdh mutex_exit(&pwp->config_lock); 25734c06356bSdh if (pmcs_report_observations(pwp) == B_FALSE) { 25744c06356bSdh goto restart; 25754c06356bSdh } 25764c06356bSdh mutex_enter(&pwp->config_lock); 25774c06356bSdh } else { 25784c06356bSdh /* 25794c06356bSdh * If config_changed is TRUE, we need to reschedule 25804c06356bSdh * discovery now. 25814c06356bSdh */ 2582c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 25834c06356bSdh "%s: Config has changed, will re-run discovery", __func__); 25844c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER); 25854c06356bSdh } 25864c06356bSdh 25874c06356bSdh pmcs_release_scratch(pwp); 25885c45adf0SJesse Butler if (!pwp->quiesced) { 25895c45adf0SJesse Butler pwp->blocked = 0; 25905c45adf0SJesse Butler } 25914c06356bSdh pwp->configuring = 0; 259232b54db7SJesse Butler cv_signal(&pwp->config_cv); 25934c06356bSdh mutex_exit(&pwp->config_lock); 25944c06356bSdh 25954c06356bSdh #ifdef DEBUG 25964c06356bSdh pptr = pmcs_find_phy_needing_work(pwp, pwp->root_phys); 25974c06356bSdh if (pptr != NULL) { 25984c06356bSdh if (!WORK_IS_SCHEDULED(pwp, PMCS_WORK_DISCOVER)) { 2599c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 26004c06356bSdh "PHY %s dead=%d changed=%d configured=%d " 26014c06356bSdh "but no work scheduled", pptr->path, pptr->dead, 26024c06356bSdh pptr->changed, pptr->configured); 26034c06356bSdh } 26044c06356bSdh pmcs_unlock_phy(pptr); 26054c06356bSdh } 26064c06356bSdh #endif 26074c06356bSdh 26084c06356bSdh return; 26094c06356bSdh 26104c06356bSdh restart: 26114c06356bSdh /* Clean up and restart discovery */ 26124c06356bSdh pmcs_release_scratch(pwp); 26130b53804eSReed pmcs_flush_observations(pwp); 26144c06356bSdh mutex_enter(&pwp->config_lock); 26154c06356bSdh pwp->configuring = 0; 261632b54db7SJesse Butler cv_signal(&pwp->config_cv); 26174c06356bSdh RESTART_DISCOVERY_LOCKED(pwp); 26184c06356bSdh mutex_exit(&pwp->config_lock); 26194c06356bSdh } 26204c06356bSdh 26214c06356bSdh /* 26224c06356bSdh * Return any PHY that needs to have scheduled work done. The PHY is returned 26234c06356bSdh * locked. 26244c06356bSdh */ 26254c06356bSdh static pmcs_phy_t * 26264c06356bSdh pmcs_find_phy_needing_work(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 26274c06356bSdh { 26284c06356bSdh pmcs_phy_t *cphyp, *pnext; 26294c06356bSdh 26304c06356bSdh while (pptr) { 26314c06356bSdh pmcs_lock_phy(pptr); 26324c06356bSdh 26334c06356bSdh if (pptr->changed || (pptr->dead && pptr->valid_device_id)) { 26344c06356bSdh return (pptr); 26354c06356bSdh } 26364c06356bSdh 26374c06356bSdh pnext = pptr->sibling; 26384c06356bSdh 26394c06356bSdh if (pptr->children) { 26404c06356bSdh cphyp = pptr->children; 26414c06356bSdh pmcs_unlock_phy(pptr); 26424c06356bSdh cphyp = pmcs_find_phy_needing_work(pwp, cphyp); 26434c06356bSdh if (cphyp) { 26444c06356bSdh return (cphyp); 26454c06356bSdh } 26464c06356bSdh } else { 26474c06356bSdh pmcs_unlock_phy(pptr); 26484c06356bSdh } 26494c06356bSdh 26504c06356bSdh pptr = pnext; 26514c06356bSdh } 26524c06356bSdh 26534c06356bSdh return (NULL); 26544c06356bSdh } 26554c06356bSdh 26564c06356bSdh /* 2657601c90f1SSrikanth, Ramana * We may (or may not) report observations to SCSA. This is prefaced by 2658601c90f1SSrikanth, Ramana * issuing a set_begin for each iport target map. 26594c06356bSdh */ 2660601c90f1SSrikanth, Ramana static void 2661601c90f1SSrikanth, Ramana pmcs_begin_observations(pmcs_hw_t *pwp) 26624c06356bSdh { 26634c06356bSdh pmcs_iport_t *iport; 26644c06356bSdh scsi_hba_tgtmap_t *tgtmap; 26654c06356bSdh 26664c06356bSdh rw_enter(&pwp->iports_lock, RW_READER); 26674c06356bSdh for (iport = list_head(&pwp->iports); iport != NULL; 26684c06356bSdh iport = list_next(&pwp->iports, iport)) { 26694c06356bSdh /* 26704c06356bSdh * Unless we have at least one phy up, skip this iport. 26714c06356bSdh * Note we don't need to lock the iport for report_skip 26724c06356bSdh * since it is only used here. We are doing the skip so that 26734c06356bSdh * the phymap and iportmap stabilization times are honored - 26744c06356bSdh * giving us the ability to recover port operation within the 26754c06356bSdh * stabilization time without unconfiguring targets using the 26764c06356bSdh * port. 26774c06356bSdh */ 26784c06356bSdh if (!sas_phymap_uahasphys(pwp->hss_phymap, iport->ua)) { 26794c06356bSdh iport->report_skip = 1; 26804c06356bSdh continue; /* skip set_begin */ 26814c06356bSdh } 26824c06356bSdh iport->report_skip = 0; 26834c06356bSdh 26844c06356bSdh tgtmap = iport->iss_tgtmap; 26854c06356bSdh ASSERT(tgtmap); 26864c06356bSdh if (scsi_hba_tgtmap_set_begin(tgtmap) != DDI_SUCCESS) { 2687c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, 26884c06356bSdh "%s: cannot set_begin tgtmap ", __func__); 26894c06356bSdh rw_exit(&pwp->iports_lock); 2690601c90f1SSrikanth, Ramana return; 26914c06356bSdh } 2692c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, 2693c3bc407cSdh "%s: set begin on tgtmap [0x%p]", __func__, (void *)tgtmap); 26944c06356bSdh } 26954c06356bSdh rw_exit(&pwp->iports_lock); 2696601c90f1SSrikanth, Ramana } 2697601c90f1SSrikanth, Ramana 26980b53804eSReed /* 26990b53804eSReed * Tell SCSA to flush the observations we've already sent (if any), as they 27000b53804eSReed * are no longer valid. 27010b53804eSReed */ 27020b53804eSReed static void 27030b53804eSReed pmcs_flush_observations(pmcs_hw_t *pwp) 27040b53804eSReed { 27050b53804eSReed pmcs_iport_t *iport; 27060b53804eSReed scsi_hba_tgtmap_t *tgtmap; 27070b53804eSReed 27080b53804eSReed rw_enter(&pwp->iports_lock, RW_READER); 27090b53804eSReed for (iport = list_head(&pwp->iports); iport != NULL; 27100b53804eSReed iport = list_next(&pwp->iports, iport)) { 27110b53804eSReed /* 27120b53804eSReed * Skip this iport if it has no PHYs up. 27130b53804eSReed */ 27140b53804eSReed if (!sas_phymap_uahasphys(pwp->hss_phymap, iport->ua)) { 27150b53804eSReed continue; 27160b53804eSReed } 27170b53804eSReed 27180b53804eSReed tgtmap = iport->iss_tgtmap; 27190b53804eSReed ASSERT(tgtmap); 27200b53804eSReed if (scsi_hba_tgtmap_set_flush(tgtmap) != DDI_SUCCESS) { 27210b53804eSReed pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, 27220b53804eSReed "%s: Failed set_flush on tgtmap 0x%p", __func__, 27230b53804eSReed (void *)tgtmap); 27240b53804eSReed } else { 27250b53804eSReed pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, 27260b53804eSReed "%s: set flush on tgtmap 0x%p", __func__, 27270b53804eSReed (void *)tgtmap); 27280b53804eSReed } 27290b53804eSReed } 27300b53804eSReed rw_exit(&pwp->iports_lock); 27310b53804eSReed } 27320b53804eSReed 2733601c90f1SSrikanth, Ramana /* 2734601c90f1SSrikanth, Ramana * Report current observations to SCSA. 2735601c90f1SSrikanth, Ramana */ 2736601c90f1SSrikanth, Ramana static boolean_t 2737601c90f1SSrikanth, Ramana pmcs_report_observations(pmcs_hw_t *pwp) 2738601c90f1SSrikanth, Ramana { 2739601c90f1SSrikanth, Ramana pmcs_iport_t *iport; 2740601c90f1SSrikanth, Ramana scsi_hba_tgtmap_t *tgtmap; 2741601c90f1SSrikanth, Ramana char *ap; 2742601c90f1SSrikanth, Ramana pmcs_phy_t *pptr; 2743601c90f1SSrikanth, Ramana uint64_t wwn; 27444c06356bSdh 27454c06356bSdh /* 2746601c90f1SSrikanth, Ramana * Observation is stable, report what we currently see to the tgtmaps 2747601c90f1SSrikanth, Ramana * for delta processing. 27484c06356bSdh */ 27494c06356bSdh pptr = pwp->root_phys; 27504c06356bSdh 27514c06356bSdh while (pptr) { 27524c06356bSdh pmcs_lock_phy(pptr); 27534c06356bSdh 27544c06356bSdh /* 27554c06356bSdh * Skip PHYs that have nothing attached or are dead. 27564c06356bSdh */ 27574c06356bSdh if ((pptr->dtype == NOTHING) || pptr->dead) { 27584c06356bSdh pmcs_unlock_phy(pptr); 27594c06356bSdh pptr = pptr->sibling; 27604c06356bSdh continue; 27614c06356bSdh } 27624c06356bSdh 27634c06356bSdh if (pptr->changed) { 2764c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 27654c06356bSdh "%s: oops, PHY %s changed; restart discovery", 27664c06356bSdh __func__, pptr->path); 27674c06356bSdh pmcs_unlock_phy(pptr); 27684c06356bSdh return (B_FALSE); 27694c06356bSdh } 27704c06356bSdh 27714c06356bSdh /* 27724c06356bSdh * Get the iport for this root PHY, then call the helper 27734c06356bSdh * to report observations for this iport's targets 27744c06356bSdh */ 27759aed1621SDavid Hollister wwn = pmcs_barray2wwn(pptr->sas_address); 27769aed1621SDavid Hollister pmcs_unlock_phy(pptr); 27779aed1621SDavid Hollister iport = pmcs_get_iport_by_wwn(pwp, wwn); 27784c06356bSdh if (iport == NULL) { 27794c06356bSdh /* No iport for this tgt */ 2780c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 2781c3bc407cSdh "%s: no iport for this target", __func__); 27824c06356bSdh pptr = pptr->sibling; 27834c06356bSdh continue; 27844c06356bSdh } 27854c06356bSdh 27869aed1621SDavid Hollister pmcs_lock_phy(pptr); 27874c06356bSdh if (!iport->report_skip) { 27884c06356bSdh if (pmcs_report_iport_observations( 27894c06356bSdh pwp, iport, pptr) == B_FALSE) { 27904c06356bSdh pmcs_rele_iport(iport); 27914c06356bSdh pmcs_unlock_phy(pptr); 27924c06356bSdh return (B_FALSE); 27934c06356bSdh } 27944c06356bSdh } 27954c06356bSdh pmcs_rele_iport(iport); 27964c06356bSdh pmcs_unlock_phy(pptr); 27974c06356bSdh pptr = pptr->sibling; 27984c06356bSdh } 27994c06356bSdh 28004c06356bSdh /* 28014c06356bSdh * The observation is complete, end sets. Note we will skip any 28024c06356bSdh * iports that are active, but have no PHYs in them (i.e. awaiting 28034c06356bSdh * unconfigure). Set to restart discovery if we find this. 28044c06356bSdh */ 28054c06356bSdh rw_enter(&pwp->iports_lock, RW_READER); 28064c06356bSdh for (iport = list_head(&pwp->iports); 28074c06356bSdh iport != NULL; 28084c06356bSdh iport = list_next(&pwp->iports, iport)) { 28094c06356bSdh 28104c06356bSdh if (iport->report_skip) 28114c06356bSdh continue; /* skip set_end */ 28124c06356bSdh 28134c06356bSdh tgtmap = iport->iss_tgtmap; 28144c06356bSdh ASSERT(tgtmap); 28154c06356bSdh if (scsi_hba_tgtmap_set_end(tgtmap, 0) != DDI_SUCCESS) { 2816c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, 28174c06356bSdh "%s: cannot set_end tgtmap ", __func__); 28184c06356bSdh rw_exit(&pwp->iports_lock); 28194c06356bSdh return (B_FALSE); 28204c06356bSdh } 2821c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, 2822c3bc407cSdh "%s: set end on tgtmap [0x%p]", __func__, (void *)tgtmap); 28234c06356bSdh } 28244c06356bSdh 28254c06356bSdh /* 28264c06356bSdh * Now that discovery is complete, set up the necessary 28274c06356bSdh * DDI properties on each iport node. 28284c06356bSdh */ 28294c06356bSdh for (iport = list_head(&pwp->iports); iport != NULL; 28304c06356bSdh iport = list_next(&pwp->iports, iport)) { 28314c06356bSdh /* Set up the 'attached-port' property on the iport */ 28324c06356bSdh ap = kmem_zalloc(PMCS_MAX_UA_SIZE, KM_SLEEP); 28334c06356bSdh mutex_enter(&iport->lock); 28344c06356bSdh pptr = iport->pptr; 28354c06356bSdh mutex_exit(&iport->lock); 28364c06356bSdh if (pptr == NULL) { 28374c06356bSdh /* 28384c06356bSdh * This iport is down, but has not been 28394c06356bSdh * removed from our list (unconfigured). 28404c06356bSdh * Set our value to '0'. 28414c06356bSdh */ 28424c06356bSdh (void) snprintf(ap, 1, "%s", "0"); 28434c06356bSdh } else { 28444c06356bSdh /* Otherwise, set it to remote phy's wwn */ 28454c06356bSdh pmcs_lock_phy(pptr); 28464c06356bSdh wwn = pmcs_barray2wwn(pptr->sas_address); 28474c06356bSdh (void) scsi_wwn_to_wwnstr(wwn, 1, ap); 28484c06356bSdh pmcs_unlock_phy(pptr); 28494c06356bSdh } 28504c06356bSdh if (ndi_prop_update_string(DDI_DEV_T_NONE, iport->dip, 2851499cfd15SDavid Hollister SCSI_ADDR_PROP_ATTACHED_PORT, ap) != DDI_SUCCESS) { 2852c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed " 2853c3bc407cSdh "to set prop ("SCSI_ADDR_PROP_ATTACHED_PORT")", 28544c06356bSdh __func__); 28554c06356bSdh } 28564c06356bSdh kmem_free(ap, PMCS_MAX_UA_SIZE); 28574c06356bSdh } 28584c06356bSdh rw_exit(&pwp->iports_lock); 28594c06356bSdh 28604c06356bSdh return (B_TRUE); 28614c06356bSdh } 28624c06356bSdh 28634c06356bSdh /* 28644c06356bSdh * Report observations into a particular iport's target map 28654c06356bSdh * 28664c06356bSdh * Called with phyp (and all descendents) locked 28674c06356bSdh */ 28684c06356bSdh static boolean_t 28694c06356bSdh pmcs_report_iport_observations(pmcs_hw_t *pwp, pmcs_iport_t *iport, 28704c06356bSdh pmcs_phy_t *phyp) 28714c06356bSdh { 28724c06356bSdh pmcs_phy_t *lphyp; 28734c06356bSdh scsi_hba_tgtmap_t *tgtmap; 28744c06356bSdh scsi_tgtmap_tgt_type_t tgt_type; 28754c06356bSdh char *ua; 28764c06356bSdh uint64_t wwn; 28774c06356bSdh 28784c06356bSdh tgtmap = iport->iss_tgtmap; 28794c06356bSdh ASSERT(tgtmap); 28804c06356bSdh 28814c06356bSdh lphyp = phyp; 28824c06356bSdh while (lphyp) { 28834c06356bSdh switch (lphyp->dtype) { 28844c06356bSdh default: /* Skip unknown PHYs. */ 28854c06356bSdh /* for non-root phys, skip to sibling */ 28864c06356bSdh goto next_phy; 28874c06356bSdh 28884c06356bSdh case SATA: 28894c06356bSdh case SAS: 28904c06356bSdh tgt_type = SCSI_TGT_SCSI_DEVICE; 28914c06356bSdh break; 28924c06356bSdh 28934c06356bSdh case EXPANDER: 28944c06356bSdh tgt_type = SCSI_TGT_SMP_DEVICE; 28954c06356bSdh break; 28964c06356bSdh } 28974c06356bSdh 28989aed1621SDavid Hollister if (lphyp->dead || !lphyp->configured) { 28994c06356bSdh goto next_phy; 29004c06356bSdh } 29014c06356bSdh 2902a25672a1SDavid Hollister /* 2903a25672a1SDavid Hollister * Validate the PHY's SAS address 2904a25672a1SDavid Hollister */ 2905a25672a1SDavid Hollister if (((lphyp->sas_address[0] & 0xf0) >> 4) != NAA_IEEE_REG) { 2906a25672a1SDavid Hollister pmcs_prt(pwp, PMCS_PRT_ERR, lphyp, NULL, 2907a25672a1SDavid Hollister "PHY 0x%p (%s) has invalid SAS address; " 2908a25672a1SDavid Hollister "will not enumerate", (void *)lphyp, lphyp->path); 2909a25672a1SDavid Hollister goto next_phy; 2910a25672a1SDavid Hollister } 2911a25672a1SDavid Hollister 29124c06356bSdh wwn = pmcs_barray2wwn(lphyp->sas_address); 29134c06356bSdh ua = scsi_wwn_to_wwnstr(wwn, 1, NULL); 29144c06356bSdh 2915c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, lphyp, NULL, 29164c06356bSdh "iport_observation: adding %s on tgtmap [0x%p] phy [0x%p]", 29174c06356bSdh ua, (void *)tgtmap, (void*)lphyp); 29184c06356bSdh 29194c06356bSdh if (scsi_hba_tgtmap_set_add(tgtmap, tgt_type, ua, NULL) != 29204c06356bSdh DDI_SUCCESS) { 2921c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, 29224c06356bSdh "%s: failed to add address %s", __func__, ua); 29234c06356bSdh scsi_free_wwnstr(ua); 29244c06356bSdh return (B_FALSE); 29254c06356bSdh } 29264c06356bSdh scsi_free_wwnstr(ua); 29274c06356bSdh 29284c06356bSdh if (lphyp->children) { 29294c06356bSdh if (pmcs_report_iport_observations(pwp, iport, 29304c06356bSdh lphyp->children) == B_FALSE) { 29314c06356bSdh return (B_FALSE); 29324c06356bSdh } 29334c06356bSdh } 29344c06356bSdh 29354c06356bSdh /* for non-root phys, report siblings too */ 29364c06356bSdh next_phy: 29374c06356bSdh if (IS_ROOT_PHY(lphyp)) { 29384c06356bSdh lphyp = NULL; 29394c06356bSdh } else { 29404c06356bSdh lphyp = lphyp->sibling; 29414c06356bSdh } 29424c06356bSdh } 29434c06356bSdh 29444c06356bSdh return (B_TRUE); 29454c06356bSdh } 29464c06356bSdh 29474c06356bSdh /* 29484c06356bSdh * Check for and configure new devices. 29494c06356bSdh * 29504c06356bSdh * If the changed device is a SATA device, add a SATA device. 29514c06356bSdh * 29524c06356bSdh * If the changed device is a SAS device, add a SAS device. 29534c06356bSdh * 29544c06356bSdh * If the changed device is an EXPANDER device, do a REPORT 29554c06356bSdh * GENERAL SMP command to find out the number of contained phys. 29564c06356bSdh * 29574c06356bSdh * For each number of contained phys, allocate a phy, do a 29584c06356bSdh * DISCOVERY SMP command to find out what kind of device it 29594c06356bSdh * is and add it to the linked list of phys on the *next* level. 29604c06356bSdh * 29614c06356bSdh * NOTE: pptr passed in by the caller will be a root PHY 29624c06356bSdh */ 29634c06356bSdh static int 29644c06356bSdh pmcs_configure_new_devices(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 29654c06356bSdh { 29664c06356bSdh int rval = 0; 29674c06356bSdh pmcs_iport_t *iport; 29684c06356bSdh pmcs_phy_t *pnext, *orig_pptr = pptr, *root_phy, *pchild; 29699aed1621SDavid Hollister uint64_t wwn; 29704c06356bSdh 29714c06356bSdh /* 29724c06356bSdh * First, walk through each PHY at this level 29734c06356bSdh */ 29744c06356bSdh while (pptr) { 29754c06356bSdh pmcs_lock_phy(pptr); 29764c06356bSdh pnext = pptr->sibling; 29774c06356bSdh 29784c06356bSdh /* 29794c06356bSdh * Set the new dtype if it has changed 29804c06356bSdh */ 29814c06356bSdh if ((pptr->pend_dtype != NEW) && 29824c06356bSdh (pptr->pend_dtype != pptr->dtype)) { 29834c06356bSdh pptr->dtype = pptr->pend_dtype; 29844c06356bSdh } 29854c06356bSdh 29864c06356bSdh if (pptr->changed == 0 || pptr->dead || pptr->configured) { 29874c06356bSdh goto next_phy; 29884c06356bSdh } 29894c06356bSdh 29904c06356bSdh /* 29914c06356bSdh * Confirm that this target's iport is configured 29924c06356bSdh */ 29934c06356bSdh root_phy = pmcs_get_root_phy(pptr); 29949aed1621SDavid Hollister wwn = pmcs_barray2wwn(root_phy->sas_address); 29959aed1621SDavid Hollister pmcs_unlock_phy(pptr); 29969aed1621SDavid Hollister iport = pmcs_get_iport_by_wwn(pwp, wwn); 29974c06356bSdh if (iport == NULL) { 29984c06356bSdh /* No iport for this tgt, restart */ 2999c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 30004c06356bSdh "%s: iport not yet configured, " 30014c06356bSdh "retry discovery", __func__); 30024c06356bSdh pnext = NULL; 30034c06356bSdh rval = -1; 30049aed1621SDavid Hollister pmcs_lock_phy(pptr); 30054c06356bSdh goto next_phy; 30064c06356bSdh } 30074c06356bSdh 30089aed1621SDavid Hollister pmcs_lock_phy(pptr); 30094c06356bSdh switch (pptr->dtype) { 30104c06356bSdh case NOTHING: 30114c06356bSdh pptr->changed = 0; 30124c06356bSdh break; 30134c06356bSdh case SATA: 30144c06356bSdh case SAS: 30154c06356bSdh pptr->iport = iport; 30164c06356bSdh pmcs_new_tport(pwp, pptr); 30174c06356bSdh break; 30184c06356bSdh case EXPANDER: 30194c06356bSdh pmcs_configure_expander(pwp, pptr, iport); 30204c06356bSdh break; 30214c06356bSdh } 30224c06356bSdh pmcs_rele_iport(iport); 30234c06356bSdh 30244c06356bSdh mutex_enter(&pwp->config_lock); 30254c06356bSdh if (pwp->config_changed) { 30264c06356bSdh mutex_exit(&pwp->config_lock); 30274c06356bSdh pnext = NULL; 30284c06356bSdh goto next_phy; 30294c06356bSdh } 30304c06356bSdh mutex_exit(&pwp->config_lock); 30314c06356bSdh 30324c06356bSdh next_phy: 30334c06356bSdh pmcs_unlock_phy(pptr); 30344c06356bSdh pptr = pnext; 30354c06356bSdh } 30364c06356bSdh 30374c06356bSdh if (rval != 0) { 30384c06356bSdh return (rval); 30394c06356bSdh } 30404c06356bSdh 30414c06356bSdh /* 30424c06356bSdh * Now walk through each PHY again, recalling ourselves if they 30434c06356bSdh * have children 30444c06356bSdh */ 30454c06356bSdh pptr = orig_pptr; 30464c06356bSdh while (pptr) { 30474c06356bSdh pmcs_lock_phy(pptr); 30484c06356bSdh pnext = pptr->sibling; 30494c06356bSdh pchild = pptr->children; 30504c06356bSdh pmcs_unlock_phy(pptr); 30514c06356bSdh 30524c06356bSdh if (pchild) { 30534c06356bSdh rval = pmcs_configure_new_devices(pwp, pchild); 30544c06356bSdh if (rval != 0) { 30554c06356bSdh break; 30564c06356bSdh } 30574c06356bSdh } 30584c06356bSdh 30594c06356bSdh pptr = pnext; 30604c06356bSdh } 30614c06356bSdh 30624c06356bSdh return (rval); 30634c06356bSdh } 30644c06356bSdh 30654c06356bSdh /* 30664c06356bSdh * Set all phys and descendent phys as changed if changed == B_TRUE, otherwise 30674c06356bSdh * mark them all as not changed. 30684c06356bSdh * 30694c06356bSdh * Called with parent PHY locked. 30704c06356bSdh */ 30714c06356bSdh void 30724c06356bSdh pmcs_set_changed(pmcs_hw_t *pwp, pmcs_phy_t *parent, boolean_t changed, 30734c06356bSdh int level) 30744c06356bSdh { 30754c06356bSdh pmcs_phy_t *pptr; 30764c06356bSdh 30774c06356bSdh if (level == 0) { 30784c06356bSdh if (changed) { 30794c06356bSdh PHY_CHANGED(pwp, parent); 30804c06356bSdh } else { 30814c06356bSdh parent->changed = 0; 30824c06356bSdh } 30834c06356bSdh if (parent->dtype == EXPANDER && parent->level) { 30844c06356bSdh parent->width = 1; 30854c06356bSdh } 30864c06356bSdh if (parent->children) { 30874c06356bSdh pmcs_set_changed(pwp, parent->children, changed, 30884c06356bSdh level + 1); 30894c06356bSdh } 30904c06356bSdh } else { 30914c06356bSdh pptr = parent; 30924c06356bSdh while (pptr) { 30934c06356bSdh if (changed) { 30944c06356bSdh PHY_CHANGED(pwp, pptr); 30954c06356bSdh } else { 30964c06356bSdh pptr->changed = 0; 30974c06356bSdh } 30984c06356bSdh if (pptr->dtype == EXPANDER && pptr->level) { 30994c06356bSdh pptr->width = 1; 31004c06356bSdh } 31014c06356bSdh if (pptr->children) { 31024c06356bSdh pmcs_set_changed(pwp, pptr->children, changed, 31034c06356bSdh level + 1); 31044c06356bSdh } 31054c06356bSdh pptr = pptr->sibling; 31064c06356bSdh } 31074c06356bSdh } 31084c06356bSdh } 31094c06356bSdh 31104c06356bSdh /* 31114c06356bSdh * Take the passed phy mark it and its descendants as dead. 31124c06356bSdh * Fire up reconfiguration to abort commands and bury it. 31134c06356bSdh * 31144c06356bSdh * Called with the parent PHY locked. 31154c06356bSdh */ 31164c06356bSdh void 31174c06356bSdh pmcs_kill_changed(pmcs_hw_t *pwp, pmcs_phy_t *parent, int level) 31184c06356bSdh { 31194c06356bSdh pmcs_phy_t *pptr = parent; 31204c06356bSdh 31214c06356bSdh while (pptr) { 31224c06356bSdh pptr->link_rate = 0; 31234c06356bSdh pptr->abort_sent = 0; 31244c06356bSdh pptr->abort_pending = 1; 31254c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE); 31264c06356bSdh pptr->need_rl_ext = 0; 31274c06356bSdh 31284c06356bSdh if (pptr->dead == 0) { 31294c06356bSdh PHY_CHANGED(pwp, pptr); 31304c06356bSdh RESTART_DISCOVERY(pwp); 31314c06356bSdh } 31324c06356bSdh 31334c06356bSdh pptr->dead = 1; 31344c06356bSdh 31354c06356bSdh if (pptr->children) { 31364c06356bSdh pmcs_kill_changed(pwp, pptr->children, level + 1); 31374c06356bSdh } 31384c06356bSdh 31394c06356bSdh /* 31404c06356bSdh * Only kill siblings at level > 0 31414c06356bSdh */ 31424c06356bSdh if (level == 0) { 31434c06356bSdh return; 31444c06356bSdh } 31454c06356bSdh 31464c06356bSdh pptr = pptr->sibling; 31474c06356bSdh } 31484c06356bSdh } 31494c06356bSdh 31504c06356bSdh /* 31514c06356bSdh * Go through every PHY and clear any that are dead (unless they're expanders) 31524c06356bSdh */ 31534c06356bSdh static void 31544c06356bSdh pmcs_clear_phys(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 31554c06356bSdh { 31564c06356bSdh pmcs_phy_t *pnext, *phyp; 31574c06356bSdh 31584c06356bSdh phyp = pptr; 31594c06356bSdh while (phyp) { 31604c06356bSdh if (IS_ROOT_PHY(phyp)) { 31614c06356bSdh pmcs_lock_phy(phyp); 31624c06356bSdh } 31634c06356bSdh 31644c06356bSdh if ((phyp->dtype != EXPANDER) && phyp->dead) { 31654c06356bSdh pmcs_clear_phy(pwp, phyp); 31664c06356bSdh } 31674c06356bSdh 31684c06356bSdh if (phyp->children) { 31694c06356bSdh pmcs_clear_phys(pwp, phyp->children); 31704c06356bSdh } 31714c06356bSdh 31724c06356bSdh pnext = phyp->sibling; 31734c06356bSdh 31744c06356bSdh if (IS_ROOT_PHY(phyp)) { 31754c06356bSdh pmcs_unlock_phy(phyp); 31764c06356bSdh } 31774c06356bSdh 31784c06356bSdh phyp = pnext; 31794c06356bSdh } 31804c06356bSdh } 31814c06356bSdh 31824c06356bSdh /* 31834c06356bSdh * Clear volatile parts of a phy. Called with PHY locked. 31844c06356bSdh */ 31854c06356bSdh void 31864c06356bSdh pmcs_clear_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 31874c06356bSdh { 3188c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "%s: %s", 3189c3bc407cSdh __func__, pptr->path); 31904c06356bSdh ASSERT(mutex_owned(&pptr->phy_lock)); 31914c06356bSdh /* keep sibling */ 31924c06356bSdh /* keep children */ 31934c06356bSdh /* keep parent */ 31944c06356bSdh pptr->device_id = PMCS_INVALID_DEVICE_ID; 31954c06356bSdh /* keep hw_event_ack */ 31964c06356bSdh pptr->ncphy = 0; 31974c06356bSdh /* keep phynum */ 31984c06356bSdh pptr->width = 0; 31994c06356bSdh pptr->ds_recovery_retries = 0; 3200af685682SSrikanth, Ramana pptr->ds_prev_good_recoveries = 0; 3201af685682SSrikanth, Ramana pptr->last_good_recovery = 0; 3202af685682SSrikanth, Ramana pptr->prev_recovery = 0; 3203145e0143Sdh 32044c06356bSdh /* keep dtype */ 32054c06356bSdh pptr->config_stop = 0; 32064c06356bSdh pptr->spinup_hold = 0; 32074c06356bSdh pptr->atdt = 0; 32084c06356bSdh /* keep portid */ 32094c06356bSdh pptr->link_rate = 0; 32104c06356bSdh pptr->valid_device_id = 0; 32114c06356bSdh pptr->abort_sent = 0; 32124c06356bSdh pptr->abort_pending = 0; 32134c06356bSdh pptr->need_rl_ext = 0; 32144c06356bSdh pptr->subsidiary = 0; 32154c06356bSdh pptr->configured = 0; 3216601c90f1SSrikanth, Ramana pptr->deregister_wait = 0; 32179aed1621SDavid Hollister pptr->reenumerate = 0; 32184c06356bSdh /* Only mark dead if it's not a root PHY and its dtype isn't NOTHING */ 32194c06356bSdh /* XXX: What about directly attached disks? */ 32204c06356bSdh if (!IS_ROOT_PHY(pptr) && (pptr->dtype != NOTHING)) 32214c06356bSdh pptr->dead = 1; 32224c06356bSdh pptr->changed = 0; 32234c06356bSdh /* keep SAS address */ 32244c06356bSdh /* keep path */ 32254c06356bSdh /* keep ref_count */ 32264c06356bSdh /* Don't clear iport on root PHYs - they are handled in pmcs_intr.c */ 32274c06356bSdh if (!IS_ROOT_PHY(pptr)) { 322873a3eccdSDavid Hollister pptr->last_iport = pptr->iport; 32294c06356bSdh pptr->iport = NULL; 32304c06356bSdh } 3231b18a19c2SJesse Butler /* keep target */ 32324c06356bSdh } 32334c06356bSdh 32344c06356bSdh /* 32354c06356bSdh * Allocate softstate for this target if there isn't already one. If there 32364c06356bSdh * is, just redo our internal configuration. If it is actually "new", we'll 32374c06356bSdh * soon get a tran_tgt_init for it. 32384c06356bSdh * 32394c06356bSdh * Called with PHY locked. 32404c06356bSdh */ 32414c06356bSdh static void 32424c06356bSdh pmcs_new_tport(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 32434c06356bSdh { 3244c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "%s: phy 0x%p @ %s", 3245c3bc407cSdh __func__, (void *)pptr, pptr->path); 32464c06356bSdh 32474c06356bSdh if (pmcs_configure_phy(pwp, pptr) == B_FALSE) { 32484c06356bSdh /* 32494c06356bSdh * If the config failed, mark the PHY as changed. 32504c06356bSdh */ 32514c06356bSdh PHY_CHANGED(pwp, pptr); 3252c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 32534c06356bSdh "%s: pmcs_configure_phy failed for phy 0x%p", __func__, 32544c06356bSdh (void *)pptr); 32554c06356bSdh return; 32564c06356bSdh } 32574c06356bSdh 32584c06356bSdh /* Mark PHY as no longer changed */ 32594c06356bSdh pptr->changed = 0; 32604c06356bSdh 32614c06356bSdh /* 326273a3eccdSDavid Hollister * If the PHY has no target pointer: 326373a3eccdSDavid Hollister * 326473a3eccdSDavid Hollister * If it's a root PHY, see if another PHY in the iport holds the 326573a3eccdSDavid Hollister * target pointer (primary PHY changed). If so, move it over. 326673a3eccdSDavid Hollister * 326773a3eccdSDavid Hollister * If it's not a root PHY, see if there's a PHY on the dead_phys 326873a3eccdSDavid Hollister * list that matches. 32694c06356bSdh */ 32704c06356bSdh if (pptr->target == NULL) { 327173a3eccdSDavid Hollister if (IS_ROOT_PHY(pptr)) { 327273a3eccdSDavid Hollister pmcs_phy_t *rphy = pwp->root_phys; 327373a3eccdSDavid Hollister 327473a3eccdSDavid Hollister while (rphy) { 327573a3eccdSDavid Hollister if (rphy == pptr) { 327673a3eccdSDavid Hollister rphy = rphy->sibling; 327773a3eccdSDavid Hollister continue; 327873a3eccdSDavid Hollister } 327973a3eccdSDavid Hollister 328073a3eccdSDavid Hollister mutex_enter(&rphy->phy_lock); 328173a3eccdSDavid Hollister if ((rphy->iport == pptr->iport) && 328273a3eccdSDavid Hollister (rphy->target != NULL)) { 328373a3eccdSDavid Hollister mutex_enter(&rphy->target->statlock); 328473a3eccdSDavid Hollister pptr->target = rphy->target; 328573a3eccdSDavid Hollister rphy->target = NULL; 328673a3eccdSDavid Hollister pptr->target->phy = pptr; 328773a3eccdSDavid Hollister /* The target is now on pptr */ 328873a3eccdSDavid Hollister mutex_exit(&pptr->target->statlock); 328973a3eccdSDavid Hollister mutex_exit(&rphy->phy_lock); 329073a3eccdSDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 329173a3eccdSDavid Hollister pptr, pptr->target, 329273a3eccdSDavid Hollister "%s: Moved target from %s to %s", 329373a3eccdSDavid Hollister __func__, rphy->path, pptr->path); 329473a3eccdSDavid Hollister break; 329573a3eccdSDavid Hollister } 329673a3eccdSDavid Hollister mutex_exit(&rphy->phy_lock); 329773a3eccdSDavid Hollister 329873a3eccdSDavid Hollister rphy = rphy->sibling; 329973a3eccdSDavid Hollister } 330073a3eccdSDavid Hollister } else { 330173a3eccdSDavid Hollister pmcs_reap_dead_phy(pptr); 330273a3eccdSDavid Hollister } 33034c06356bSdh } 33044c06356bSdh 33054c06356bSdh /* 33064c06356bSdh * Only assign the device if there is a target for this PHY with a 33074c06356bSdh * matching SAS address. If an iport is disconnected from one piece 33084c06356bSdh * of storage and connected to another within the iport stabilization 33094c06356bSdh * time, we can get the PHY/target mismatch situation. 33104c06356bSdh * 33114c06356bSdh * Otherwise, it'll get done in tran_tgt_init. 33124c06356bSdh */ 33134c06356bSdh if (pptr->target) { 33144c06356bSdh mutex_enter(&pptr->target->statlock); 33154c06356bSdh if (pmcs_phy_target_match(pptr) == B_FALSE) { 33164c06356bSdh mutex_exit(&pptr->target->statlock); 33174c06356bSdh if (!IS_ROOT_PHY(pptr)) { 33184c06356bSdh pmcs_dec_phy_ref_count(pptr); 33194c06356bSdh } 3320c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 33214c06356bSdh "%s: Not assigning existing tgt %p for PHY %p " 33224c06356bSdh "(WWN mismatch)", __func__, (void *)pptr->target, 33234c06356bSdh (void *)pptr); 33244c06356bSdh pptr->target = NULL; 33254c06356bSdh return; 33264c06356bSdh } 33274c06356bSdh 33284c06356bSdh if (!pmcs_assign_device(pwp, pptr->target)) { 3329c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target, 33304c06356bSdh "%s: pmcs_assign_device failed for target 0x%p", 33314c06356bSdh __func__, (void *)pptr->target); 33324c06356bSdh } 33334c06356bSdh mutex_exit(&pptr->target->statlock); 33344c06356bSdh } 33354c06356bSdh } 33364c06356bSdh 33374c06356bSdh /* 33384c06356bSdh * Called with PHY lock held. 33394c06356bSdh */ 33404c06356bSdh static boolean_t 33414c06356bSdh pmcs_configure_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 33424c06356bSdh { 33434c06356bSdh char *dtype; 33444c06356bSdh 33454c06356bSdh ASSERT(mutex_owned(&pptr->phy_lock)); 33464c06356bSdh 33474c06356bSdh /* 33484c06356bSdh * Mark this device as no longer changed. 33494c06356bSdh */ 33504c06356bSdh pptr->changed = 0; 33514c06356bSdh 33524c06356bSdh /* 33534c06356bSdh * If we don't have a device handle, get one. 33544c06356bSdh */ 33554c06356bSdh if (pmcs_get_device_handle(pwp, pptr)) { 33564c06356bSdh return (B_FALSE); 33574c06356bSdh } 33584c06356bSdh 33594c06356bSdh pptr->configured = 1; 33604c06356bSdh 33614c06356bSdh switch (pptr->dtype) { 33624c06356bSdh case SAS: 33634c06356bSdh dtype = "SAS"; 33644c06356bSdh break; 33654c06356bSdh case SATA: 33664c06356bSdh dtype = "SATA"; 33674c06356bSdh break; 33684c06356bSdh case EXPANDER: 33694c06356bSdh dtype = "SMP"; 33704c06356bSdh break; 33714c06356bSdh default: 33724c06356bSdh dtype = "???"; 33734c06356bSdh } 33744c06356bSdh 3375c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "config_dev: %s " 3376c3bc407cSdh "dev %s " SAS_ADDR_FMT " dev id 0x%x lr 0x%x", dtype, pptr->path, 33774c06356bSdh SAS_ADDR_PRT(pptr->sas_address), pptr->device_id, pptr->link_rate); 33784c06356bSdh 33794c06356bSdh return (B_TRUE); 33804c06356bSdh } 33814c06356bSdh 33824c06356bSdh /* 33834c06356bSdh * Called with PHY locked 33844c06356bSdh */ 33854c06356bSdh static void 33864c06356bSdh pmcs_configure_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr, pmcs_iport_t *iport) 33874c06356bSdh { 33884c06356bSdh pmcs_phy_t *ctmp, *clist = NULL, *cnext; 33894c06356bSdh int result, i, nphy = 0; 33904c06356bSdh boolean_t root_phy = B_FALSE; 33914c06356bSdh 33924c06356bSdh ASSERT(iport); 33934c06356bSdh 33944c06356bSdh /* 33954c06356bSdh * Step 1- clear our "changed" bit. If we need to retry/restart due 33964c06356bSdh * to resource shortages, we'll set it again. While we're doing 33974c06356bSdh * configuration, other events may set it again as well. If the PHY 33984c06356bSdh * is a root PHY and is currently marked as having changed, reset the 33994c06356bSdh * config_stop timer as well. 34004c06356bSdh */ 34014c06356bSdh if (IS_ROOT_PHY(pptr) && pptr->changed) { 34024c06356bSdh pptr->config_stop = ddi_get_lbolt() + 34034c06356bSdh drv_usectohz(PMCS_MAX_CONFIG_TIME); 34044c06356bSdh } 34054c06356bSdh pptr->changed = 0; 34064c06356bSdh 34074c06356bSdh /* 34084c06356bSdh * Step 2- make sure we don't overflow 34094c06356bSdh */ 34104c06356bSdh if (pptr->level == PMCS_MAX_XPND-1) { 3411c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_WARN, pptr, NULL, 34124c06356bSdh "%s: SAS expansion tree too deep", __func__); 34134c06356bSdh return; 34144c06356bSdh } 34154c06356bSdh 34164c06356bSdh /* 34174c06356bSdh * Step 3- Check if this expander is part of a wide phy that has 34184c06356bSdh * already been configured. 34194c06356bSdh * 34204c06356bSdh * This is known by checking this level for another EXPANDER device 34214c06356bSdh * with the same SAS address and isn't already marked as a subsidiary 34224c06356bSdh * phy and a parent whose SAS address is the same as our SAS address 34234c06356bSdh * (if there are parents). 34244c06356bSdh */ 34254c06356bSdh if (!IS_ROOT_PHY(pptr)) { 34264c06356bSdh /* 34274c06356bSdh * No need to lock the parent here because we're in discovery 34284c06356bSdh * and the only time a PHY's children pointer can change is 34294c06356bSdh * in discovery; either in pmcs_clear_expander (which has 34304c06356bSdh * already been called) or here, down below. Plus, trying to 34314c06356bSdh * grab the parent's lock here can cause deadlock. 34324c06356bSdh */ 34334c06356bSdh ctmp = pptr->parent->children; 34344c06356bSdh } else { 34354c06356bSdh ctmp = pwp->root_phys; 34364c06356bSdh root_phy = B_TRUE; 34374c06356bSdh } 34384c06356bSdh 34394c06356bSdh while (ctmp) { 34404c06356bSdh /* 34414c06356bSdh * If we've checked all PHYs up to pptr, we stop. Otherwise, 34424c06356bSdh * we'll be checking for a primary PHY with a higher PHY 34434c06356bSdh * number than pptr, which will never happen. The primary 34444c06356bSdh * PHY on non-root expanders will ALWAYS be the lowest 34454c06356bSdh * numbered PHY. 34464c06356bSdh */ 34474c06356bSdh if (ctmp == pptr) { 34484c06356bSdh break; 34494c06356bSdh } 34504c06356bSdh 34514c06356bSdh /* 34524c06356bSdh * If pptr and ctmp are root PHYs, just grab the mutex on 34534c06356bSdh * ctmp. No need to lock the entire tree. If they are not 34544c06356bSdh * root PHYs, there is no need to lock since a non-root PHY's 34554c06356bSdh * SAS address and other characteristics can only change in 34564c06356bSdh * discovery anyway. 34574c06356bSdh */ 34584c06356bSdh if (root_phy) { 34594c06356bSdh mutex_enter(&ctmp->phy_lock); 34604c06356bSdh } 34614c06356bSdh 34624c06356bSdh if (ctmp->dtype == EXPANDER && ctmp->width && 34634c06356bSdh memcmp(ctmp->sas_address, pptr->sas_address, 8) == 0) { 34644c06356bSdh int widephy = 0; 34654c06356bSdh /* 34664c06356bSdh * If these phys are not root PHYs, compare their SAS 34674c06356bSdh * addresses too. 34684c06356bSdh */ 34694c06356bSdh if (!root_phy) { 34704c06356bSdh if (memcmp(ctmp->parent->sas_address, 34714c06356bSdh pptr->parent->sas_address, 8) == 0) { 34724c06356bSdh widephy = 1; 34734c06356bSdh } 34744c06356bSdh } else { 34754c06356bSdh widephy = 1; 34764c06356bSdh } 34774c06356bSdh if (widephy) { 34784c06356bSdh ctmp->width++; 34794c06356bSdh pptr->subsidiary = 1; 3480499cfd15SDavid Hollister 3481499cfd15SDavid Hollister /* 3482499cfd15SDavid Hollister * Update the primary PHY's attached-port-pm 3483499cfd15SDavid Hollister * and target-port-pm information with the info 3484499cfd15SDavid Hollister * from this subsidiary 3485499cfd15SDavid Hollister */ 3486499cfd15SDavid Hollister pmcs_update_phy_pm_props(ctmp, 3487499cfd15SDavid Hollister pptr->att_port_pm_tmp, 3488499cfd15SDavid Hollister pptr->tgt_port_pm_tmp, B_TRUE); 3489499cfd15SDavid Hollister 3490c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 3491c3bc407cSdh "%s: PHY %s part of wide PHY %s " 3492c3bc407cSdh "(now %d wide)", __func__, pptr->path, 3493c3bc407cSdh ctmp->path, ctmp->width); 34944c06356bSdh if (root_phy) { 34954c06356bSdh mutex_exit(&ctmp->phy_lock); 34964c06356bSdh } 34974c06356bSdh return; 34984c06356bSdh } 34994c06356bSdh } 35004c06356bSdh 35014c06356bSdh cnext = ctmp->sibling; 35024c06356bSdh if (root_phy) { 35034c06356bSdh mutex_exit(&ctmp->phy_lock); 35044c06356bSdh } 35054c06356bSdh ctmp = cnext; 35064c06356bSdh } 35074c06356bSdh 35084c06356bSdh /* 35094c06356bSdh * Step 4- If we don't have a device handle, get one. Since this 35104c06356bSdh * is the primary PHY, make sure subsidiary is cleared. 35114c06356bSdh */ 35124c06356bSdh pptr->subsidiary = 0; 35136745c559SJesse Butler pptr->iport = iport; 35144c06356bSdh if (pmcs_get_device_handle(pwp, pptr)) { 35154c06356bSdh goto out; 35164c06356bSdh } 3517c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "Config expander %s " 35184c06356bSdh SAS_ADDR_FMT " dev id 0x%x lr 0x%x", pptr->path, 35194c06356bSdh SAS_ADDR_PRT(pptr->sas_address), pptr->device_id, pptr->link_rate); 35204c06356bSdh 35214c06356bSdh /* 35224c06356bSdh * Step 5- figure out how many phys are in this expander. 35234c06356bSdh */ 35244c06356bSdh nphy = pmcs_expander_get_nphy(pwp, pptr); 35254c06356bSdh if (nphy <= 0) { 35264c06356bSdh if (nphy == 0 && ddi_get_lbolt() < pptr->config_stop) { 35274c06356bSdh PHY_CHANGED(pwp, pptr); 35284c06356bSdh RESTART_DISCOVERY(pwp); 35294c06356bSdh } else { 3530c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 35314c06356bSdh "%s: Retries exhausted for %s, killing", __func__, 35324c06356bSdh pptr->path); 35334c06356bSdh pptr->config_stop = 0; 35344c06356bSdh pmcs_kill_changed(pwp, pptr, 0); 35354c06356bSdh } 35364c06356bSdh goto out; 35374c06356bSdh } 35384c06356bSdh 35394c06356bSdh /* 35404c06356bSdh * Step 6- Allocate a list of phys for this expander and figure out 35414c06356bSdh * what each one is. 35424c06356bSdh */ 35434c06356bSdh for (i = 0; i < nphy; i++) { 35444c06356bSdh ctmp = kmem_cache_alloc(pwp->phy_cache, KM_SLEEP); 35454c06356bSdh bzero(ctmp, sizeof (pmcs_phy_t)); 35464c06356bSdh ctmp->device_id = PMCS_INVALID_DEVICE_ID; 35474c06356bSdh ctmp->sibling = clist; 35484c06356bSdh ctmp->pend_dtype = NEW; /* Init pending dtype */ 35494c06356bSdh ctmp->config_stop = ddi_get_lbolt() + 35504c06356bSdh drv_usectohz(PMCS_MAX_CONFIG_TIME); 35514c06356bSdh clist = ctmp; 35524c06356bSdh } 35534c06356bSdh 35544c06356bSdh mutex_enter(&pwp->config_lock); 35554c06356bSdh if (pwp->config_changed) { 35564c06356bSdh RESTART_DISCOVERY_LOCKED(pwp); 35574c06356bSdh mutex_exit(&pwp->config_lock); 35584c06356bSdh /* 35594c06356bSdh * Clean up the newly allocated PHYs and return 35604c06356bSdh */ 35614c06356bSdh while (clist) { 35624c06356bSdh ctmp = clist->sibling; 35634c06356bSdh kmem_cache_free(pwp->phy_cache, clist); 35644c06356bSdh clist = ctmp; 35654c06356bSdh } 35664c06356bSdh return; 35674c06356bSdh } 35684c06356bSdh mutex_exit(&pwp->config_lock); 35694c06356bSdh 35704c06356bSdh /* 35714c06356bSdh * Step 7- Now fill in the rest of the static portions of the phy. 35724c06356bSdh */ 35734c06356bSdh for (i = 0, ctmp = clist; ctmp; ctmp = ctmp->sibling, i++) { 35744c06356bSdh ctmp->parent = pptr; 35754c06356bSdh ctmp->pwp = pwp; 35764c06356bSdh ctmp->level = pptr->level+1; 35774c06356bSdh ctmp->portid = pptr->portid; 35784c06356bSdh if (ctmp->tolerates_sas2) { 35794c06356bSdh ASSERT(i < SAS2_PHYNUM_MAX); 35804c06356bSdh ctmp->phynum = i & SAS2_PHYNUM_MASK; 35814c06356bSdh } else { 35824c06356bSdh ASSERT(i < SAS_PHYNUM_MAX); 35834c06356bSdh ctmp->phynum = i & SAS_PHYNUM_MASK; 35844c06356bSdh } 35854c06356bSdh pmcs_phy_name(pwp, ctmp, ctmp->path, sizeof (ctmp->path)); 35864c06356bSdh pmcs_lock_phy(ctmp); 35874c06356bSdh } 35884c06356bSdh 35894c06356bSdh /* 35904c06356bSdh * Step 8- Discover things about each phy in the expander. 35914c06356bSdh */ 35924c06356bSdh for (i = 0, ctmp = clist; ctmp; ctmp = ctmp->sibling, i++) { 35934c06356bSdh result = pmcs_expander_content_discover(pwp, pptr, ctmp); 35944c06356bSdh if (result <= 0) { 35954c06356bSdh if (ddi_get_lbolt() < pptr->config_stop) { 35964c06356bSdh PHY_CHANGED(pwp, pptr); 35974c06356bSdh RESTART_DISCOVERY(pwp); 35984c06356bSdh } else { 35994c06356bSdh pptr->config_stop = 0; 3600c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 36014c06356bSdh "%s: Retries exhausted for %s, killing", 36024c06356bSdh __func__, pptr->path); 36034c06356bSdh pmcs_kill_changed(pwp, pptr, 0); 36044c06356bSdh } 36054c06356bSdh goto out; 36064c06356bSdh } 36074c06356bSdh 36084c06356bSdh /* Set pend_dtype to dtype for 1st time initialization */ 36094c06356bSdh ctmp->pend_dtype = ctmp->dtype; 36104c06356bSdh } 36114c06356bSdh 36124c06356bSdh /* 36139aed1621SDavid Hollister * Step 9: Install the new list on the next level. There should 36149aed1621SDavid Hollister * typically be no children pointer on this PHY. There is one known 36159aed1621SDavid Hollister * case where this can happen, though. If a root PHY goes down and 36169aed1621SDavid Hollister * comes back up before discovery can run, we will fail to remove the 36179aed1621SDavid Hollister * children from that PHY since it will no longer be marked dead. 36189aed1621SDavid Hollister * However, in this case, all children should also be marked dead. If 36199aed1621SDavid Hollister * we see that, take those children and put them on the dead_phys list. 36204c06356bSdh */ 36214c06356bSdh if (pptr->children != NULL) { 36229aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 36239aed1621SDavid Hollister "%s: Expander @ %s still has children: Clean up", 3624c3bc407cSdh __func__, pptr->path); 36259aed1621SDavid Hollister pmcs_add_dead_phys(pwp, pptr->children); 36264c06356bSdh } 36274c06356bSdh 36289aed1621SDavid Hollister /* 36299aed1621SDavid Hollister * Set the new children pointer for this expander 36309aed1621SDavid Hollister */ 36319aed1621SDavid Hollister pptr->children = clist; 36324c06356bSdh clist = NULL; 36334c06356bSdh pptr->ncphy = nphy; 36344c06356bSdh pptr->configured = 1; 36354c06356bSdh 36364c06356bSdh /* 36374c06356bSdh * We only set width if we're greater than level 0. 36384c06356bSdh */ 36394c06356bSdh if (pptr->level) { 36404c06356bSdh pptr->width = 1; 36414c06356bSdh } 36424c06356bSdh 36434c06356bSdh /* 36444c06356bSdh * Now tell the rest of the world about us, as an SMP node. 36454c06356bSdh */ 36464c06356bSdh pptr->iport = iport; 36474c06356bSdh pmcs_new_tport(pwp, pptr); 36484c06356bSdh 36494c06356bSdh out: 36504c06356bSdh while (clist) { 36514c06356bSdh ctmp = clist->sibling; 36524c06356bSdh pmcs_unlock_phy(clist); 36534c06356bSdh kmem_cache_free(pwp->phy_cache, clist); 36544c06356bSdh clist = ctmp; 36554c06356bSdh } 36564c06356bSdh } 36574c06356bSdh 36584c06356bSdh /* 36594c06356bSdh * 2. Check expanders marked changed (but not dead) to see if they still have 36604c06356bSdh * the same number of phys and the same SAS address. Mark them, their subsidiary 36614c06356bSdh * phys (if wide) and their descendents dead if anything has changed. Check the 36624c06356bSdh * the devices they contain to see if *they* have changed. If they've changed 36634c06356bSdh * from type NOTHING we leave them marked changed to be configured later 36644c06356bSdh * (picking up a new SAS address and link rate if possible). Otherwise, any 36654c06356bSdh * change in type, SAS address or removal of target role will cause us to 36664c06356bSdh * mark them (and their descendents) as dead and cause any pending commands 36674c06356bSdh * and associated devices to be removed. 36684c06356bSdh * 36694c06356bSdh * Called with PHY (pptr) locked. 36704c06356bSdh */ 36714c06356bSdh 36724c06356bSdh static void 36734c06356bSdh pmcs_check_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 36744c06356bSdh { 36754c06356bSdh int nphy, result; 36764c06356bSdh pmcs_phy_t *ctmp, *local, *local_list = NULL, *local_tail = NULL; 36774c06356bSdh boolean_t kill_changed, changed; 36784c06356bSdh 3679c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 36804c06356bSdh "%s: check %s", __func__, pptr->path); 36814c06356bSdh 36824c06356bSdh /* 36834c06356bSdh * Step 1: Mark phy as not changed. We will mark it changed if we need 36844c06356bSdh * to retry. 36854c06356bSdh */ 36864c06356bSdh pptr->changed = 0; 36874c06356bSdh 36884c06356bSdh /* 36894c06356bSdh * Reset the config_stop time. Although we're not actually configuring 36904c06356bSdh * anything here, we do want some indication of when to give up trying 36914c06356bSdh * if we can't communicate with the expander. 36924c06356bSdh */ 36934c06356bSdh pptr->config_stop = ddi_get_lbolt() + 36944c06356bSdh drv_usectohz(PMCS_MAX_CONFIG_TIME); 36954c06356bSdh 36964c06356bSdh /* 36974c06356bSdh * Step 2: Figure out how many phys are in this expander. If 36984c06356bSdh * pmcs_expander_get_nphy returns 0 we ran out of resources, 36994c06356bSdh * so reschedule and try later. If it returns another error, 37004c06356bSdh * just return. 37014c06356bSdh */ 37024c06356bSdh nphy = pmcs_expander_get_nphy(pwp, pptr); 37034c06356bSdh if (nphy <= 0) { 37044c06356bSdh if ((nphy == 0) && (ddi_get_lbolt() < pptr->config_stop)) { 37054c06356bSdh PHY_CHANGED(pwp, pptr); 37064c06356bSdh RESTART_DISCOVERY(pwp); 37074c06356bSdh } else { 37084c06356bSdh pptr->config_stop = 0; 3709c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 37104c06356bSdh "%s: Retries exhausted for %s, killing", __func__, 37114c06356bSdh pptr->path); 37124c06356bSdh pmcs_kill_changed(pwp, pptr, 0); 37134c06356bSdh } 37144c06356bSdh return; 37154c06356bSdh } 37164c06356bSdh 37174c06356bSdh /* 37184c06356bSdh * Step 3: If the number of phys don't agree, kill the old sub-tree. 37194c06356bSdh */ 37204c06356bSdh if (nphy != pptr->ncphy) { 3721c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 37224c06356bSdh "%s: number of contained phys for %s changed from %d to %d", 37234c06356bSdh __func__, pptr->path, pptr->ncphy, nphy); 37244c06356bSdh /* 37254c06356bSdh * Force a rescan of this expander after dead contents 37264c06356bSdh * are cleared and removed. 37274c06356bSdh */ 37284c06356bSdh pmcs_kill_changed(pwp, pptr, 0); 37294c06356bSdh return; 37304c06356bSdh } 37314c06356bSdh 37324c06356bSdh /* 37334c06356bSdh * Step 4: if we're at the bottom of the stack, we're done 37344c06356bSdh * (we can't have any levels below us) 37354c06356bSdh */ 37364c06356bSdh if (pptr->level == PMCS_MAX_XPND-1) { 37374c06356bSdh return; 37384c06356bSdh } 37394c06356bSdh 37404c06356bSdh /* 37414c06356bSdh * Step 5: Discover things about each phy in this expander. We do 37424c06356bSdh * this by walking the current list of contained phys and doing a 37434c06356bSdh * content discovery for it to a local phy. 37444c06356bSdh */ 37454c06356bSdh ctmp = pptr->children; 37464c06356bSdh ASSERT(ctmp); 37474c06356bSdh if (ctmp == NULL) { 3748c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 37494c06356bSdh "%s: No children attached to expander @ %s?", __func__, 37504c06356bSdh pptr->path); 37514c06356bSdh return; 37524c06356bSdh } 37534c06356bSdh 37544c06356bSdh while (ctmp) { 37554c06356bSdh /* 37564c06356bSdh * Allocate a local PHY to contain the proposed new contents 37574c06356bSdh * and link it to the rest of the local PHYs so that they 37584c06356bSdh * can all be freed later. 37594c06356bSdh */ 37604c06356bSdh local = pmcs_clone_phy(ctmp); 37614c06356bSdh 37624c06356bSdh if (local_list == NULL) { 37634c06356bSdh local_list = local; 37644c06356bSdh local_tail = local; 37654c06356bSdh } else { 37664c06356bSdh local_tail->sibling = local; 37674c06356bSdh local_tail = local; 37684c06356bSdh } 37694c06356bSdh 37704c06356bSdh /* 37714c06356bSdh * Need to lock the local PHY since pmcs_expander_content_ 37724c06356bSdh * discovery may call pmcs_clear_phy on it, which expects 37734c06356bSdh * the PHY to be locked. 37744c06356bSdh */ 37754c06356bSdh pmcs_lock_phy(local); 37764c06356bSdh result = pmcs_expander_content_discover(pwp, pptr, local); 37774c06356bSdh pmcs_unlock_phy(local); 37784c06356bSdh if (result <= 0) { 37794c06356bSdh if (ddi_get_lbolt() < pptr->config_stop) { 37804c06356bSdh PHY_CHANGED(pwp, pptr); 37814c06356bSdh RESTART_DISCOVERY(pwp); 37824c06356bSdh } else { 37834c06356bSdh pptr->config_stop = 0; 3784c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 37854c06356bSdh "%s: Retries exhausted for %s, killing", 37864c06356bSdh __func__, pptr->path); 37874c06356bSdh pmcs_kill_changed(pwp, pptr, 0); 37884c06356bSdh } 37894c06356bSdh 37904c06356bSdh /* 37914c06356bSdh * Release all the local PHYs that we allocated. 37924c06356bSdh */ 37934c06356bSdh pmcs_free_phys(pwp, local_list); 37944c06356bSdh return; 37954c06356bSdh } 37964c06356bSdh 37974c06356bSdh ctmp = ctmp->sibling; 37984c06356bSdh } 37994c06356bSdh 38004c06356bSdh /* 38014c06356bSdh * Step 6: Compare the local PHY's contents to our current PHY. If 38024c06356bSdh * there are changes, take the appropriate action. 38034c06356bSdh * This is done in two steps (step 5 above, and 6 here) so that if we 38044c06356bSdh * have to bail during this process (e.g. pmcs_expander_content_discover 38054c06356bSdh * fails), we haven't actually changed the state of any of the real 38064c06356bSdh * PHYs. Next time we come through here, we'll be starting over from 38074c06356bSdh * scratch. This keeps us from marking a changed PHY as no longer 38084c06356bSdh * changed, but then having to bail only to come back next time and 38094c06356bSdh * think that the PHY hadn't changed. If this were to happen, we 38104c06356bSdh * would fail to properly configure the device behind this PHY. 38114c06356bSdh */ 38124c06356bSdh local = local_list; 38134c06356bSdh ctmp = pptr->children; 38144c06356bSdh 38154c06356bSdh while (ctmp) { 38164c06356bSdh changed = B_FALSE; 38174c06356bSdh kill_changed = B_FALSE; 38184c06356bSdh 38194c06356bSdh /* 38204c06356bSdh * We set local to local_list prior to this loop so that we 38214c06356bSdh * can simply walk the local_list while we walk this list. The 38224c06356bSdh * two lists should be completely in sync. 38234c06356bSdh * 38244c06356bSdh * Clear the changed flag here. 38254c06356bSdh */ 38264c06356bSdh ctmp->changed = 0; 38274c06356bSdh 38284c06356bSdh if (ctmp->dtype != local->dtype) { 38294c06356bSdh if (ctmp->dtype != NOTHING) { 3830c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL, 3831c3bc407cSdh "%s: %s type changed from %s to %s " 3832c3bc407cSdh "(killing)", __func__, ctmp->path, 3833c3bc407cSdh PHY_TYPE(ctmp), PHY_TYPE(local)); 38344c06356bSdh /* 38354c06356bSdh * Force a rescan of this expander after dead 38364c06356bSdh * contents are cleared and removed. 38374c06356bSdh */ 38384c06356bSdh changed = B_TRUE; 38394c06356bSdh kill_changed = B_TRUE; 38404c06356bSdh } else { 38414c06356bSdh changed = B_TRUE; 3842c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL, 38434c06356bSdh "%s: %s type changed from NOTHING to %s", 38444c06356bSdh __func__, ctmp->path, PHY_TYPE(local)); 384573a3eccdSDavid Hollister /* 384673a3eccdSDavid Hollister * Since this PHY was nothing and is now 384773a3eccdSDavid Hollister * something, reset the config_stop timer. 384873a3eccdSDavid Hollister */ 384973a3eccdSDavid Hollister ctmp->config_stop = ddi_get_lbolt() + 385073a3eccdSDavid Hollister drv_usectohz(PMCS_MAX_CONFIG_TIME); 38514c06356bSdh } 38524c06356bSdh 38534c06356bSdh } else if (ctmp->atdt != local->atdt) { 3854c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL, "%s: " 3855c3bc407cSdh "%s attached device type changed from %d to %d " 3856c3bc407cSdh "(killing)", __func__, ctmp->path, ctmp->atdt, 3857c3bc407cSdh local->atdt); 38584c06356bSdh /* 38594c06356bSdh * Force a rescan of this expander after dead 38604c06356bSdh * contents are cleared and removed. 38614c06356bSdh */ 38624c06356bSdh changed = B_TRUE; 38634c06356bSdh 38644c06356bSdh if (local->atdt == 0) { 38654c06356bSdh kill_changed = B_TRUE; 38664c06356bSdh } 38674c06356bSdh } else if (ctmp->link_rate != local->link_rate) { 3868c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, ctmp, NULL, "%s: %s " 3869c3bc407cSdh "changed speed from %s to %s", __func__, ctmp->path, 38704c06356bSdh pmcs_get_rate(ctmp->link_rate), 38714c06356bSdh pmcs_get_rate(local->link_rate)); 38724c06356bSdh /* If the speed changed from invalid, force rescan */ 38734c06356bSdh if (!PMCS_VALID_LINK_RATE(ctmp->link_rate)) { 38744c06356bSdh changed = B_TRUE; 38754c06356bSdh RESTART_DISCOVERY(pwp); 38764c06356bSdh } else { 38774c06356bSdh /* Just update to the new link rate */ 38784c06356bSdh ctmp->link_rate = local->link_rate; 38794c06356bSdh } 38804c06356bSdh 38814c06356bSdh if (!PMCS_VALID_LINK_RATE(local->link_rate)) { 38824c06356bSdh kill_changed = B_TRUE; 38834c06356bSdh } 38844c06356bSdh } else if (memcmp(ctmp->sas_address, local->sas_address, 38854c06356bSdh sizeof (ctmp->sas_address)) != 0) { 3886c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL, 3887c3bc407cSdh "%s: SAS Addr for %s changed from " SAS_ADDR_FMT 3888c3bc407cSdh "to " SAS_ADDR_FMT " (kill old tree)", __func__, 38894c06356bSdh ctmp->path, SAS_ADDR_PRT(ctmp->sas_address), 38904c06356bSdh SAS_ADDR_PRT(local->sas_address)); 38914c06356bSdh /* 38924c06356bSdh * Force a rescan of this expander after dead 38934c06356bSdh * contents are cleared and removed. 38944c06356bSdh */ 38954c06356bSdh changed = B_TRUE; 38964c06356bSdh } else { 3897c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL, 38984c06356bSdh "%s: %s looks the same (type %s)", 38994c06356bSdh __func__, ctmp->path, PHY_TYPE(ctmp)); 39004c06356bSdh /* 39014c06356bSdh * If EXPANDER, still mark it changed so we 39024c06356bSdh * re-evaluate its contents. If it's not an expander, 39034c06356bSdh * but it hasn't been configured, also mark it as 39044c06356bSdh * changed so that it will undergo configuration. 39054c06356bSdh */ 39064c06356bSdh if (ctmp->dtype == EXPANDER) { 39074c06356bSdh changed = B_TRUE; 39084c06356bSdh } else if ((ctmp->dtype != NOTHING) && 39094c06356bSdh !ctmp->configured) { 39104c06356bSdh ctmp->changed = 1; 39114c06356bSdh } else { 39124c06356bSdh /* It simply hasn't changed */ 39134c06356bSdh ctmp->changed = 0; 39144c06356bSdh } 39154c06356bSdh } 39164c06356bSdh 39174c06356bSdh /* 39184c06356bSdh * If the PHY changed, call pmcs_kill_changed if indicated, 39194c06356bSdh * update its contents to reflect its current state and mark it 39204c06356bSdh * as changed. 39214c06356bSdh */ 39224c06356bSdh if (changed) { 39234c06356bSdh /* 39244c06356bSdh * pmcs_kill_changed will mark the PHY as changed, so 39254c06356bSdh * only do PHY_CHANGED if we did not do kill_changed. 39264c06356bSdh */ 39274c06356bSdh if (kill_changed) { 39284c06356bSdh pmcs_kill_changed(pwp, ctmp, 0); 39294c06356bSdh } else { 39304c06356bSdh /* 39314c06356bSdh * If we're not killing the device, it's not 39324c06356bSdh * dead. Mark the PHY as changed. 39334c06356bSdh */ 39344c06356bSdh PHY_CHANGED(pwp, ctmp); 39354c06356bSdh 39364c06356bSdh if (ctmp->dead) { 39374c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 3938c3bc407cSdh ctmp, NULL, "%s: Unmarking PHY %s " 3939c3bc407cSdh "dead, restarting discovery", 39404c06356bSdh __func__, ctmp->path); 39414c06356bSdh ctmp->dead = 0; 39424c06356bSdh RESTART_DISCOVERY(pwp); 39434c06356bSdh } 39444c06356bSdh } 39454c06356bSdh 39464c06356bSdh /* 39474c06356bSdh * If the dtype of this PHY is now NOTHING, mark it as 39484c06356bSdh * unconfigured. Set pend_dtype to what the new dtype 39494c06356bSdh * is. It'll get updated at the end of the discovery 39504c06356bSdh * process. 39514c06356bSdh */ 39524c06356bSdh if (local->dtype == NOTHING) { 39534c06356bSdh bzero(ctmp->sas_address, 39544c06356bSdh sizeof (local->sas_address)); 39554c06356bSdh ctmp->atdt = 0; 39564c06356bSdh ctmp->link_rate = 0; 39574c06356bSdh ctmp->pend_dtype = NOTHING; 39584c06356bSdh ctmp->configured = 0; 39594c06356bSdh } else { 39604c06356bSdh (void) memcpy(ctmp->sas_address, 39614c06356bSdh local->sas_address, 39624c06356bSdh sizeof (local->sas_address)); 39634c06356bSdh ctmp->atdt = local->atdt; 39644c06356bSdh ctmp->link_rate = local->link_rate; 39654c06356bSdh ctmp->pend_dtype = local->dtype; 39664c06356bSdh } 39674c06356bSdh } 39684c06356bSdh 39694c06356bSdh local = local->sibling; 39704c06356bSdh ctmp = ctmp->sibling; 39714c06356bSdh } 39724c06356bSdh 39734c06356bSdh /* 39744c06356bSdh * If we got to here, that means we were able to see all the PHYs 39754c06356bSdh * and we can now update all of the real PHYs with the information 39764c06356bSdh * we got on the local PHYs. Once that's done, free all the local 39774c06356bSdh * PHYs. 39784c06356bSdh */ 39794c06356bSdh 39804c06356bSdh pmcs_free_phys(pwp, local_list); 39814c06356bSdh } 39824c06356bSdh 39834c06356bSdh /* 39844c06356bSdh * Top level routine to check expanders. We call pmcs_check_expander for 39854c06356bSdh * each expander. Since we're not doing any configuration right now, it 39864c06356bSdh * doesn't matter if this is breadth-first. 39874c06356bSdh */ 398873a3eccdSDavid Hollister static void 39894c06356bSdh pmcs_check_expanders(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 39904c06356bSdh { 39914c06356bSdh pmcs_phy_t *phyp, *pnext, *pchild; 39924c06356bSdh 3993c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 3994c3bc407cSdh "%s: %s", __func__, pptr->path); 39954c06356bSdh 39964c06356bSdh /* 39974c06356bSdh * Check each expander at this level 39984c06356bSdh */ 39994c06356bSdh phyp = pptr; 400073a3eccdSDavid Hollister while (phyp) { 40014c06356bSdh pmcs_lock_phy(phyp); 40024c06356bSdh 40034c06356bSdh if ((phyp->dtype == EXPANDER) && phyp->changed && 40044c06356bSdh !phyp->dead && !phyp->subsidiary && 40054c06356bSdh phyp->configured) { 40064c06356bSdh pmcs_check_expander(pwp, phyp); 40074c06356bSdh } 40084c06356bSdh 40094c06356bSdh pnext = phyp->sibling; 40104c06356bSdh pmcs_unlock_phy(phyp); 40114c06356bSdh phyp = pnext; 40124c06356bSdh } 40134c06356bSdh 40144c06356bSdh /* 40154c06356bSdh * Now check the children 40164c06356bSdh */ 40174c06356bSdh phyp = pptr; 401873a3eccdSDavid Hollister while (phyp) { 40194c06356bSdh pmcs_lock_phy(phyp); 40204c06356bSdh pnext = phyp->sibling; 40214c06356bSdh pchild = phyp->children; 40224c06356bSdh pmcs_unlock_phy(phyp); 40234c06356bSdh 40244c06356bSdh if (pchild) { 402573a3eccdSDavid Hollister pmcs_check_expanders(pwp, pchild); 40264c06356bSdh } 40274c06356bSdh 40284c06356bSdh phyp = pnext; 40294c06356bSdh } 40304c06356bSdh } 40314c06356bSdh 40324c06356bSdh /* 40334c06356bSdh * Called with softstate and PHY locked 40344c06356bSdh */ 40354c06356bSdh static void 40364c06356bSdh pmcs_clear_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr, int level) 40374c06356bSdh { 40384c06356bSdh pmcs_phy_t *ctmp; 40394c06356bSdh 40404c06356bSdh ASSERT(mutex_owned(&pwp->lock)); 40414c06356bSdh ASSERT(mutex_owned(&pptr->phy_lock)); 40424c06356bSdh ASSERT(pptr->level < PMCS_MAX_XPND - 1); 40434c06356bSdh 4044c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 4045c3bc407cSdh "%s: checking %s", __func__, pptr->path); 40464c06356bSdh 40474c06356bSdh ctmp = pptr->children; 40484c06356bSdh while (ctmp) { 40494c06356bSdh /* 40504c06356bSdh * If the expander is dead, mark its children dead 40514c06356bSdh */ 40524c06356bSdh if (pptr->dead) { 40534c06356bSdh ctmp->dead = 1; 40544c06356bSdh } 40554c06356bSdh if (ctmp->dtype == EXPANDER) { 40564c06356bSdh pmcs_clear_expander(pwp, ctmp, level + 1); 40574c06356bSdh } 40584c06356bSdh ctmp = ctmp->sibling; 40594c06356bSdh } 40604c06356bSdh 40614c06356bSdh /* 40624c06356bSdh * If this expander is not dead, we're done here. 40634c06356bSdh */ 40644c06356bSdh if (!pptr->dead) { 40654c06356bSdh return; 40664c06356bSdh } 40674c06356bSdh 40684c06356bSdh /* 40694c06356bSdh * Now snip out the list of children below us and release them 40704c06356bSdh */ 40719aed1621SDavid Hollister if (pptr->children) { 40729aed1621SDavid Hollister pmcs_add_dead_phys(pwp, pptr->children); 40734c06356bSdh } 40744c06356bSdh 40754c06356bSdh pptr->children = NULL; 40764c06356bSdh 40774c06356bSdh /* 40784c06356bSdh * Clear subsidiary phys as well. Getting the parent's PHY lock 40794c06356bSdh * is only necessary if level == 0 since otherwise the parent is 40804c06356bSdh * already locked. 40814c06356bSdh */ 40824c06356bSdh if (!IS_ROOT_PHY(pptr)) { 40834c06356bSdh if (level == 0) { 40844c06356bSdh mutex_enter(&pptr->parent->phy_lock); 40854c06356bSdh } 40864c06356bSdh ctmp = pptr->parent->children; 40874c06356bSdh if (level == 0) { 40884c06356bSdh mutex_exit(&pptr->parent->phy_lock); 40894c06356bSdh } 40904c06356bSdh } else { 40914c06356bSdh ctmp = pwp->root_phys; 40924c06356bSdh } 40934c06356bSdh 40944c06356bSdh while (ctmp) { 40954c06356bSdh if (ctmp == pptr) { 40964c06356bSdh ctmp = ctmp->sibling; 40974c06356bSdh continue; 40984c06356bSdh } 40994c06356bSdh /* 41004c06356bSdh * We only need to lock subsidiary PHYs on the level 0 41014c06356bSdh * expander. Any children of that expander, subsidiaries or 41024c06356bSdh * not, will already be locked. 41034c06356bSdh */ 41044c06356bSdh if (level == 0) { 41054c06356bSdh pmcs_lock_phy(ctmp); 41064c06356bSdh } 41074c06356bSdh if (ctmp->dtype != EXPANDER || ctmp->subsidiary == 0 || 41084c06356bSdh memcmp(ctmp->sas_address, pptr->sas_address, 41094c06356bSdh sizeof (ctmp->sas_address)) != 0) { 41104c06356bSdh if (level == 0) { 41114c06356bSdh pmcs_unlock_phy(ctmp); 41124c06356bSdh } 41134c06356bSdh ctmp = ctmp->sibling; 41144c06356bSdh continue; 41154c06356bSdh } 4116c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL, 4117c3bc407cSdh "%s: subsidiary %s", __func__, ctmp->path); 41184c06356bSdh pmcs_clear_phy(pwp, ctmp); 41194c06356bSdh if (level == 0) { 41204c06356bSdh pmcs_unlock_phy(ctmp); 41214c06356bSdh } 41224c06356bSdh ctmp = ctmp->sibling; 41234c06356bSdh } 41244c06356bSdh 41254c06356bSdh pmcs_clear_phy(pwp, pptr); 41264c06356bSdh } 41274c06356bSdh 41284c06356bSdh /* 41294c06356bSdh * Called with PHY locked and with scratch acquired. We return 0 if 41304c06356bSdh * we fail to allocate resources or notice that the configuration 41314c06356bSdh * count changed while we were running the command. We return 41324c06356bSdh * less than zero if we had an I/O error or received an unsupported 41334c06356bSdh * configuration. Otherwise we return the number of phys in the 41344c06356bSdh * expander. 41354c06356bSdh */ 41364c06356bSdh #define DFM(m, y) if (m == NULL) m = y 41374c06356bSdh static int 41384c06356bSdh pmcs_expander_get_nphy(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 41394c06356bSdh { 41404c06356bSdh struct pmcwork *pwrk; 41414c06356bSdh char buf[64]; 41424c06356bSdh const uint_t rdoff = 0x100; /* returned data offset */ 41434c06356bSdh smp_response_frame_t *srf; 41444c06356bSdh smp_report_general_resp_t *srgr; 41454c06356bSdh uint32_t msg[PMCS_MSG_SIZE], *ptr, htag, status, ival; 41469aed1621SDavid Hollister int result = 0; 41474c06356bSdh 41484c06356bSdh ival = 0x40001100; 41499aed1621SDavid Hollister 41504c06356bSdh again: 41519aed1621SDavid Hollister if (!pptr->iport || !pptr->valid_device_id) { 41529aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target, 41539aed1621SDavid Hollister "%s: Can't reach PHY %s", __func__, pptr->path); 41549aed1621SDavid Hollister goto out; 41559aed1621SDavid Hollister } 41569aed1621SDavid Hollister 41574c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 41584c06356bSdh if (pwrk == NULL) { 41594c06356bSdh goto out; 41604c06356bSdh } 41614c06356bSdh (void) memset(pwp->scratch, 0x77, PMCS_SCRATCH_SIZE); 41624c06356bSdh pwrk->arg = pwp->scratch; 41634c06356bSdh pwrk->dtype = pptr->dtype; 41644c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 41654c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 41664c06356bSdh if (ptr == NULL) { 41674c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 4168c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, NULL, 4169c3bc407cSdh "%s: GET_IQ_ENTRY failed", __func__); 41704c06356bSdh pmcs_pwork(pwp, pwrk); 41714c06356bSdh goto out; 41724c06356bSdh } 41734c06356bSdh 41744c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST)); 41754c06356bSdh msg[1] = LE_32(pwrk->htag); 41764c06356bSdh msg[2] = LE_32(pptr->device_id); 41774c06356bSdh msg[3] = LE_32((4 << SMP_REQUEST_LENGTH_SHIFT) | SMP_INDIRECT_RESPONSE); 41784c06356bSdh /* 41794c06356bSdh * Send SMP REPORT GENERAL (of either SAS1.1 or SAS2 flavors). 41804c06356bSdh */ 41814c06356bSdh msg[4] = BE_32(ival); 41824c06356bSdh msg[5] = 0; 41834c06356bSdh msg[6] = 0; 41844c06356bSdh msg[7] = 0; 41854c06356bSdh msg[8] = 0; 41864c06356bSdh msg[9] = 0; 41874c06356bSdh msg[10] = 0; 41884c06356bSdh msg[11] = 0; 41894c06356bSdh msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff)); 41904c06356bSdh msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff)); 41914c06356bSdh msg[14] = LE_32(PMCS_SCRATCH_SIZE - rdoff); 41924c06356bSdh msg[15] = 0; 41934c06356bSdh 41944c06356bSdh COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE); 41956745c559SJesse Butler 41966745c559SJesse Butler /* SMP serialization */ 41976745c559SJesse Butler pmcs_smp_acquire(pptr->iport); 41986745c559SJesse Butler 41994c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 42004c06356bSdh htag = pwrk->htag; 42014c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 42024c06356bSdh 42034c06356bSdh pmcs_unlock_phy(pptr); 42044c06356bSdh WAIT_FOR(pwrk, 1000, result); 4205601c90f1SSrikanth, Ramana /* Release SMP lock before reacquiring PHY lock */ 42066745c559SJesse Butler pmcs_smp_release(pptr->iport); 4207601c90f1SSrikanth, Ramana pmcs_lock_phy(pptr); 42086745c559SJesse Butler 42094c06356bSdh pmcs_pwork(pwp, pwrk); 42104c06356bSdh 42114c06356bSdh mutex_enter(&pwp->config_lock); 42124c06356bSdh if (pwp->config_changed) { 42134c06356bSdh RESTART_DISCOVERY_LOCKED(pwp); 42144c06356bSdh mutex_exit(&pwp->config_lock); 42154c06356bSdh result = 0; 42164c06356bSdh goto out; 42174c06356bSdh } 42184c06356bSdh mutex_exit(&pwp->config_lock); 42194c06356bSdh 42204c06356bSdh if (result) { 42214c06356bSdh pmcs_timed_out(pwp, htag, __func__); 4222c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 42234c06356bSdh "%s: Issuing SMP ABORT for htag 0x%08x", __func__, htag); 42244c06356bSdh if (pmcs_abort(pwp, pptr, htag, 0, 0)) { 4225c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 42264c06356bSdh "%s: Unable to issue SMP ABORT for htag 0x%08x", 42274c06356bSdh __func__, htag); 42284c06356bSdh } else { 4229c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 42304c06356bSdh "%s: Issuing SMP ABORT for htag 0x%08x", 42314c06356bSdh __func__, htag); 42324c06356bSdh } 42334c06356bSdh result = 0; 42344c06356bSdh goto out; 42354c06356bSdh } 42364c06356bSdh ptr = (void *)pwp->scratch; 42374c06356bSdh status = LE_32(ptr[2]); 42384c06356bSdh if (status == PMCOUT_STATUS_UNDERFLOW || 42394c06356bSdh status == PMCOUT_STATUS_OVERFLOW) { 4240c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, pptr, NULL, 42414c06356bSdh "%s: over/underflow", __func__); 42424c06356bSdh status = PMCOUT_STATUS_OK; 42434c06356bSdh } 42444c06356bSdh srf = (smp_response_frame_t *)&((uint32_t *)pwp->scratch)[rdoff >> 2]; 42454c06356bSdh srgr = (smp_report_general_resp_t *) 42464c06356bSdh &((uint32_t *)pwp->scratch)[(rdoff >> 2)+1]; 42474c06356bSdh 42484c06356bSdh if (status != PMCOUT_STATUS_OK) { 42494c06356bSdh char *nag = NULL; 42504c06356bSdh (void) snprintf(buf, sizeof (buf), 42514c06356bSdh "%s: SMP op failed (0x%x)", __func__, status); 42524c06356bSdh switch (status) { 42534c06356bSdh case PMCOUT_STATUS_IO_PORT_IN_RESET: 42544c06356bSdh DFM(nag, "I/O Port In Reset"); 42554c06356bSdh /* FALLTHROUGH */ 42564c06356bSdh case PMCOUT_STATUS_ERROR_HW_TIMEOUT: 42574c06356bSdh DFM(nag, "Hardware Timeout"); 42584c06356bSdh /* FALLTHROUGH */ 42594c06356bSdh case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE: 42604c06356bSdh DFM(nag, "Internal SMP Resource Failure"); 42614c06356bSdh /* FALLTHROUGH */ 42624c06356bSdh case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY: 42634c06356bSdh DFM(nag, "PHY Not Ready"); 42644c06356bSdh /* FALLTHROUGH */ 42654c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: 42664c06356bSdh DFM(nag, "Connection Rate Not Supported"); 42674c06356bSdh /* FALLTHROUGH */ 42684c06356bSdh case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT: 42694c06356bSdh DFM(nag, "Open Retry Timeout"); 42704c06356bSdh /* FALLTHROUGH */ 4271a25672a1SDavid Hollister case PMCOUT_STATUS_IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY: 4272a25672a1SDavid Hollister DFM(nag, "HW Resource Busy"); 4273a25672a1SDavid Hollister /* FALLTHROUGH */ 42744c06356bSdh case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR: 42754c06356bSdh DFM(nag, "Response Connection Error"); 4276c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 42774c06356bSdh "%s: expander %s SMP operation failed (%s)", 42784c06356bSdh __func__, pptr->path, nag); 42794c06356bSdh break; 42804c06356bSdh 42814c06356bSdh /* 42824c06356bSdh * For the IO_DS_NON_OPERATIONAL case, we need to kick off 42834c06356bSdh * device state recovery and return 0 so that the caller 42844c06356bSdh * doesn't assume this expander is dead for good. 42854c06356bSdh */ 42864c06356bSdh case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL: { 42874c06356bSdh pmcs_xscsi_t *xp = pptr->target; 42884c06356bSdh 4289c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, pptr, xp, 42904c06356bSdh "%s: expander %s device state non-operational", 42914c06356bSdh __func__, pptr->path); 42924c06356bSdh 42934c06356bSdh if (xp == NULL) { 42946745c559SJesse Butler /* 42956745c559SJesse Butler * Kick off recovery right now. 42966745c559SJesse Butler */ 42976745c559SJesse Butler SCHEDULE_WORK(pwp, PMCS_WORK_DS_ERR_RECOVERY); 42986745c559SJesse Butler (void) ddi_taskq_dispatch(pwp->tq, pmcs_worker, 42996745c559SJesse Butler pwp, DDI_NOSLEEP); 43006745c559SJesse Butler } else { 43016745c559SJesse Butler mutex_enter(&xp->statlock); 43026745c559SJesse Butler pmcs_start_dev_state_recovery(xp, pptr); 43036745c559SJesse Butler mutex_exit(&xp->statlock); 43044c06356bSdh } 43054c06356bSdh 43064c06356bSdh break; 43074c06356bSdh } 43084c06356bSdh 43094c06356bSdh default: 43104c06356bSdh pmcs_print_entry(pwp, PMCS_PRT_DEBUG, buf, ptr); 43114c06356bSdh result = -EIO; 43124c06356bSdh break; 43134c06356bSdh } 43144c06356bSdh } else if (srf->srf_frame_type != SMP_FRAME_TYPE_RESPONSE) { 4315c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 43164c06356bSdh "%s: bad response frame type 0x%x", 43174c06356bSdh __func__, srf->srf_frame_type); 43184c06356bSdh result = -EINVAL; 43194c06356bSdh } else if (srf->srf_function != SMP_FUNC_REPORT_GENERAL) { 4320c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 4321c3bc407cSdh "%s: bad response function 0x%x", 43224c06356bSdh __func__, srf->srf_function); 43234c06356bSdh result = -EINVAL; 43244c06356bSdh } else if (srf->srf_result != 0) { 43254c06356bSdh /* 43264c06356bSdh * Check to see if we have a value of 3 for failure and 43274c06356bSdh * whether we were using a SAS2.0 allocation length value 43284c06356bSdh * and retry without it. 43294c06356bSdh */ 43304c06356bSdh if (srf->srf_result == 3 && (ival & 0xff00)) { 43314c06356bSdh ival &= ~0xff00; 4332c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 43334c06356bSdh "%s: err 0x%x with SAS2 request- retry with SAS1", 43344c06356bSdh __func__, srf->srf_result); 43354c06356bSdh goto again; 43364c06356bSdh } 4337c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 4338c3bc407cSdh "%s: bad response 0x%x", __func__, srf->srf_result); 43394c06356bSdh result = -EINVAL; 43404c06356bSdh } else if (srgr->srgr_configuring) { 4341c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 43424c06356bSdh "%s: expander at phy %s is still configuring", 43434c06356bSdh __func__, pptr->path); 43444c06356bSdh result = 0; 43454c06356bSdh } else { 43464c06356bSdh result = srgr->srgr_number_of_phys; 43474c06356bSdh if (ival & 0xff00) { 43484c06356bSdh pptr->tolerates_sas2 = 1; 43494c06356bSdh } 43509aed1621SDavid Hollister /* 43519aed1621SDavid Hollister * Save off the REPORT_GENERAL response 43529aed1621SDavid Hollister */ 43539aed1621SDavid Hollister bcopy(srgr, &pptr->rg_resp, sizeof (smp_report_general_resp_t)); 4354c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 43554c06356bSdh "%s has %d phys and %s SAS2", pptr->path, result, 43564c06356bSdh pptr->tolerates_sas2? "tolerates" : "does not tolerate"); 43574c06356bSdh } 43584c06356bSdh out: 43594c06356bSdh return (result); 43604c06356bSdh } 43614c06356bSdh 43624c06356bSdh /* 43634c06356bSdh * Called with expander locked (and thus, pptr) as well as all PHYs up to 43644c06356bSdh * the root, and scratch acquired. Return 0 if we fail to allocate resources 43654c06356bSdh * or notice that the configuration changed while we were running the command. 43664c06356bSdh * 43674c06356bSdh * We return less than zero if we had an I/O error or received an 43684c06356bSdh * unsupported configuration. 43694c06356bSdh */ 43704c06356bSdh static int 43714c06356bSdh pmcs_expander_content_discover(pmcs_hw_t *pwp, pmcs_phy_t *expander, 43724c06356bSdh pmcs_phy_t *pptr) 43734c06356bSdh { 43744c06356bSdh struct pmcwork *pwrk; 43754c06356bSdh char buf[64]; 43764c06356bSdh uint8_t sas_address[8]; 43774c06356bSdh uint8_t att_sas_address[8]; 43784c06356bSdh smp_response_frame_t *srf; 43794c06356bSdh smp_discover_resp_t *sdr; 43804c06356bSdh const uint_t rdoff = 0x100; /* returned data offset */ 43814c06356bSdh uint8_t *roff; 43824c06356bSdh uint32_t status, *ptr, msg[PMCS_MSG_SIZE], htag; 43839aed1621SDavid Hollister int result = 0; 43844c06356bSdh uint8_t ini_support; 43854c06356bSdh uint8_t tgt_support; 43864c06356bSdh 43879aed1621SDavid Hollister if (!expander->iport || !expander->valid_device_id) { 43889aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, expander, expander->target, 43899aed1621SDavid Hollister "%s: Can't reach PHY %s", __func__, expander->path); 43909aed1621SDavid Hollister goto out; 43919aed1621SDavid Hollister } 43929aed1621SDavid Hollister 43934c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, expander); 43944c06356bSdh if (pwrk == NULL) { 43954c06356bSdh goto out; 43964c06356bSdh } 43974c06356bSdh (void) memset(pwp->scratch, 0x77, PMCS_SCRATCH_SIZE); 43984c06356bSdh pwrk->arg = pwp->scratch; 43994c06356bSdh pwrk->dtype = expander->dtype; 44004c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST)); 44014c06356bSdh msg[1] = LE_32(pwrk->htag); 44024c06356bSdh msg[2] = LE_32(expander->device_id); 44034c06356bSdh msg[3] = LE_32((12 << SMP_REQUEST_LENGTH_SHIFT) | 44044c06356bSdh SMP_INDIRECT_RESPONSE); 44054c06356bSdh /* 44064c06356bSdh * Send SMP DISCOVER (of either SAS1.1 or SAS2 flavors). 44074c06356bSdh */ 44084c06356bSdh if (expander->tolerates_sas2) { 44094c06356bSdh msg[4] = BE_32(0x40101B00); 44104c06356bSdh } else { 44114c06356bSdh msg[4] = BE_32(0x40100000); 44124c06356bSdh } 44134c06356bSdh msg[5] = 0; 44144c06356bSdh msg[6] = BE_32((pptr->phynum << 16)); 44154c06356bSdh msg[7] = 0; 44164c06356bSdh msg[8] = 0; 44174c06356bSdh msg[9] = 0; 44184c06356bSdh msg[10] = 0; 44194c06356bSdh msg[11] = 0; 44204c06356bSdh msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff)); 44214c06356bSdh msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff)); 44224c06356bSdh msg[14] = LE_32(PMCS_SCRATCH_SIZE - rdoff); 44234c06356bSdh msg[15] = 0; 44244c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 44254c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 44264c06356bSdh if (ptr == NULL) { 44274c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 44284c06356bSdh goto out; 44294c06356bSdh } 44304c06356bSdh 44314c06356bSdh COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE); 44326745c559SJesse Butler 44336745c559SJesse Butler /* SMP serialization */ 44346745c559SJesse Butler pmcs_smp_acquire(expander->iport); 44356745c559SJesse Butler 44364c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 44374c06356bSdh htag = pwrk->htag; 44384c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 44394c06356bSdh 44404c06356bSdh /* 44414c06356bSdh * Drop PHY lock while waiting so other completions aren't potentially 44424c06356bSdh * blocked. 44434c06356bSdh */ 44444c06356bSdh pmcs_unlock_phy(expander); 44454c06356bSdh WAIT_FOR(pwrk, 1000, result); 4446601c90f1SSrikanth, Ramana /* Release SMP lock before reacquiring PHY lock */ 44476745c559SJesse Butler pmcs_smp_release(expander->iport); 4448601c90f1SSrikanth, Ramana pmcs_lock_phy(expander); 44496745c559SJesse Butler 44504c06356bSdh pmcs_pwork(pwp, pwrk); 44514c06356bSdh 44524c06356bSdh mutex_enter(&pwp->config_lock); 44534c06356bSdh if (pwp->config_changed) { 44544c06356bSdh RESTART_DISCOVERY_LOCKED(pwp); 44554c06356bSdh mutex_exit(&pwp->config_lock); 44564c06356bSdh result = 0; 44574c06356bSdh goto out; 44584c06356bSdh } 44594c06356bSdh mutex_exit(&pwp->config_lock); 44604c06356bSdh 44614c06356bSdh if (result) { 44626745c559SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__); 44634c06356bSdh if (pmcs_abort(pwp, expander, htag, 0, 0)) { 4464c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 44654c06356bSdh "%s: Unable to issue SMP ABORT for htag 0x%08x", 44664c06356bSdh __func__, htag); 44674c06356bSdh } else { 4468c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 44694c06356bSdh "%s: Issuing SMP ABORT for htag 0x%08x", 44704c06356bSdh __func__, htag); 44714c06356bSdh } 44724c06356bSdh result = -ETIMEDOUT; 44734c06356bSdh goto out; 44744c06356bSdh } 44754c06356bSdh ptr = (void *)pwp->scratch; 44764c06356bSdh /* 44774c06356bSdh * Point roff to the DMA offset for returned data 44784c06356bSdh */ 44794c06356bSdh roff = pwp->scratch; 44804c06356bSdh roff += rdoff; 44814c06356bSdh srf = (smp_response_frame_t *)roff; 44824c06356bSdh sdr = (smp_discover_resp_t *)(roff+4); 44834c06356bSdh status = LE_32(ptr[2]); 44844c06356bSdh if (status == PMCOUT_STATUS_UNDERFLOW || 44854c06356bSdh status == PMCOUT_STATUS_OVERFLOW) { 4486c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, pptr, NULL, 44874c06356bSdh "%s: over/underflow", __func__); 44884c06356bSdh status = PMCOUT_STATUS_OK; 44894c06356bSdh } 44904c06356bSdh if (status != PMCOUT_STATUS_OK) { 44914c06356bSdh char *nag = NULL; 44924c06356bSdh (void) snprintf(buf, sizeof (buf), 44934c06356bSdh "%s: SMP op failed (0x%x)", __func__, status); 44944c06356bSdh switch (status) { 44954c06356bSdh case PMCOUT_STATUS_ERROR_HW_TIMEOUT: 44964c06356bSdh DFM(nag, "Hardware Timeout"); 44974c06356bSdh /* FALLTHROUGH */ 44984c06356bSdh case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE: 44994c06356bSdh DFM(nag, "Internal SMP Resource Failure"); 45004c06356bSdh /* FALLTHROUGH */ 45014c06356bSdh case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY: 45024c06356bSdh DFM(nag, "PHY Not Ready"); 45034c06356bSdh /* FALLTHROUGH */ 45044c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: 45054c06356bSdh DFM(nag, "Connection Rate Not Supported"); 45064c06356bSdh /* FALLTHROUGH */ 45074c06356bSdh case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT: 45084c06356bSdh DFM(nag, "Open Retry Timeout"); 45094c06356bSdh /* FALLTHROUGH */ 4510a25672a1SDavid Hollister case PMCOUT_STATUS_IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY: 4511a25672a1SDavid Hollister DFM(nag, "HW Resource Busy"); 4512a25672a1SDavid Hollister /* FALLTHROUGH */ 45134c06356bSdh case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR: 45144c06356bSdh DFM(nag, "Response Connection Error"); 4515c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 45164c06356bSdh "%s: expander %s SMP operation failed (%s)", 45174c06356bSdh __func__, pptr->path, nag); 45184c06356bSdh break; 45194c06356bSdh default: 45204c06356bSdh pmcs_print_entry(pwp, PMCS_PRT_DEBUG, buf, ptr); 45214c06356bSdh result = -EIO; 45224c06356bSdh break; 45234c06356bSdh } 45244c06356bSdh goto out; 45254c06356bSdh } else if (srf->srf_frame_type != SMP_FRAME_TYPE_RESPONSE) { 4526c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 45274c06356bSdh "%s: bad response frame type 0x%x", 45284c06356bSdh __func__, srf->srf_frame_type); 45294c06356bSdh result = -EINVAL; 45304c06356bSdh goto out; 45314c06356bSdh } else if (srf->srf_function != SMP_FUNC_DISCOVER) { 4532c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 4533c3bc407cSdh "%s: bad response function 0x%x", 45344c06356bSdh __func__, srf->srf_function); 45354c06356bSdh result = -EINVAL; 45364c06356bSdh goto out; 45374c06356bSdh } else if (srf->srf_result != SMP_RES_FUNCTION_ACCEPTED) { 45384c06356bSdh result = pmcs_smp_function_result(pwp, srf); 45394c06356bSdh /* Need not fail if PHY is Vacant */ 45404c06356bSdh if (result != SMP_RES_PHY_VACANT) { 45414c06356bSdh result = -EINVAL; 45424c06356bSdh goto out; 45434c06356bSdh } 45444c06356bSdh } 45454c06356bSdh 45469aed1621SDavid Hollister /* 45479aed1621SDavid Hollister * Save off the DISCOVER response 45489aed1621SDavid Hollister */ 45499aed1621SDavid Hollister bcopy(sdr, &pptr->disc_resp, sizeof (smp_discover_resp_t)); 45509aed1621SDavid Hollister 45514c06356bSdh ini_support = (sdr->sdr_attached_sata_host | 45524c06356bSdh (sdr->sdr_attached_smp_initiator << 1) | 45534c06356bSdh (sdr->sdr_attached_stp_initiator << 2) | 45544c06356bSdh (sdr->sdr_attached_ssp_initiator << 3)); 45554c06356bSdh 45564c06356bSdh tgt_support = (sdr->sdr_attached_sata_device | 45574c06356bSdh (sdr->sdr_attached_smp_target << 1) | 45584c06356bSdh (sdr->sdr_attached_stp_target << 2) | 45594c06356bSdh (sdr->sdr_attached_ssp_target << 3)); 45604c06356bSdh 45614c06356bSdh pmcs_wwn2barray(BE_64(sdr->sdr_sas_addr), sas_address); 45624c06356bSdh pmcs_wwn2barray(BE_64(sdr->sdr_attached_sas_addr), att_sas_address); 45634c06356bSdh 45649aed1621SDavid Hollister /* 45659aed1621SDavid Hollister * Set the routing attribute regardless of the PHY type. 45669aed1621SDavid Hollister */ 45679aed1621SDavid Hollister pptr->routing_attr = sdr->sdr_routing_attr; 45689aed1621SDavid Hollister 45694c06356bSdh switch (sdr->sdr_attached_device_type) { 45704c06356bSdh case SAS_IF_DTYPE_ENDPOINT: 4571c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 45724c06356bSdh "exp_content: %s atdt=0x%x lr=%x is=%x ts=%x SAS=" 45734c06356bSdh SAS_ADDR_FMT " attSAS=" SAS_ADDR_FMT " atPHY=%x", 45744c06356bSdh pptr->path, 45754c06356bSdh sdr->sdr_attached_device_type, 45764c06356bSdh sdr->sdr_negotiated_logical_link_rate, 45774c06356bSdh ini_support, 45784c06356bSdh tgt_support, 45794c06356bSdh SAS_ADDR_PRT(sas_address), 45804c06356bSdh SAS_ADDR_PRT(att_sas_address), 45814c06356bSdh sdr->sdr_attached_phy_identifier); 45824c06356bSdh 45834c06356bSdh if (sdr->sdr_attached_sata_device || 45844c06356bSdh sdr->sdr_attached_stp_target) { 45854c06356bSdh pptr->dtype = SATA; 45864c06356bSdh } else if (sdr->sdr_attached_ssp_target) { 45874c06356bSdh pptr->dtype = SAS; 45884c06356bSdh } else if (tgt_support || ini_support) { 4589c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 4590c3bc407cSdh "%s: %s has tgt support=%x init support=(%x)", 45914c06356bSdh __func__, pptr->path, tgt_support, ini_support); 45924c06356bSdh } 45939aed1621SDavid Hollister 45949aed1621SDavid Hollister switch (pptr->routing_attr) { 45959aed1621SDavid Hollister case SMP_ROUTING_SUBTRACTIVE: 45969aed1621SDavid Hollister case SMP_ROUTING_TABLE: 45979aed1621SDavid Hollister case SMP_ROUTING_DIRECT: 45989aed1621SDavid Hollister pptr->routing_method = SMP_ROUTING_DIRECT; 45999aed1621SDavid Hollister break; 46009aed1621SDavid Hollister default: 46019aed1621SDavid Hollister pptr->routing_method = 0xff; /* Invalid method */ 46029aed1621SDavid Hollister break; 46039aed1621SDavid Hollister } 4604616875b4SDavid Hollister pmcs_update_phy_pm_props(pptr, (1ULL << pptr->phynum), 4605616875b4SDavid Hollister (1ULL << sdr->sdr_attached_phy_identifier), B_TRUE); 46064c06356bSdh break; 46074c06356bSdh case SAS_IF_DTYPE_EDGE: 46084c06356bSdh case SAS_IF_DTYPE_FANOUT: 4609c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 46104c06356bSdh "exp_content: %s atdt=0x%x lr=%x is=%x ts=%x SAS=" 46114c06356bSdh SAS_ADDR_FMT " attSAS=" SAS_ADDR_FMT " atPHY=%x", 46124c06356bSdh pptr->path, 46134c06356bSdh sdr->sdr_attached_device_type, 46144c06356bSdh sdr->sdr_negotiated_logical_link_rate, 46154c06356bSdh ini_support, 46164c06356bSdh tgt_support, 46174c06356bSdh SAS_ADDR_PRT(sas_address), 46184c06356bSdh SAS_ADDR_PRT(att_sas_address), 46194c06356bSdh sdr->sdr_attached_phy_identifier); 46204c06356bSdh if (sdr->sdr_attached_smp_target) { 46214c06356bSdh /* 46224c06356bSdh * Avoid configuring phys that just point back 46234c06356bSdh * at a parent phy 46244c06356bSdh */ 46254c06356bSdh if (expander->parent && 46264c06356bSdh memcmp(expander->parent->sas_address, 46274c06356bSdh att_sas_address, 46284c06356bSdh sizeof (expander->parent->sas_address)) == 0) { 4629c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG3, pptr, NULL, 46304c06356bSdh "%s: skipping port back to parent " 46314c06356bSdh "expander (%s)", __func__, pptr->path); 46324c06356bSdh pptr->dtype = NOTHING; 46334c06356bSdh break; 46344c06356bSdh } 46354c06356bSdh pptr->dtype = EXPANDER; 46364c06356bSdh 46374c06356bSdh } else if (tgt_support || ini_support) { 4638c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 4639c3bc407cSdh "%s has tgt support=%x init support=(%x)", 46404c06356bSdh pptr->path, tgt_support, ini_support); 46414c06356bSdh pptr->dtype = EXPANDER; 46424c06356bSdh } 46439aed1621SDavid Hollister if (pptr->routing_attr == SMP_ROUTING_DIRECT) { 46449aed1621SDavid Hollister pptr->routing_method = 0xff; /* Invalid method */ 46459aed1621SDavid Hollister } else { 46469aed1621SDavid Hollister pptr->routing_method = pptr->routing_attr; 46479aed1621SDavid Hollister } 4648616875b4SDavid Hollister pmcs_update_phy_pm_props(pptr, (1ULL << pptr->phynum), 4649616875b4SDavid Hollister (1ULL << sdr->sdr_attached_phy_identifier), B_TRUE); 46504c06356bSdh break; 46514c06356bSdh default: 46524c06356bSdh pptr->dtype = NOTHING; 46534c06356bSdh break; 46544c06356bSdh } 46554c06356bSdh if (pptr->dtype != NOTHING) { 46564c06356bSdh pmcs_phy_t *ctmp; 46574c06356bSdh 46584c06356bSdh /* 46594c06356bSdh * If the attached device is a SATA device and the expander 46604c06356bSdh * is (possibly) a SAS2 compliant expander, check for whether 46614c06356bSdh * there is a NAA=5 WWN field starting at this offset and 46624c06356bSdh * use that for the SAS Address for this device. 46634c06356bSdh */ 46644c06356bSdh if (expander->tolerates_sas2 && pptr->dtype == SATA && 4665a25672a1SDavid Hollister (roff[SAS_ATTACHED_NAME_OFFSET] >> 8) == NAA_IEEE_REG) { 46664c06356bSdh (void) memcpy(pptr->sas_address, 46674c06356bSdh &roff[SAS_ATTACHED_NAME_OFFSET], 8); 46684c06356bSdh } else { 46694c06356bSdh (void) memcpy(pptr->sas_address, att_sas_address, 8); 46704c06356bSdh } 46714c06356bSdh pptr->atdt = (sdr->sdr_attached_device_type); 46724c06356bSdh /* 46734c06356bSdh * Now run up from the expander's parent up to the top to 46744c06356bSdh * make sure we only use the least common link_rate. 46754c06356bSdh */ 46764c06356bSdh for (ctmp = expander->parent; ctmp; ctmp = ctmp->parent) { 46774c06356bSdh if (ctmp->link_rate < 46784c06356bSdh sdr->sdr_negotiated_logical_link_rate) { 4679c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 46804c06356bSdh "%s: derating link rate from %x to %x due " 46814c06356bSdh "to %s being slower", pptr->path, 46824c06356bSdh sdr->sdr_negotiated_logical_link_rate, 46834c06356bSdh ctmp->link_rate, 46844c06356bSdh ctmp->path); 46854c06356bSdh sdr->sdr_negotiated_logical_link_rate = 46864c06356bSdh ctmp->link_rate; 46874c06356bSdh } 46884c06356bSdh } 46894c06356bSdh pptr->link_rate = sdr->sdr_negotiated_logical_link_rate; 46904c06356bSdh pptr->state.prog_min_rate = sdr->sdr_prog_min_phys_link_rate; 46914c06356bSdh pptr->state.hw_min_rate = sdr->sdr_hw_min_phys_link_rate; 46924c06356bSdh pptr->state.prog_max_rate = sdr->sdr_prog_max_phys_link_rate; 46934c06356bSdh pptr->state.hw_max_rate = sdr->sdr_hw_max_phys_link_rate; 46944c06356bSdh PHY_CHANGED(pwp, pptr); 46954c06356bSdh } else { 46964c06356bSdh pmcs_clear_phy(pwp, pptr); 46974c06356bSdh } 46984c06356bSdh result = 1; 46994c06356bSdh out: 47004c06356bSdh return (result); 47014c06356bSdh } 47024c06356bSdh 47034c06356bSdh /* 47044c06356bSdh * Get a work structure and assign it a tag with type and serial number 47054c06356bSdh * If a structure is returned, it is returned locked. 47064c06356bSdh */ 47074c06356bSdh pmcwork_t * 47084c06356bSdh pmcs_gwork(pmcs_hw_t *pwp, uint32_t tag_type, pmcs_phy_t *phyp) 47094c06356bSdh { 47104c06356bSdh pmcwork_t *p; 47114c06356bSdh uint16_t snum; 47124c06356bSdh uint32_t off; 47134c06356bSdh 47144c06356bSdh mutex_enter(&pwp->wfree_lock); 47154c06356bSdh p = STAILQ_FIRST(&pwp->wf); 47164c06356bSdh if (p == NULL) { 47174c06356bSdh /* 47184c06356bSdh * If we couldn't get a work structure, it's time to bite 47194c06356bSdh * the bullet, grab the pfree_lock and copy over all the 47204c06356bSdh * work structures from the pending free list to the actual 472102b04f6eSSrikanth, Ramana * free list (assuming it's not also empty). 47224c06356bSdh */ 47234c06356bSdh mutex_enter(&pwp->pfree_lock); 472402b04f6eSSrikanth, Ramana if (STAILQ_FIRST(&pwp->pf) == NULL) { 472502b04f6eSSrikanth, Ramana mutex_exit(&pwp->pfree_lock); 472602b04f6eSSrikanth, Ramana mutex_exit(&pwp->wfree_lock); 472702b04f6eSSrikanth, Ramana return (NULL); 472802b04f6eSSrikanth, Ramana } 47294c06356bSdh pwp->wf.stqh_first = pwp->pf.stqh_first; 47304c06356bSdh pwp->wf.stqh_last = pwp->pf.stqh_last; 47314c06356bSdh STAILQ_INIT(&pwp->pf); 47324c06356bSdh mutex_exit(&pwp->pfree_lock); 47334c06356bSdh 47344c06356bSdh p = STAILQ_FIRST(&pwp->wf); 473502b04f6eSSrikanth, Ramana ASSERT(p != NULL); 47364c06356bSdh } 47374c06356bSdh STAILQ_REMOVE(&pwp->wf, p, pmcwork, next); 47384c06356bSdh snum = pwp->wserno++; 47394c06356bSdh mutex_exit(&pwp->wfree_lock); 47404c06356bSdh 47414c06356bSdh off = p - pwp->work; 47424c06356bSdh 47434c06356bSdh mutex_enter(&p->lock); 47444c06356bSdh ASSERT(p->state == PMCS_WORK_STATE_NIL); 47454c06356bSdh ASSERT(p->htag == PMCS_TAG_FREE); 47464c06356bSdh p->htag = (tag_type << PMCS_TAG_TYPE_SHIFT) & PMCS_TAG_TYPE_MASK; 47474c06356bSdh p->htag |= ((snum << PMCS_TAG_SERNO_SHIFT) & PMCS_TAG_SERNO_MASK); 47484c06356bSdh p->htag |= ((off << PMCS_TAG_INDEX_SHIFT) & PMCS_TAG_INDEX_MASK); 47494c06356bSdh p->start = gethrtime(); 47504c06356bSdh p->state = PMCS_WORK_STATE_READY; 47514c06356bSdh p->ssp_event = 0; 47524c06356bSdh p->dead = 0; 47534c06356bSdh 47544c06356bSdh if (phyp) { 47554c06356bSdh p->phy = phyp; 47564c06356bSdh pmcs_inc_phy_ref_count(phyp); 47574c06356bSdh } 47584c06356bSdh 47594c06356bSdh return (p); 47604c06356bSdh } 47614c06356bSdh 47624c06356bSdh /* 47634c06356bSdh * Called with pwrk lock held. Returned with lock released. 47644c06356bSdh */ 47654c06356bSdh void 47664c06356bSdh pmcs_pwork(pmcs_hw_t *pwp, pmcwork_t *p) 47674c06356bSdh { 47684c06356bSdh ASSERT(p != NULL); 47694c06356bSdh ASSERT(mutex_owned(&p->lock)); 47704c06356bSdh 47714c06356bSdh p->last_ptr = p->ptr; 47724c06356bSdh p->last_arg = p->arg; 47734c06356bSdh p->last_phy = p->phy; 47744c06356bSdh p->last_xp = p->xp; 47754c06356bSdh p->last_htag = p->htag; 47764c06356bSdh p->last_state = p->state; 47774c06356bSdh p->finish = gethrtime(); 47784c06356bSdh 47794c06356bSdh if (p->phy) { 47804c06356bSdh pmcs_dec_phy_ref_count(p->phy); 47814c06356bSdh } 47824c06356bSdh 47834c06356bSdh p->state = PMCS_WORK_STATE_NIL; 47844c06356bSdh p->htag = PMCS_TAG_FREE; 47854c06356bSdh p->xp = NULL; 47864c06356bSdh p->ptr = NULL; 47874c06356bSdh p->arg = NULL; 47884c06356bSdh p->phy = NULL; 4789c3bc407cSdh p->abt_htag = 0; 47904c06356bSdh p->timer = 0; 47914c06356bSdh mutex_exit(&p->lock); 47924c06356bSdh 47934c06356bSdh if (mutex_tryenter(&pwp->wfree_lock) == 0) { 47944c06356bSdh mutex_enter(&pwp->pfree_lock); 47954c06356bSdh STAILQ_INSERT_TAIL(&pwp->pf, p, next); 47964c06356bSdh mutex_exit(&pwp->pfree_lock); 47974c06356bSdh } else { 47984c06356bSdh STAILQ_INSERT_TAIL(&pwp->wf, p, next); 47994c06356bSdh mutex_exit(&pwp->wfree_lock); 48004c06356bSdh } 48014c06356bSdh } 48024c06356bSdh 48034c06356bSdh /* 48044c06356bSdh * Find a work structure based upon a tag and make sure that the tag 48054c06356bSdh * serial number matches the work structure we've found. 48064c06356bSdh * If a structure is found, its lock is held upon return. 48074c06356bSdh */ 48084c06356bSdh pmcwork_t * 48094c06356bSdh pmcs_tag2wp(pmcs_hw_t *pwp, uint32_t htag) 48104c06356bSdh { 48114c06356bSdh pmcwork_t *p; 48124c06356bSdh uint32_t idx = PMCS_TAG_INDEX(htag); 48134c06356bSdh 48144c06356bSdh p = &pwp->work[idx]; 48154c06356bSdh 48164c06356bSdh mutex_enter(&p->lock); 48174c06356bSdh if (p->htag == htag) { 48184c06356bSdh return (p); 48194c06356bSdh } 48204c06356bSdh mutex_exit(&p->lock); 4821c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, 4822c3bc407cSdh "INDEX 0x%x HTAG 0x%x got p->htag 0x%x", idx, htag, p->htag); 48234c06356bSdh return (NULL); 48244c06356bSdh } 48254c06356bSdh 48264c06356bSdh /* 48274c06356bSdh * Issue an abort for a command or for all commands. 48284c06356bSdh * 48294c06356bSdh * Since this can be called from interrupt context, 48304c06356bSdh * we don't wait for completion if wait is not set. 48314c06356bSdh * 48324c06356bSdh * Called with PHY lock held. 48334c06356bSdh */ 48344c06356bSdh int 48354c06356bSdh pmcs_abort(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint32_t tag, int all_cmds, 48364c06356bSdh int wait) 48374c06356bSdh { 48384c06356bSdh pmcwork_t *pwrk; 48394c06356bSdh pmcs_xscsi_t *tgt; 48404c06356bSdh uint32_t msg[PMCS_MSG_SIZE], *ptr; 48414c06356bSdh int result, abt_type; 48424c06356bSdh uint32_t abt_htag, status; 48434c06356bSdh 48444c06356bSdh if (pptr->abort_all_start) { 4845c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, "%s: ABORT_ALL for " 4846c3bc407cSdh "(%s) already in progress.", __func__, pptr->path); 48474c06356bSdh return (EBUSY); 48484c06356bSdh } 48494c06356bSdh 48504c06356bSdh switch (pptr->dtype) { 48514c06356bSdh case SAS: 48524c06356bSdh abt_type = PMCIN_SSP_ABORT; 48534c06356bSdh break; 48544c06356bSdh case SATA: 48554c06356bSdh abt_type = PMCIN_SATA_ABORT; 48564c06356bSdh break; 48574c06356bSdh case EXPANDER: 48584c06356bSdh abt_type = PMCIN_SMP_ABORT; 48594c06356bSdh break; 48604c06356bSdh default: 48614c06356bSdh return (0); 48624c06356bSdh } 48634c06356bSdh 48644c06356bSdh pwrk = pmcs_gwork(pwp, wait ? PMCS_TAG_TYPE_WAIT : PMCS_TAG_TYPE_NONE, 48654c06356bSdh pptr); 48664c06356bSdh 48674c06356bSdh if (pwrk == NULL) { 4868c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__); 48694c06356bSdh return (ENOMEM); 48704c06356bSdh } 48714c06356bSdh 48724c06356bSdh pwrk->dtype = pptr->dtype; 48734c06356bSdh if (wait) { 48744c06356bSdh pwrk->arg = msg; 48754c06356bSdh } 48764c06356bSdh if (pptr->valid_device_id == 0) { 48774c06356bSdh pmcs_pwork(pwp, pwrk); 4878c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 4879c3bc407cSdh "%s: Invalid DeviceID", __func__); 48804c06356bSdh return (ENODEV); 48814c06356bSdh } 48824c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, abt_type)); 48834c06356bSdh msg[1] = LE_32(pwrk->htag); 48844c06356bSdh msg[2] = LE_32(pptr->device_id); 48854c06356bSdh if (all_cmds) { 48864c06356bSdh msg[3] = 0; 48874c06356bSdh msg[4] = LE_32(1); 48884c06356bSdh pwrk->ptr = NULL; 48894c06356bSdh pptr->abort_all_start = gethrtime(); 48904c06356bSdh } else { 48914c06356bSdh msg[3] = LE_32(tag); 48924c06356bSdh msg[4] = 0; 4893c3bc407cSdh pwrk->abt_htag = tag; 48944c06356bSdh } 48954c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 48964c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 48974c06356bSdh if (ptr == NULL) { 48984c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 48994c06356bSdh pmcs_pwork(pwp, pwrk); 4900c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__); 49014c06356bSdh return (ENOMEM); 49024c06356bSdh } 49034c06356bSdh 49044c06356bSdh COPY_MESSAGE(ptr, msg, 5); 49054c06356bSdh if (all_cmds) { 4906c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 49074c06356bSdh "%s: aborting all commands for %s device %s. (htag=0x%x)", 49084c06356bSdh __func__, pmcs_get_typename(pptr->dtype), pptr->path, 49094c06356bSdh msg[1]); 49104c06356bSdh } else { 4911c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 49124c06356bSdh "%s: aborting tag 0x%x for %s device %s. (htag=0x%x)", 49134c06356bSdh __func__, tag, pmcs_get_typename(pptr->dtype), pptr->path, 49144c06356bSdh msg[1]); 49154c06356bSdh } 49164c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 49174c06356bSdh 49184c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 49194c06356bSdh if (!wait) { 49204c06356bSdh mutex_exit(&pwrk->lock); 49214c06356bSdh return (0); 49224c06356bSdh } 49234c06356bSdh 49244c06356bSdh abt_htag = pwrk->htag; 49254c06356bSdh pmcs_unlock_phy(pwrk->phy); 49264c06356bSdh WAIT_FOR(pwrk, 1000, result); 49274c06356bSdh pmcs_lock_phy(pwrk->phy); 49284c06356bSdh 49294c06356bSdh tgt = pwrk->xp; 49304c06356bSdh pmcs_pwork(pwp, pwrk); 49314c06356bSdh 49324c06356bSdh if (tgt != NULL) { 49334c06356bSdh mutex_enter(&tgt->aqlock); 49344c06356bSdh if (!STAILQ_EMPTY(&tgt->aq)) { 4935c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, 49364c06356bSdh "%s: Abort complete (result=0x%x), but " 49374c06356bSdh "aq not empty (tgt 0x%p), waiting", 49384c06356bSdh __func__, result, (void *)tgt); 49394c06356bSdh cv_wait(&tgt->abort_cv, &tgt->aqlock); 49404c06356bSdh } 49414c06356bSdh mutex_exit(&tgt->aqlock); 49424c06356bSdh } 49434c06356bSdh 49444c06356bSdh if (all_cmds) { 49454c06356bSdh pptr->abort_all_start = 0; 49464c06356bSdh cv_signal(&pptr->abort_all_cv); 49474c06356bSdh } 49484c06356bSdh 49494c06356bSdh if (result) { 4950c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, 49514c06356bSdh "%s: Abort (htag 0x%08x) request timed out", 49524c06356bSdh __func__, abt_htag); 49534c06356bSdh if (tgt != NULL) { 49544c06356bSdh mutex_enter(&tgt->statlock); 49554c06356bSdh if ((tgt->dev_state != PMCS_DEVICE_STATE_IN_RECOVERY) && 49564c06356bSdh (tgt->dev_state != 49574c06356bSdh PMCS_DEVICE_STATE_NON_OPERATIONAL)) { 4958c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, 49594c06356bSdh "%s: Trying DS error recovery for tgt 0x%p", 49604c06356bSdh __func__, (void *)tgt); 49614c06356bSdh (void) pmcs_send_err_recovery_cmd(pwp, 4962145e0143Sdh PMCS_DEVICE_STATE_IN_RECOVERY, pptr, tgt); 49634c06356bSdh } 49644c06356bSdh mutex_exit(&tgt->statlock); 49654c06356bSdh } 49664c06356bSdh return (ETIMEDOUT); 49674c06356bSdh } 49684c06356bSdh 49694c06356bSdh status = LE_32(msg[2]); 49704c06356bSdh if (status != PMCOUT_STATUS_OK) { 49714c06356bSdh /* 49724c06356bSdh * The only non-success status are IO_NOT_VALID & 49734c06356bSdh * IO_ABORT_IN_PROGRESS. 49744c06356bSdh * In case of IO_ABORT_IN_PROGRESS, the other ABORT cmd's 49754c06356bSdh * status is of concern and this duplicate cmd status can 49764c06356bSdh * be ignored. 49774c06356bSdh * If IO_NOT_VALID, that's not an error per-se. 49784c06356bSdh * For abort of single I/O complete the command anyway. 49794c06356bSdh * If, however, we were aborting all, that is a problem 49804c06356bSdh * as IO_NOT_VALID really means that the IO or device is 49814c06356bSdh * not there. So, discovery process will take of the cleanup. 49824c06356bSdh */ 4983c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, 4984c3bc407cSdh "%s: abort result 0x%x", __func__, LE_32(msg[2])); 49854c06356bSdh if (all_cmds) { 49864c06356bSdh PHY_CHANGED(pwp, pptr); 49874c06356bSdh RESTART_DISCOVERY(pwp); 49884c06356bSdh } else { 49894c06356bSdh return (EINVAL); 49904c06356bSdh } 49914c06356bSdh 49924c06356bSdh return (0); 49934c06356bSdh } 49944c06356bSdh 49954c06356bSdh if (tgt != NULL) { 49964c06356bSdh mutex_enter(&tgt->statlock); 49974c06356bSdh if (tgt->dev_state == PMCS_DEVICE_STATE_IN_RECOVERY) { 4998c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, 49994c06356bSdh "%s: Restoring OPERATIONAL dev_state for tgt 0x%p", 50004c06356bSdh __func__, (void *)tgt); 50014c06356bSdh (void) pmcs_send_err_recovery_cmd(pwp, 5002145e0143Sdh PMCS_DEVICE_STATE_OPERATIONAL, pptr, tgt); 50034c06356bSdh } 50044c06356bSdh mutex_exit(&tgt->statlock); 50054c06356bSdh } 50064c06356bSdh 50074c06356bSdh return (0); 50084c06356bSdh } 50094c06356bSdh 50104c06356bSdh /* 50114c06356bSdh * Issue a task management function to an SSP device. 50124c06356bSdh * 50134c06356bSdh * Called with PHY lock held. 50144c06356bSdh * statlock CANNOT be held upon entry. 50154c06356bSdh */ 50164c06356bSdh int 50174c06356bSdh pmcs_ssp_tmf(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint8_t tmf, uint32_t tag, 50184c06356bSdh uint64_t lun, uint32_t *response) 50194c06356bSdh { 50204c06356bSdh int result, ds; 50214c06356bSdh uint8_t local[PMCS_QENTRY_SIZE << 1], *xd; 50224c06356bSdh sas_ssp_rsp_iu_t *rptr = (void *)local; 50234c06356bSdh static const uint8_t ssp_rsp_evec[] = { 50244c06356bSdh 0x58, 0x61, 0x56, 0x72, 0x00 50254c06356bSdh }; 50264c06356bSdh uint32_t msg[PMCS_MSG_SIZE], *ptr, status; 50274c06356bSdh struct pmcwork *pwrk; 50284c06356bSdh pmcs_xscsi_t *xp; 50294c06356bSdh 50304c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 50314c06356bSdh if (pwrk == NULL) { 5032c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__); 50334c06356bSdh return (ENOMEM); 50344c06356bSdh } 50354c06356bSdh /* 50364c06356bSdh * NB: We use the PMCS_OQ_GENERAL outbound queue 50374c06356bSdh * NB: so as to not get entangled in normal I/O 50384c06356bSdh * NB: processing. 50394c06356bSdh */ 50404c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 50414c06356bSdh PMCIN_SSP_INI_TM_START)); 50424c06356bSdh msg[1] = LE_32(pwrk->htag); 50434c06356bSdh msg[2] = LE_32(pptr->device_id); 50444c06356bSdh if (tmf == SAS_ABORT_TASK || tmf == SAS_QUERY_TASK) { 50454c06356bSdh msg[3] = LE_32(tag); 50464c06356bSdh } else { 50474c06356bSdh msg[3] = 0; 50484c06356bSdh } 50494c06356bSdh msg[4] = LE_32(tmf); 50504c06356bSdh msg[5] = BE_32((uint32_t)lun); 50514c06356bSdh msg[6] = BE_32((uint32_t)(lun >> 32)); 50524c06356bSdh msg[7] = LE_32(PMCIN_MESSAGE_REPORT); 50534c06356bSdh 50544c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 50554c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 50564c06356bSdh if (ptr == NULL) { 50574c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 50584c06356bSdh pmcs_pwork(pwp, pwrk); 5059c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__); 50604c06356bSdh return (ENOMEM); 50614c06356bSdh } 50624c06356bSdh COPY_MESSAGE(ptr, msg, 7); 50634c06356bSdh pwrk->arg = msg; 50644c06356bSdh pwrk->dtype = pptr->dtype; 50654c06356bSdh xp = pptr->target; 506656976565SDavid Hollister pwrk->xp = xp; 506756976565SDavid Hollister 50684c06356bSdh if (xp != NULL) { 50694c06356bSdh mutex_enter(&xp->statlock); 50704c06356bSdh if (xp->dev_state == PMCS_DEVICE_STATE_NON_OPERATIONAL) { 50714c06356bSdh mutex_exit(&xp->statlock); 50724c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 50734c06356bSdh pmcs_pwork(pwp, pwrk); 5074c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, "%s: Not " 5075c3bc407cSdh "sending '%s' because DS is '%s'", __func__, 5076c3bc407cSdh pmcs_tmf2str(tmf), pmcs_status_str 50774c06356bSdh (PMCOUT_STATUS_IO_DS_NON_OPERATIONAL)); 50784c06356bSdh return (EIO); 50794c06356bSdh } 50804c06356bSdh mutex_exit(&xp->statlock); 50814c06356bSdh } 50824c06356bSdh 5083c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 50844c06356bSdh "%s: sending '%s' to %s (lun %llu) tag 0x%x", __func__, 50854c06356bSdh pmcs_tmf2str(tmf), pptr->path, (unsigned long long) lun, tag); 50864c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 50874c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 50884c06356bSdh 50894c06356bSdh pmcs_unlock_phy(pptr); 50904c06356bSdh /* 50914c06356bSdh * This is a command sent to the target device, so it can take 50924c06356bSdh * significant amount of time to complete when path & device is busy. 50934c06356bSdh * Set a timeout to 20 seconds 50944c06356bSdh */ 50954c06356bSdh WAIT_FOR(pwrk, 20000, result); 50964c06356bSdh pmcs_lock_phy(pptr); 50974c06356bSdh pmcs_pwork(pwp, pwrk); 50984c06356bSdh 50994c06356bSdh if (result) { 51004c06356bSdh if (xp == NULL) { 51014c06356bSdh return (ETIMEDOUT); 51024c06356bSdh } 51034c06356bSdh 51044c06356bSdh mutex_enter(&xp->statlock); 51054c06356bSdh pmcs_start_dev_state_recovery(xp, pptr); 51064c06356bSdh mutex_exit(&xp->statlock); 51074c06356bSdh return (ETIMEDOUT); 51084c06356bSdh } 51094c06356bSdh 51104c06356bSdh status = LE_32(msg[2]); 51114c06356bSdh if (status != PMCOUT_STATUS_OK) { 5112c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 51134c06356bSdh "%s: status %s for TMF %s action to %s, lun %llu", 51144c06356bSdh __func__, pmcs_status_str(status), pmcs_tmf2str(tmf), 51154c06356bSdh pptr->path, (unsigned long long) lun); 51164c06356bSdh if ((status == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) || 51174c06356bSdh (status == PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK) || 51184c06356bSdh (status == PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS)) { 51194c06356bSdh ds = PMCS_DEVICE_STATE_NON_OPERATIONAL; 51204c06356bSdh } else if (status == PMCOUT_STATUS_IO_DS_IN_RECOVERY) { 51214c06356bSdh /* 51224c06356bSdh * If the status is IN_RECOVERY, it's an indication 51234c06356bSdh * that it's now time for us to request to have the 51244c06356bSdh * device state set to OPERATIONAL since we're the ones 51254c06356bSdh * that requested recovery to begin with. 51264c06356bSdh */ 51274c06356bSdh ds = PMCS_DEVICE_STATE_OPERATIONAL; 51284c06356bSdh } else { 51294c06356bSdh ds = PMCS_DEVICE_STATE_IN_RECOVERY; 51304c06356bSdh } 51314c06356bSdh if (xp != NULL) { 51324c06356bSdh mutex_enter(&xp->statlock); 51334c06356bSdh if (xp->dev_state != ds) { 5134c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 51354c06356bSdh "%s: Sending err recovery cmd" 51364c06356bSdh " for tgt 0x%p (status = %s)", 51374c06356bSdh __func__, (void *)xp, 51384c06356bSdh pmcs_status_str(status)); 5139145e0143Sdh (void) pmcs_send_err_recovery_cmd(pwp, ds, 5140145e0143Sdh pptr, xp); 51414c06356bSdh } 51424c06356bSdh mutex_exit(&xp->statlock); 51434c06356bSdh } 51444c06356bSdh return (EIO); 51454c06356bSdh } else { 51464c06356bSdh ds = PMCS_DEVICE_STATE_OPERATIONAL; 51474c06356bSdh if (xp != NULL) { 51484c06356bSdh mutex_enter(&xp->statlock); 51494c06356bSdh if (xp->dev_state != ds) { 5150c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 51514c06356bSdh "%s: Sending err recovery cmd" 51524c06356bSdh " for tgt 0x%p (status = %s)", 51534c06356bSdh __func__, (void *)xp, 51544c06356bSdh pmcs_status_str(status)); 5155145e0143Sdh (void) pmcs_send_err_recovery_cmd(pwp, ds, 5156145e0143Sdh pptr, xp); 51574c06356bSdh } 51584c06356bSdh mutex_exit(&xp->statlock); 51594c06356bSdh } 51604c06356bSdh } 51614c06356bSdh if (LE_32(msg[3]) == 0) { 5162c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 5163c3bc407cSdh "TMF completed with no response"); 51644c06356bSdh return (EIO); 51654c06356bSdh } 51664c06356bSdh pmcs_endian_transform(pwp, local, &msg[5], ssp_rsp_evec); 51674c06356bSdh xd = (uint8_t *)(&msg[5]); 51684c06356bSdh xd += SAS_RSP_HDR_SIZE; 51694c06356bSdh if (rptr->datapres != SAS_RSP_DATAPRES_RESPONSE_DATA) { 5170c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 51714c06356bSdh "%s: TMF response not RESPONSE DATA (0x%x)", 51724c06356bSdh __func__, rptr->datapres); 51734c06356bSdh return (EIO); 51744c06356bSdh } 51754c06356bSdh if (rptr->response_data_length != 4) { 51764c06356bSdh pmcs_print_entry(pwp, PMCS_PRT_DEBUG, 51774c06356bSdh "Bad SAS RESPONSE DATA LENGTH", msg); 51784c06356bSdh return (EIO); 51794c06356bSdh } 51804c06356bSdh (void) memcpy(&status, xd, sizeof (uint32_t)); 51814c06356bSdh status = BE_32(status); 51824c06356bSdh if (response != NULL) 51834c06356bSdh *response = status; 51844c06356bSdh /* 51854c06356bSdh * The status is actually in the low-order byte. The upper three 51864c06356bSdh * bytes contain additional information for the TMFs that support them. 51874c06356bSdh * However, at this time we do not issue any of those. In the other 51884c06356bSdh * cases, the upper three bytes are supposed to be 0, but it appears 51894c06356bSdh * they aren't always. Just mask them off. 51904c06356bSdh */ 51914c06356bSdh switch (status & 0xff) { 51924c06356bSdh case SAS_RSP_TMF_COMPLETE: 5193c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 5194c3bc407cSdh "%s: TMF complete", __func__); 51954c06356bSdh result = 0; 51964c06356bSdh break; 51974c06356bSdh case SAS_RSP_TMF_SUCCEEDED: 5198c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 5199c3bc407cSdh "%s: TMF succeeded", __func__); 52004c06356bSdh result = 0; 52014c06356bSdh break; 52024c06356bSdh case SAS_RSP_INVALID_FRAME: 5203c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 52044c06356bSdh "%s: TMF returned INVALID FRAME", __func__); 52054c06356bSdh result = EIO; 52064c06356bSdh break; 52074c06356bSdh case SAS_RSP_TMF_NOT_SUPPORTED: 5208c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 52094c06356bSdh "%s: TMF returned TMF NOT SUPPORTED", __func__); 52104c06356bSdh result = EIO; 52114c06356bSdh break; 52124c06356bSdh case SAS_RSP_TMF_FAILED: 5213c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 52144c06356bSdh "%s: TMF returned TMF FAILED", __func__); 52154c06356bSdh result = EIO; 52164c06356bSdh break; 52174c06356bSdh case SAS_RSP_TMF_INCORRECT_LUN: 5218c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 52194c06356bSdh "%s: TMF returned INCORRECT LUN", __func__); 52204c06356bSdh result = EIO; 52214c06356bSdh break; 52224c06356bSdh case SAS_RSP_OVERLAPPED_OIPTTA: 5223c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 52244c06356bSdh "%s: TMF returned OVERLAPPED INITIATOR PORT TRANSFER TAG " 52254c06356bSdh "ATTEMPTED", __func__); 52264c06356bSdh result = EIO; 52274c06356bSdh break; 52284c06356bSdh default: 5229c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 52304c06356bSdh "%s: TMF returned unknown code 0x%x", __func__, status); 52314c06356bSdh result = EIO; 52324c06356bSdh break; 52334c06356bSdh } 52344c06356bSdh return (result); 52354c06356bSdh } 52364c06356bSdh 52374c06356bSdh /* 52384c06356bSdh * Called with PHY lock held and scratch acquired 52394c06356bSdh */ 52404c06356bSdh int 52414c06356bSdh pmcs_sata_abort_ncq(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 52424c06356bSdh { 52434c06356bSdh const char *utag_fail_fmt = "%s: untagged NCQ command failure"; 52444c06356bSdh const char *tag_fail_fmt = "%s: NCQ command failure (tag 0x%x)"; 52454c06356bSdh uint32_t msg[PMCS_QENTRY_SIZE], *ptr, result, status; 52464c06356bSdh uint8_t *fp = pwp->scratch, ds; 52474c06356bSdh fis_t fis; 52484c06356bSdh pmcwork_t *pwrk; 52494c06356bSdh pmcs_xscsi_t *tgt; 52504c06356bSdh 52514c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 52524c06356bSdh if (pwrk == NULL) { 52534c06356bSdh return (ENOMEM); 52544c06356bSdh } 52554c06356bSdh msg[0] = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE, 52564c06356bSdh PMCIN_SATA_HOST_IO_START)); 52574c06356bSdh msg[1] = LE_32(pwrk->htag); 52584c06356bSdh msg[2] = LE_32(pptr->device_id); 52594c06356bSdh msg[3] = LE_32(512); 52604c06356bSdh msg[4] = LE_32(SATA_PROTOCOL_PIO | PMCIN_DATADIR_2_INI); 52614c06356bSdh msg[5] = LE_32((READ_LOG_EXT << 16) | (C_BIT << 8) | FIS_REG_H2DEV); 52624c06356bSdh msg[6] = LE_32(0x10); 52634c06356bSdh msg[8] = LE_32(1); 52644c06356bSdh msg[9] = 0; 52654c06356bSdh msg[10] = 0; 52664c06356bSdh msg[11] = 0; 52674c06356bSdh msg[12] = LE_32(DWORD0(pwp->scratch_dma)); 52684c06356bSdh msg[13] = LE_32(DWORD1(pwp->scratch_dma)); 52694c06356bSdh msg[14] = LE_32(512); 52704c06356bSdh msg[15] = 0; 52714c06356bSdh 52724c06356bSdh pwrk->arg = msg; 52734c06356bSdh pwrk->dtype = pptr->dtype; 52744c06356bSdh 52754c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 52764c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 52774c06356bSdh if (ptr == NULL) { 52784c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 52794c06356bSdh pmcs_pwork(pwp, pwrk); 52804c06356bSdh return (ENOMEM); 52814c06356bSdh } 52824c06356bSdh COPY_MESSAGE(ptr, msg, PMCS_QENTRY_SIZE); 52834c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 52844c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 52854c06356bSdh 52864c06356bSdh pmcs_unlock_phy(pptr); 52874c06356bSdh WAIT_FOR(pwrk, 250, result); 52884c06356bSdh pmcs_lock_phy(pptr); 52894c06356bSdh pmcs_pwork(pwp, pwrk); 52904c06356bSdh 5291c3bc407cSdh tgt = pptr->target; 52924c06356bSdh if (result) { 52936745c559SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, pmcs_timeo, __func__); 52944c06356bSdh return (EIO); 52954c06356bSdh } 52964c06356bSdh status = LE_32(msg[2]); 52974c06356bSdh if (status != PMCOUT_STATUS_OK || LE_32(msg[3])) { 52984c06356bSdh if (tgt == NULL) { 5299c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, 53004c06356bSdh "%s: cannot find target for phy 0x%p for " 53014c06356bSdh "dev state recovery", __func__, (void *)pptr); 53024c06356bSdh return (EIO); 53034c06356bSdh } 53044c06356bSdh 53054c06356bSdh mutex_enter(&tgt->statlock); 53064c06356bSdh 53074c06356bSdh pmcs_print_entry(pwp, PMCS_PRT_DEBUG, "READ LOG EXT", msg); 53084c06356bSdh if ((status == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) || 53094c06356bSdh (status == PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK) || 53104c06356bSdh (status == PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS)) { 53114c06356bSdh ds = PMCS_DEVICE_STATE_NON_OPERATIONAL; 53124c06356bSdh } else { 53134c06356bSdh ds = PMCS_DEVICE_STATE_IN_RECOVERY; 53144c06356bSdh } 53154c06356bSdh if (tgt->dev_state != ds) { 5316c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, "%s: Trying " 5317c3bc407cSdh "SATA DS Recovery for tgt(0x%p) for status(%s)", 53184c06356bSdh __func__, (void *)tgt, pmcs_status_str(status)); 5319145e0143Sdh (void) pmcs_send_err_recovery_cmd(pwp, ds, pptr, tgt); 53204c06356bSdh } 53214c06356bSdh 53224c06356bSdh mutex_exit(&tgt->statlock); 53234c06356bSdh return (EIO); 53244c06356bSdh } 53254c06356bSdh fis[0] = (fp[4] << 24) | (fp[3] << 16) | (fp[2] << 8) | FIS_REG_D2H; 53264c06356bSdh fis[1] = (fp[8] << 24) | (fp[7] << 16) | (fp[6] << 8) | fp[5]; 53274c06356bSdh fis[2] = (fp[12] << 24) | (fp[11] << 16) | (fp[10] << 8) | fp[9]; 53284c06356bSdh fis[3] = (fp[16] << 24) | (fp[15] << 16) | (fp[14] << 8) | fp[13]; 53294c06356bSdh fis[4] = 0; 53304c06356bSdh if (fp[0] & 0x80) { 5331c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, 5332c3bc407cSdh utag_fail_fmt, __func__); 53334c06356bSdh } else { 5334c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, 5335c3bc407cSdh tag_fail_fmt, __func__, fp[0] & 0x1f); 53364c06356bSdh } 53374c06356bSdh pmcs_fis_dump(pwp, fis); 53384c06356bSdh pptr->need_rl_ext = 0; 53394c06356bSdh return (0); 53404c06356bSdh } 53414c06356bSdh 53424c06356bSdh /* 53434c06356bSdh * Transform a structure from CPU to Device endian format, or 53444c06356bSdh * vice versa, based upon a transformation vector. 53454c06356bSdh * 53464c06356bSdh * A transformation vector is an array of bytes, each byte 53474c06356bSdh * of which is defined thusly: 53484c06356bSdh * 53494c06356bSdh * bit 7: from CPU to desired endian, otherwise from desired endian 53504c06356bSdh * to CPU format 53514c06356bSdh * bit 6: Big Endian, else Little Endian 53524c06356bSdh * bits 5-4: 53534c06356bSdh * 00 Undefined 53544c06356bSdh * 01 One Byte quantities 53554c06356bSdh * 02 Two Byte quantities 53564c06356bSdh * 03 Four Byte quantities 53574c06356bSdh * 53584c06356bSdh * bits 3-0: 53594c06356bSdh * 00 Undefined 53604c06356bSdh * Number of quantities to transform 53614c06356bSdh * 53624c06356bSdh * The vector is terminated by a 0 value. 53634c06356bSdh */ 53644c06356bSdh 53654c06356bSdh void 53664c06356bSdh pmcs_endian_transform(pmcs_hw_t *pwp, void *orig_out, void *orig_in, 53674c06356bSdh const uint8_t *xfvec) 53684c06356bSdh { 53694c06356bSdh uint8_t c, *out = orig_out, *in = orig_in; 53704c06356bSdh 53714c06356bSdh if (xfvec == NULL) { 5372c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 5373c3bc407cSdh "%s: null xfvec", __func__); 53744c06356bSdh return; 53754c06356bSdh } 53764c06356bSdh if (out == NULL) { 5377c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 5378c3bc407cSdh "%s: null out", __func__); 53794c06356bSdh return; 53804c06356bSdh } 53814c06356bSdh if (in == NULL) { 5382c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 5383c3bc407cSdh "%s: null in", __func__); 53844c06356bSdh return; 53854c06356bSdh } 53864c06356bSdh while ((c = *xfvec++) != 0) { 53874c06356bSdh int nbyt = (c & 0xf); 53884c06356bSdh int size = (c >> 4) & 0x3; 53894c06356bSdh int bige = (c >> 4) & 0x4; 53904c06356bSdh 53914c06356bSdh switch (size) { 53924c06356bSdh case 1: 53934c06356bSdh { 53944c06356bSdh while (nbyt-- > 0) { 53954c06356bSdh *out++ = *in++; 53964c06356bSdh } 53974c06356bSdh break; 53984c06356bSdh } 53994c06356bSdh case 2: 54004c06356bSdh { 54014c06356bSdh uint16_t tmp; 54024c06356bSdh while (nbyt-- > 0) { 54034c06356bSdh (void) memcpy(&tmp, in, sizeof (uint16_t)); 54044c06356bSdh if (bige) { 54054c06356bSdh tmp = BE_16(tmp); 54064c06356bSdh } else { 54074c06356bSdh tmp = LE_16(tmp); 54084c06356bSdh } 54094c06356bSdh (void) memcpy(out, &tmp, sizeof (uint16_t)); 54104c06356bSdh out += sizeof (uint16_t); 54114c06356bSdh in += sizeof (uint16_t); 54124c06356bSdh } 54134c06356bSdh break; 54144c06356bSdh } 54154c06356bSdh case 3: 54164c06356bSdh { 54174c06356bSdh uint32_t tmp; 54184c06356bSdh while (nbyt-- > 0) { 54194c06356bSdh (void) memcpy(&tmp, in, sizeof (uint32_t)); 54204c06356bSdh if (bige) { 54214c06356bSdh tmp = BE_32(tmp); 54224c06356bSdh } else { 54234c06356bSdh tmp = LE_32(tmp); 54244c06356bSdh } 54254c06356bSdh (void) memcpy(out, &tmp, sizeof (uint32_t)); 54264c06356bSdh out += sizeof (uint32_t); 54274c06356bSdh in += sizeof (uint32_t); 54284c06356bSdh } 54294c06356bSdh break; 54304c06356bSdh } 54314c06356bSdh default: 5432c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 5433c3bc407cSdh "%s: bad size", __func__); 54344c06356bSdh return; 54354c06356bSdh } 54364c06356bSdh } 54374c06356bSdh } 54384c06356bSdh 54394c06356bSdh const char * 54404c06356bSdh pmcs_get_rate(unsigned int linkrt) 54414c06356bSdh { 54424c06356bSdh const char *rate; 54434c06356bSdh switch (linkrt) { 54444c06356bSdh case SAS_LINK_RATE_1_5GBIT: 54454c06356bSdh rate = "1.5"; 54464c06356bSdh break; 54474c06356bSdh case SAS_LINK_RATE_3GBIT: 54484c06356bSdh rate = "3.0"; 54494c06356bSdh break; 54504c06356bSdh case SAS_LINK_RATE_6GBIT: 54514c06356bSdh rate = "6.0"; 54524c06356bSdh break; 54534c06356bSdh default: 54544c06356bSdh rate = "???"; 54554c06356bSdh break; 54564c06356bSdh } 54574c06356bSdh return (rate); 54584c06356bSdh } 54594c06356bSdh 54604c06356bSdh const char * 54614c06356bSdh pmcs_get_typename(pmcs_dtype_t type) 54624c06356bSdh { 54634c06356bSdh switch (type) { 54644c06356bSdh case NOTHING: 54654c06356bSdh return ("NIL"); 54664c06356bSdh case SATA: 54674c06356bSdh return ("SATA"); 54684c06356bSdh case SAS: 54694c06356bSdh return ("SSP"); 54704c06356bSdh case EXPANDER: 54714c06356bSdh return ("EXPANDER"); 54724c06356bSdh } 54734c06356bSdh return ("????"); 54744c06356bSdh } 54754c06356bSdh 54764c06356bSdh const char * 54774c06356bSdh pmcs_tmf2str(int tmf) 54784c06356bSdh { 54794c06356bSdh switch (tmf) { 54804c06356bSdh case SAS_ABORT_TASK: 54814c06356bSdh return ("Abort Task"); 54824c06356bSdh case SAS_ABORT_TASK_SET: 54834c06356bSdh return ("Abort Task Set"); 54844c06356bSdh case SAS_CLEAR_TASK_SET: 54854c06356bSdh return ("Clear Task Set"); 54864c06356bSdh case SAS_LOGICAL_UNIT_RESET: 54874c06356bSdh return ("Logical Unit Reset"); 54884c06356bSdh case SAS_I_T_NEXUS_RESET: 54894c06356bSdh return ("I_T Nexus Reset"); 54904c06356bSdh case SAS_CLEAR_ACA: 54914c06356bSdh return ("Clear ACA"); 54924c06356bSdh case SAS_QUERY_TASK: 54934c06356bSdh return ("Query Task"); 54944c06356bSdh case SAS_QUERY_TASK_SET: 54954c06356bSdh return ("Query Task Set"); 54964c06356bSdh case SAS_QUERY_UNIT_ATTENTION: 54974c06356bSdh return ("Query Unit Attention"); 54984c06356bSdh default: 54994c06356bSdh return ("Unknown"); 55004c06356bSdh } 55014c06356bSdh } 55024c06356bSdh 55034c06356bSdh const char * 55044c06356bSdh pmcs_status_str(uint32_t status) 55054c06356bSdh { 55064c06356bSdh switch (status) { 55074c06356bSdh case PMCOUT_STATUS_OK: 55084c06356bSdh return ("OK"); 55094c06356bSdh case PMCOUT_STATUS_ABORTED: 55104c06356bSdh return ("ABORTED"); 55114c06356bSdh case PMCOUT_STATUS_OVERFLOW: 55124c06356bSdh return ("OVERFLOW"); 55134c06356bSdh case PMCOUT_STATUS_UNDERFLOW: 55144c06356bSdh return ("UNDERFLOW"); 55154c06356bSdh case PMCOUT_STATUS_FAILED: 55164c06356bSdh return ("FAILED"); 55174c06356bSdh case PMCOUT_STATUS_ABORT_RESET: 55184c06356bSdh return ("ABORT_RESET"); 55194c06356bSdh case PMCOUT_STATUS_IO_NOT_VALID: 55204c06356bSdh return ("IO_NOT_VALID"); 55214c06356bSdh case PMCOUT_STATUS_NO_DEVICE: 55224c06356bSdh return ("NO_DEVICE"); 55234c06356bSdh case PMCOUT_STATUS_ILLEGAL_PARAMETER: 55244c06356bSdh return ("ILLEGAL_PARAMETER"); 55254c06356bSdh case PMCOUT_STATUS_LINK_FAILURE: 55264c06356bSdh return ("LINK_FAILURE"); 55274c06356bSdh case PMCOUT_STATUS_PROG_ERROR: 55284c06356bSdh return ("PROG_ERROR"); 55294c06356bSdh case PMCOUT_STATUS_EDC_IN_ERROR: 55304c06356bSdh return ("EDC_IN_ERROR"); 55314c06356bSdh case PMCOUT_STATUS_EDC_OUT_ERROR: 55324c06356bSdh return ("EDC_OUT_ERROR"); 55334c06356bSdh case PMCOUT_STATUS_ERROR_HW_TIMEOUT: 55344c06356bSdh return ("ERROR_HW_TIMEOUT"); 55354c06356bSdh case PMCOUT_STATUS_XFER_ERR_BREAK: 55364c06356bSdh return ("XFER_ERR_BREAK"); 55374c06356bSdh case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY: 55384c06356bSdh return ("XFER_ERR_PHY_NOT_READY"); 55394c06356bSdh case PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED: 55404c06356bSdh return ("OPEN_CNX_PROTOCOL_NOT_SUPPORTED"); 55414c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION: 55424c06356bSdh return ("OPEN_CNX_ERROR_ZONE_VIOLATION"); 55434c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK: 55444c06356bSdh return ("OPEN_CNX_ERROR_BREAK"); 55454c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS: 55464c06356bSdh return ("OPEN_CNX_ERROR_IT_NEXUS_LOSS"); 55474c06356bSdh case PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION: 55484c06356bSdh return ("OPENCNX_ERROR_BAD_DESTINATION"); 55494c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: 55504c06356bSdh return ("OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED"); 55514c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY: 55524c06356bSdh return ("OPEN_CNX_ERROR_STP_RESOURCES_BUSY"); 55534c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION: 55544c06356bSdh return ("OPEN_CNX_ERROR_WRONG_DESTINATION"); 55559aed1621SDavid Hollister case PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_ERROR: 55569aed1621SDavid Hollister return ("OPEN_CNX_ERROR_UNKNOWN_ERROR"); 55574c06356bSdh case PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED: 55584c06356bSdh return ("IO_XFER_ERROR_NAK_RECEIVED"); 55594c06356bSdh case PMCOUT_STATUS_XFER_ERROR_ACK_NAK_TIMEOUT: 55604c06356bSdh return ("XFER_ERROR_ACK_NAK_TIMEOUT"); 55614c06356bSdh case PMCOUT_STATUS_XFER_ERROR_PEER_ABORTED: 55624c06356bSdh return ("XFER_ERROR_PEER_ABORTED"); 55634c06356bSdh case PMCOUT_STATUS_XFER_ERROR_RX_FRAME: 55644c06356bSdh return ("XFER_ERROR_RX_FRAME"); 55654c06356bSdh case PMCOUT_STATUS_IO_XFER_ERROR_DMA: 55664c06356bSdh return ("IO_XFER_ERROR_DMA"); 55674c06356bSdh case PMCOUT_STATUS_XFER_ERROR_CREDIT_TIMEOUT: 55684c06356bSdh return ("XFER_ERROR_CREDIT_TIMEOUT"); 55694c06356bSdh case PMCOUT_STATUS_XFER_ERROR_SATA_LINK_TIMEOUT: 55704c06356bSdh return ("XFER_ERROR_SATA_LINK_TIMEOUT"); 55714c06356bSdh case PMCOUT_STATUS_XFER_ERROR_SATA: 55724c06356bSdh return ("XFER_ERROR_SATA"); 55734c06356bSdh case PMCOUT_STATUS_XFER_ERROR_REJECTED_NCQ_MODE: 55744c06356bSdh return ("XFER_ERROR_REJECTED_NCQ_MODE"); 55754c06356bSdh case PMCOUT_STATUS_XFER_ERROR_ABORTED_DUE_TO_SRST: 55764c06356bSdh return ("XFER_ERROR_ABORTED_DUE_TO_SRST"); 55774c06356bSdh case PMCOUT_STATUS_XFER_ERROR_ABORTED_NCQ_MODE: 55784c06356bSdh return ("XFER_ERROR_ABORTED_NCQ_MODE"); 55794c06356bSdh case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT: 55804c06356bSdh return ("IO_XFER_OPEN_RETRY_TIMEOUT"); 55814c06356bSdh case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR: 55824c06356bSdh return ("SMP_RESP_CONNECTION_ERROR"); 55834c06356bSdh case PMCOUT_STATUS_XFER_ERROR_UNEXPECTED_PHASE: 55844c06356bSdh return ("XFER_ERROR_UNEXPECTED_PHASE"); 55854c06356bSdh case PMCOUT_STATUS_XFER_ERROR_RDY_OVERRUN: 55864c06356bSdh return ("XFER_ERROR_RDY_OVERRUN"); 55874c06356bSdh case PMCOUT_STATUS_XFER_ERROR_RDY_NOT_EXPECTED: 55884c06356bSdh return ("XFER_ERROR_RDY_NOT_EXPECTED"); 55894c06356bSdh case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT: 55904c06356bSdh return ("XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT"); 55914c06356bSdh case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK: 55924c06356bSdh return ("XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK"); 55934c06356bSdh case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK: 55944c06356bSdh return ("XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK"); 55954c06356bSdh case PMCOUT_STATUS_XFER_ERROR_OFFSET_MISMATCH: 55964c06356bSdh return ("XFER_ERROR_OFFSET_MISMATCH"); 55974c06356bSdh case PMCOUT_STATUS_XFER_ERROR_ZERO_DATA_LEN: 55984c06356bSdh return ("XFER_ERROR_ZERO_DATA_LEN"); 55994c06356bSdh case PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED: 56004c06356bSdh return ("XFER_CMD_FRAME_ISSUED"); 56014c06356bSdh case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE: 56024c06356bSdh return ("ERROR_INTERNAL_SMP_RESOURCE"); 56034c06356bSdh case PMCOUT_STATUS_IO_PORT_IN_RESET: 56044c06356bSdh return ("IO_PORT_IN_RESET"); 56054c06356bSdh case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL: 56064c06356bSdh return ("DEVICE STATE NON-OPERATIONAL"); 56074c06356bSdh case PMCOUT_STATUS_IO_DS_IN_RECOVERY: 56084c06356bSdh return ("DEVICE STATE IN RECOVERY"); 5609a25672a1SDavid Hollister case PMCOUT_STATUS_IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY: 5610a25672a1SDavid Hollister return ("OPEN CNX ERR HW RESOURCE BUSY"); 56114c06356bSdh default: 56124c06356bSdh return (NULL); 56134c06356bSdh } 56144c06356bSdh } 56154c06356bSdh 56164c06356bSdh uint64_t 56174c06356bSdh pmcs_barray2wwn(uint8_t ba[8]) 56184c06356bSdh { 56194c06356bSdh uint64_t result = 0; 56204c06356bSdh int i; 56214c06356bSdh 56224c06356bSdh for (i = 0; i < 8; i++) { 56234c06356bSdh result <<= 8; 56244c06356bSdh result |= ba[i]; 56254c06356bSdh } 56264c06356bSdh return (result); 56274c06356bSdh } 56284c06356bSdh 56294c06356bSdh void 56304c06356bSdh pmcs_wwn2barray(uint64_t wwn, uint8_t ba[8]) 56314c06356bSdh { 56324c06356bSdh int i; 56334c06356bSdh for (i = 0; i < 8; i++) { 56344c06356bSdh ba[7 - i] = wwn & 0xff; 56354c06356bSdh wwn >>= 8; 56364c06356bSdh } 56374c06356bSdh } 56384c06356bSdh 56394c06356bSdh void 56404c06356bSdh pmcs_report_fwversion(pmcs_hw_t *pwp) 56414c06356bSdh { 56424c06356bSdh const char *fwsupport; 56434c06356bSdh switch (PMCS_FW_TYPE(pwp)) { 56444c06356bSdh case PMCS_FW_TYPE_RELEASED: 56454c06356bSdh fwsupport = "Released"; 56464c06356bSdh break; 56474c06356bSdh case PMCS_FW_TYPE_DEVELOPMENT: 56484c06356bSdh fwsupport = "Development"; 56494c06356bSdh break; 56504c06356bSdh case PMCS_FW_TYPE_ALPHA: 56514c06356bSdh fwsupport = "Alpha"; 56524c06356bSdh break; 56534c06356bSdh case PMCS_FW_TYPE_BETA: 56544c06356bSdh fwsupport = "Beta"; 56554c06356bSdh break; 56564c06356bSdh default: 56574c06356bSdh fwsupport = "Special"; 56584c06356bSdh break; 56594c06356bSdh } 5660c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, 56612ac4abe8SDavid Hollister "Chip Revision: %c; F/W Revision %x.%x.%x %s (ILA rev %08x)", 56622ac4abe8SDavid Hollister 'A' + pwp->chiprev, PMCS_FW_MAJOR(pwp), PMCS_FW_MINOR(pwp), 56632ac4abe8SDavid Hollister PMCS_FW_MICRO(pwp), fwsupport, pwp->ila_ver); 56644c06356bSdh } 56654c06356bSdh 56664c06356bSdh void 56674c06356bSdh pmcs_phy_name(pmcs_hw_t *pwp, pmcs_phy_t *pptr, char *obuf, size_t olen) 56684c06356bSdh { 56694c06356bSdh if (pptr->parent) { 56704c06356bSdh pmcs_phy_name(pwp, pptr->parent, obuf, olen); 56714c06356bSdh (void) snprintf(obuf, olen, "%s.%02x", obuf, pptr->phynum); 56724c06356bSdh } else { 56734c06356bSdh (void) snprintf(obuf, olen, "pp%02x", pptr->phynum); 56744c06356bSdh } 56754c06356bSdh } 56764c06356bSdh 56774c06356bSdh /* 56784c06356bSdh * Implementation for pmcs_find_phy_by_devid. 56794c06356bSdh * If the PHY is found, it is returned locked. 56804c06356bSdh */ 56814c06356bSdh static pmcs_phy_t * 56824c06356bSdh pmcs_find_phy_by_devid_impl(pmcs_phy_t *phyp, uint32_t device_id) 56834c06356bSdh { 56844c06356bSdh pmcs_phy_t *match, *cphyp, *nphyp; 56854c06356bSdh 56864c06356bSdh ASSERT(!mutex_owned(&phyp->phy_lock)); 56874c06356bSdh 56884c06356bSdh while (phyp) { 56894c06356bSdh pmcs_lock_phy(phyp); 56904c06356bSdh 56914c06356bSdh if ((phyp->valid_device_id) && (phyp->device_id == device_id)) { 56924c06356bSdh return (phyp); 56934c06356bSdh } 56944c06356bSdh if (phyp->children) { 56954c06356bSdh cphyp = phyp->children; 56964c06356bSdh pmcs_unlock_phy(phyp); 56974c06356bSdh match = pmcs_find_phy_by_devid_impl(cphyp, device_id); 56984c06356bSdh if (match) { 56994c06356bSdh ASSERT(mutex_owned(&match->phy_lock)); 57004c06356bSdh return (match); 57014c06356bSdh } 57024c06356bSdh pmcs_lock_phy(phyp); 57034c06356bSdh } 57044c06356bSdh 57054c06356bSdh if (IS_ROOT_PHY(phyp)) { 57064c06356bSdh pmcs_unlock_phy(phyp); 57074c06356bSdh phyp = NULL; 57084c06356bSdh } else { 57094c06356bSdh nphyp = phyp->sibling; 57104c06356bSdh pmcs_unlock_phy(phyp); 57114c06356bSdh phyp = nphyp; 57124c06356bSdh } 57134c06356bSdh } 57144c06356bSdh 57154c06356bSdh return (NULL); 57164c06356bSdh } 57174c06356bSdh 57184c06356bSdh /* 57194c06356bSdh * If the PHY is found, it is returned locked 57204c06356bSdh */ 57214c06356bSdh pmcs_phy_t * 57224c06356bSdh pmcs_find_phy_by_devid(pmcs_hw_t *pwp, uint32_t device_id) 57234c06356bSdh { 57244c06356bSdh pmcs_phy_t *phyp, *match = NULL; 57254c06356bSdh 57264c06356bSdh phyp = pwp->root_phys; 57274c06356bSdh 57284c06356bSdh while (phyp) { 57294c06356bSdh match = pmcs_find_phy_by_devid_impl(phyp, device_id); 57304c06356bSdh if (match) { 57314c06356bSdh ASSERT(mutex_owned(&match->phy_lock)); 57324c06356bSdh return (match); 57334c06356bSdh } 57344c06356bSdh phyp = phyp->sibling; 57354c06356bSdh } 57364c06356bSdh 57374c06356bSdh return (NULL); 57384c06356bSdh } 57394c06356bSdh 57404c06356bSdh /* 57414c06356bSdh * This function is called as a sanity check to ensure that a newly registered 57424c06356bSdh * PHY doesn't have a device_id that exists with another registered PHY. 57434c06356bSdh */ 57444c06356bSdh static boolean_t 57454c06356bSdh pmcs_validate_devid(pmcs_phy_t *parent, pmcs_phy_t *phyp, uint32_t device_id) 57464c06356bSdh { 57479aed1621SDavid Hollister pmcs_phy_t *pptr, *pchild; 57484c06356bSdh boolean_t rval; 57494c06356bSdh 57504c06356bSdh pptr = parent; 57514c06356bSdh 57524c06356bSdh while (pptr) { 57534c06356bSdh if (pptr->valid_device_id && (pptr != phyp) && 57544c06356bSdh (pptr->device_id == device_id)) { 57559aed1621SDavid Hollister /* 57569aed1621SDavid Hollister * This can still be OK if both of these PHYs actually 57579aed1621SDavid Hollister * represent the same device (e.g. expander). It could 57589aed1621SDavid Hollister * be a case of a new "primary" PHY. If the SAS address 57599aed1621SDavid Hollister * is the same and they have the same parent, we'll 57609aed1621SDavid Hollister * accept this if the PHY to be registered is the 57619aed1621SDavid Hollister * primary. 57629aed1621SDavid Hollister */ 57639aed1621SDavid Hollister if ((phyp->parent == pptr->parent) && 57649aed1621SDavid Hollister (memcmp(phyp->sas_address, 57659aed1621SDavid Hollister pptr->sas_address, 8) == 0) && (phyp->width > 1)) { 57669aed1621SDavid Hollister /* 57679aed1621SDavid Hollister * Move children over to the new primary and 57689aed1621SDavid Hollister * update both PHYs 57699aed1621SDavid Hollister */ 57709aed1621SDavid Hollister pmcs_lock_phy(pptr); 57719aed1621SDavid Hollister phyp->children = pptr->children; 57729aed1621SDavid Hollister pchild = phyp->children; 57739aed1621SDavid Hollister while (pchild) { 57749aed1621SDavid Hollister pchild->parent = phyp; 57759aed1621SDavid Hollister pchild = pchild->sibling; 57769aed1621SDavid Hollister } 57779aed1621SDavid Hollister phyp->subsidiary = 0; 57789aed1621SDavid Hollister phyp->ncphy = pptr->ncphy; 57799aed1621SDavid Hollister /* 57809aed1621SDavid Hollister * device_id, valid_device_id, and configured 57819aed1621SDavid Hollister * will be set by the caller 57829aed1621SDavid Hollister */ 57839aed1621SDavid Hollister pptr->children = NULL; 57849aed1621SDavid Hollister pptr->subsidiary = 1; 57859aed1621SDavid Hollister pptr->ncphy = 0; 57869aed1621SDavid Hollister pmcs_unlock_phy(pptr); 57879aed1621SDavid Hollister pmcs_prt(pptr->pwp, PMCS_PRT_DEBUG, pptr, NULL, 57889aed1621SDavid Hollister "%s: Moving device_id %d from PHY %s to %s", 57899aed1621SDavid Hollister __func__, device_id, pptr->path, 57909aed1621SDavid Hollister phyp->path); 57919aed1621SDavid Hollister return (B_TRUE); 57929aed1621SDavid Hollister } 5793c3bc407cSdh pmcs_prt(pptr->pwp, PMCS_PRT_DEBUG, pptr, NULL, 57944c06356bSdh "%s: phy %s already exists as %s with " 57954c06356bSdh "device id 0x%x", __func__, phyp->path, 57964c06356bSdh pptr->path, device_id); 57974c06356bSdh return (B_FALSE); 57984c06356bSdh } 57994c06356bSdh 58004c06356bSdh if (pptr->children) { 58014c06356bSdh rval = pmcs_validate_devid(pptr->children, phyp, 58024c06356bSdh device_id); 58034c06356bSdh if (rval == B_FALSE) { 58044c06356bSdh return (rval); 58054c06356bSdh } 58064c06356bSdh } 58074c06356bSdh 58084c06356bSdh pptr = pptr->sibling; 58094c06356bSdh } 58104c06356bSdh 58114c06356bSdh /* This PHY and device_id are valid */ 58124c06356bSdh return (B_TRUE); 58134c06356bSdh } 58144c06356bSdh 58154c06356bSdh /* 58164c06356bSdh * If the PHY is found, it is returned locked 58174c06356bSdh */ 58184c06356bSdh static pmcs_phy_t * 58194c06356bSdh pmcs_find_phy_by_wwn_impl(pmcs_phy_t *phyp, uint8_t *wwn) 58204c06356bSdh { 58214c06356bSdh pmcs_phy_t *matched_phy, *cphyp, *nphyp; 58224c06356bSdh 58234c06356bSdh ASSERT(!mutex_owned(&phyp->phy_lock)); 58244c06356bSdh 58254c06356bSdh while (phyp) { 58264c06356bSdh pmcs_lock_phy(phyp); 58274c06356bSdh 58284c06356bSdh if (phyp->valid_device_id) { 58294c06356bSdh if (memcmp(phyp->sas_address, wwn, 8) == 0) { 58304c06356bSdh return (phyp); 58314c06356bSdh } 58324c06356bSdh } 58334c06356bSdh 58344c06356bSdh if (phyp->children) { 58354c06356bSdh cphyp = phyp->children; 58364c06356bSdh pmcs_unlock_phy(phyp); 58374c06356bSdh matched_phy = pmcs_find_phy_by_wwn_impl(cphyp, wwn); 58384c06356bSdh if (matched_phy) { 58394c06356bSdh ASSERT(mutex_owned(&matched_phy->phy_lock)); 58404c06356bSdh return (matched_phy); 58414c06356bSdh } 58424c06356bSdh pmcs_lock_phy(phyp); 58434c06356bSdh } 58444c06356bSdh 58454c06356bSdh /* 58464c06356bSdh * Only iterate through non-root PHYs 58474c06356bSdh */ 58484c06356bSdh if (IS_ROOT_PHY(phyp)) { 58494c06356bSdh pmcs_unlock_phy(phyp); 58504c06356bSdh phyp = NULL; 58514c06356bSdh } else { 58524c06356bSdh nphyp = phyp->sibling; 58534c06356bSdh pmcs_unlock_phy(phyp); 58544c06356bSdh phyp = nphyp; 58554c06356bSdh } 58564c06356bSdh } 58574c06356bSdh 58584c06356bSdh return (NULL); 58594c06356bSdh } 58604c06356bSdh 58614c06356bSdh pmcs_phy_t * 58624c06356bSdh pmcs_find_phy_by_wwn(pmcs_hw_t *pwp, uint64_t wwn) 58634c06356bSdh { 58644c06356bSdh uint8_t ebstr[8]; 58654c06356bSdh pmcs_phy_t *pptr, *matched_phy; 58664c06356bSdh 58674c06356bSdh pmcs_wwn2barray(wwn, ebstr); 58684c06356bSdh 58694c06356bSdh pptr = pwp->root_phys; 58704c06356bSdh while (pptr) { 58714c06356bSdh matched_phy = pmcs_find_phy_by_wwn_impl(pptr, ebstr); 58724c06356bSdh if (matched_phy) { 58734c06356bSdh ASSERT(mutex_owned(&matched_phy->phy_lock)); 58744c06356bSdh return (matched_phy); 58754c06356bSdh } 58764c06356bSdh 58774c06356bSdh pptr = pptr->sibling; 58784c06356bSdh } 58794c06356bSdh 58804c06356bSdh return (NULL); 58814c06356bSdh } 58824c06356bSdh 58834c06356bSdh 58844c06356bSdh /* 58854c06356bSdh * pmcs_find_phy_by_sas_address 58864c06356bSdh * 58874c06356bSdh * Find a PHY that both matches "sas_addr" and is on "iport". 58884c06356bSdh * If a matching PHY is found, it is returned locked. 58894c06356bSdh */ 58904c06356bSdh pmcs_phy_t * 58914c06356bSdh pmcs_find_phy_by_sas_address(pmcs_hw_t *pwp, pmcs_iport_t *iport, 58924c06356bSdh pmcs_phy_t *root, char *sas_addr) 58934c06356bSdh { 58944c06356bSdh int ua_form = 1; 58954c06356bSdh uint64_t wwn; 58964c06356bSdh char addr[PMCS_MAX_UA_SIZE]; 58974c06356bSdh pmcs_phy_t *pptr, *pnext, *pchild; 58984c06356bSdh 58994c06356bSdh if (root == NULL) { 59004c06356bSdh pptr = pwp->root_phys; 59014c06356bSdh } else { 59024c06356bSdh pptr = root; 59034c06356bSdh } 59044c06356bSdh 59054c06356bSdh while (pptr) { 59064c06356bSdh pmcs_lock_phy(pptr); 59074c06356bSdh /* 59084c06356bSdh * If the PHY is dead or does not have a valid device ID, 59094c06356bSdh * skip it. 59104c06356bSdh */ 59114c06356bSdh if ((pptr->dead) || (!pptr->valid_device_id)) { 59124c06356bSdh goto next_phy; 59134c06356bSdh } 59144c06356bSdh 59154c06356bSdh if (pptr->iport != iport) { 59164c06356bSdh goto next_phy; 59174c06356bSdh } 59184c06356bSdh 59194c06356bSdh wwn = pmcs_barray2wwn(pptr->sas_address); 59204c06356bSdh (void *) scsi_wwn_to_wwnstr(wwn, ua_form, addr); 59214c06356bSdh if (strncmp(addr, sas_addr, strlen(addr)) == 0) { 59224c06356bSdh return (pptr); 59234c06356bSdh } 59244c06356bSdh 59254c06356bSdh if (pptr->children) { 59264c06356bSdh pchild = pptr->children; 59274c06356bSdh pmcs_unlock_phy(pptr); 59284c06356bSdh pnext = pmcs_find_phy_by_sas_address(pwp, iport, pchild, 59294c06356bSdh sas_addr); 59304c06356bSdh if (pnext) { 59314c06356bSdh return (pnext); 59324c06356bSdh } 59334c06356bSdh pmcs_lock_phy(pptr); 59344c06356bSdh } 59354c06356bSdh 59364c06356bSdh next_phy: 59374c06356bSdh pnext = pptr->sibling; 59384c06356bSdh pmcs_unlock_phy(pptr); 59394c06356bSdh pptr = pnext; 59404c06356bSdh } 59414c06356bSdh 59424c06356bSdh return (NULL); 59434c06356bSdh } 59444c06356bSdh 59454c06356bSdh void 59464c06356bSdh pmcs_fis_dump(pmcs_hw_t *pwp, fis_t fis) 59474c06356bSdh { 59484c06356bSdh switch (fis[0] & 0xff) { 59494c06356bSdh case FIS_REG_H2DEV: 5950c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, 5951c3bc407cSdh "FIS REGISTER HOST TO DEVICE: " 59524c06356bSdh "OP=0x%02x Feature=0x%04x Count=0x%04x Device=0x%02x " 59534c06356bSdh "LBA=%llu", BYTE2(fis[0]), BYTE3(fis[2]) << 8 | 59544c06356bSdh BYTE3(fis[0]), WORD0(fis[3]), BYTE3(fis[1]), 59554c06356bSdh (unsigned long long) 59564c06356bSdh (((uint64_t)fis[2] & 0x00ffffff) << 24 | 59574c06356bSdh ((uint64_t)fis[1] & 0x00ffffff))); 59584c06356bSdh break; 59594c06356bSdh case FIS_REG_D2H: 5960c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, 5961c3bc407cSdh "FIS REGISTER DEVICE TO HOST: Status=0x%02x " 5962c3bc407cSdh "Error=0x%02x Dev=0x%02x Count=0x%04x LBA=%llu", 59634c06356bSdh BYTE2(fis[0]), BYTE3(fis[0]), BYTE3(fis[1]), WORD0(fis[3]), 59644c06356bSdh (unsigned long long)(((uint64_t)fis[2] & 0x00ffffff) << 24 | 59654c06356bSdh ((uint64_t)fis[1] & 0x00ffffff))); 59664c06356bSdh break; 59674c06356bSdh default: 5968c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, 5969a25672a1SDavid Hollister "FIS: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x", 5970a25672a1SDavid Hollister fis[0], fis[1], fis[2], fis[3], fis[4]); 59714c06356bSdh break; 59724c06356bSdh } 59734c06356bSdh } 59744c06356bSdh 59754c06356bSdh void 59764c06356bSdh pmcs_print_entry(pmcs_hw_t *pwp, int level, char *msg, void *arg) 59774c06356bSdh { 59784c06356bSdh uint32_t *mb = arg; 59794c06356bSdh size_t i; 59804c06356bSdh 5981c3bc407cSdh pmcs_prt(pwp, level, NULL, NULL, msg); 59824c06356bSdh for (i = 0; i < (PMCS_QENTRY_SIZE / sizeof (uint32_t)); i += 4) { 5983c3bc407cSdh pmcs_prt(pwp, level, NULL, NULL, 5984c3bc407cSdh "Offset %2lu: 0x%08x 0x%08x 0x%08x 0x%08x", 5985c3bc407cSdh i * sizeof (uint32_t), LE_32(mb[i]), 5986c3bc407cSdh LE_32(mb[i+1]), LE_32(mb[i+2]), LE_32(mb[i+3])); 59874c06356bSdh } 59884c06356bSdh } 59894c06356bSdh 59904c06356bSdh /* 59914c06356bSdh * If phyp == NULL we're being called from the worker thread, in which 59924c06356bSdh * case we need to check all the PHYs. In this case, the softstate lock 59934c06356bSdh * will be held. 59944c06356bSdh * If phyp is non-NULL, just issue the spinup release for the specified PHY 59954c06356bSdh * (which will already be locked). 59964c06356bSdh */ 59974c06356bSdh void 59984c06356bSdh pmcs_spinup_release(pmcs_hw_t *pwp, pmcs_phy_t *phyp) 59994c06356bSdh { 60004c06356bSdh uint32_t *msg; 60014c06356bSdh struct pmcwork *pwrk; 60024c06356bSdh pmcs_phy_t *tphyp; 60034c06356bSdh 60044c06356bSdh if (phyp != NULL) { 60054c06356bSdh ASSERT(mutex_owned(&phyp->phy_lock)); 6006c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, NULL, 60074c06356bSdh "%s: Issuing spinup release only for PHY %s", __func__, 60084c06356bSdh phyp->path); 60094c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 60104c06356bSdh msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 60114c06356bSdh if (msg == NULL || (pwrk = 60124c06356bSdh pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) { 60134c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 60144c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_SPINUP_RELEASE); 60154c06356bSdh return; 60164c06356bSdh } 60174c06356bSdh 60184c06356bSdh phyp->spinup_hold = 0; 60194c06356bSdh bzero(msg, PMCS_QENTRY_SIZE); 60204c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 60214c06356bSdh PMCIN_LOCAL_PHY_CONTROL)); 60224c06356bSdh msg[1] = LE_32(pwrk->htag); 60234c06356bSdh msg[2] = LE_32((0x10 << 8) | phyp->phynum); 60244c06356bSdh 60254c06356bSdh pwrk->dtype = phyp->dtype; 60264c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 60274c06356bSdh mutex_exit(&pwrk->lock); 60284c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 60294c06356bSdh return; 60304c06356bSdh } 60314c06356bSdh 60324c06356bSdh ASSERT(mutex_owned(&pwp->lock)); 60334c06356bSdh 60344c06356bSdh tphyp = pwp->root_phys; 60354c06356bSdh while (tphyp) { 60364c06356bSdh pmcs_lock_phy(tphyp); 60374c06356bSdh if (tphyp->spinup_hold == 0) { 60384c06356bSdh pmcs_unlock_phy(tphyp); 60394c06356bSdh tphyp = tphyp->sibling; 60404c06356bSdh continue; 60414c06356bSdh } 60424c06356bSdh 6043c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, NULL, 60444c06356bSdh "%s: Issuing spinup release for PHY %s", __func__, 60454c06356bSdh phyp->path); 60464c06356bSdh 60474c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 60484c06356bSdh msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 60494c06356bSdh if (msg == NULL || (pwrk = 60504c06356bSdh pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) { 60514c06356bSdh pmcs_unlock_phy(tphyp); 60524c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 60534c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_SPINUP_RELEASE); 60544c06356bSdh break; 60554c06356bSdh } 60564c06356bSdh 60574c06356bSdh tphyp->spinup_hold = 0; 60584c06356bSdh bzero(msg, PMCS_QENTRY_SIZE); 60594c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 60604c06356bSdh PMCIN_LOCAL_PHY_CONTROL)); 60614c06356bSdh msg[1] = LE_32(pwrk->htag); 60624c06356bSdh msg[2] = LE_32((0x10 << 8) | tphyp->phynum); 60634c06356bSdh 60644c06356bSdh pwrk->dtype = phyp->dtype; 60654c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 60664c06356bSdh mutex_exit(&pwrk->lock); 60674c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 60684c06356bSdh pmcs_unlock_phy(tphyp); 60694c06356bSdh 60704c06356bSdh tphyp = tphyp->sibling; 60714c06356bSdh } 60724c06356bSdh } 60734c06356bSdh 60744c06356bSdh /* 60754c06356bSdh * Abort commands on dead PHYs and deregister them as well as removing 60764c06356bSdh * the associated targets. 60774c06356bSdh */ 60784c06356bSdh static int 60794c06356bSdh pmcs_kill_devices(pmcs_hw_t *pwp, pmcs_phy_t *phyp) 60804c06356bSdh { 60814c06356bSdh pmcs_phy_t *pnext, *pchild; 60824c06356bSdh boolean_t remove_device; 60834c06356bSdh int rval = 0; 60844c06356bSdh 60854c06356bSdh while (phyp) { 60864c06356bSdh pmcs_lock_phy(phyp); 60874c06356bSdh pchild = phyp->children; 60884c06356bSdh pnext = phyp->sibling; 60894c06356bSdh pmcs_unlock_phy(phyp); 60904c06356bSdh 60914c06356bSdh if (pchild) { 60924c06356bSdh rval = pmcs_kill_devices(pwp, pchild); 60934c06356bSdh if (rval) { 60944c06356bSdh return (rval); 60954c06356bSdh } 60964c06356bSdh } 60974c06356bSdh 60984c06356bSdh /* 60994c06356bSdh * pmcs_remove_device requires the softstate lock. 61004c06356bSdh */ 61014c06356bSdh mutex_enter(&pwp->lock); 61024c06356bSdh pmcs_lock_phy(phyp); 61034c06356bSdh if (phyp->dead && phyp->valid_device_id) { 61044c06356bSdh remove_device = B_TRUE; 61054c06356bSdh } else { 61064c06356bSdh remove_device = B_FALSE; 61074c06356bSdh } 61084c06356bSdh 61094c06356bSdh if (remove_device) { 61104c06356bSdh pmcs_remove_device(pwp, phyp); 61114c06356bSdh mutex_exit(&pwp->lock); 61124c06356bSdh 61134c06356bSdh rval = pmcs_kill_device(pwp, phyp); 61144c06356bSdh 61154c06356bSdh if (rval) { 61164c06356bSdh pmcs_unlock_phy(phyp); 61174c06356bSdh return (rval); 61184c06356bSdh } 61194c06356bSdh } else { 61204c06356bSdh mutex_exit(&pwp->lock); 61214c06356bSdh } 61224c06356bSdh 61234c06356bSdh pmcs_unlock_phy(phyp); 61244c06356bSdh phyp = pnext; 61254c06356bSdh } 61264c06356bSdh 61274c06356bSdh return (rval); 61284c06356bSdh } 61294c06356bSdh 61304c06356bSdh /* 61314c06356bSdh * Called with PHY locked 61324c06356bSdh */ 61334c06356bSdh int 61344c06356bSdh pmcs_kill_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 61354c06356bSdh { 61364c06356bSdh int r, result; 61374c06356bSdh uint32_t msg[PMCS_MSG_SIZE], *ptr, status; 61384c06356bSdh struct pmcwork *pwrk; 61394c06356bSdh 6140c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, "kill %s device @ %s", 61414c06356bSdh pmcs_get_typename(pptr->dtype), pptr->path); 61424c06356bSdh 61434c06356bSdh /* 61444c06356bSdh * There may be an outstanding ABORT_ALL running, which we wouldn't 61454c06356bSdh * know just by checking abort_pending. We can, however, check 61464c06356bSdh * abort_all_start. If it's non-zero, there is one, and we'll just 61474c06356bSdh * sit here and wait for it to complete. If we don't, we'll remove 61484c06356bSdh * the device while there are still commands pending. 61494c06356bSdh */ 61504c06356bSdh if (pptr->abort_all_start) { 61514c06356bSdh while (pptr->abort_all_start) { 6152c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 61534c06356bSdh "%s: Waiting for outstanding ABORT_ALL on PHY 0x%p", 61544c06356bSdh __func__, (void *)pptr); 61554c06356bSdh cv_wait(&pptr->abort_all_cv, &pptr->phy_lock); 61564c06356bSdh } 61574c06356bSdh } else if (pptr->abort_pending) { 61584c06356bSdh r = pmcs_abort(pwp, pptr, pptr->device_id, 1, 1); 61594c06356bSdh 61604c06356bSdh if (r) { 6161c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 61624c06356bSdh "%s: ABORT_ALL returned non-zero status (%d) for " 61634c06356bSdh "PHY 0x%p", __func__, r, (void *)pptr); 61644c06356bSdh return (r); 61654c06356bSdh } 61664c06356bSdh pptr->abort_pending = 0; 61674c06356bSdh } 61684c06356bSdh 61694c06356bSdh if (pptr->valid_device_id == 0) { 61704c06356bSdh return (0); 61714c06356bSdh } 61724c06356bSdh 61734c06356bSdh if ((pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr)) == NULL) { 6174c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__); 61754c06356bSdh return (ENOMEM); 61764c06356bSdh } 61774c06356bSdh pwrk->arg = msg; 61784c06356bSdh pwrk->dtype = pptr->dtype; 61794c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 61804c06356bSdh PMCIN_DEREGISTER_DEVICE_HANDLE)); 61814c06356bSdh msg[1] = LE_32(pwrk->htag); 61824c06356bSdh msg[2] = LE_32(pptr->device_id); 61834c06356bSdh 61844c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 61854c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 61864c06356bSdh if (ptr == NULL) { 61874c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 61884c06356bSdh mutex_exit(&pwrk->lock); 6189c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__); 61904c06356bSdh return (ENOMEM); 61914c06356bSdh } 61924c06356bSdh 61934c06356bSdh COPY_MESSAGE(ptr, msg, 3); 61944c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 61954c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 61964c06356bSdh 61974c06356bSdh pmcs_unlock_phy(pptr); 61984c06356bSdh WAIT_FOR(pwrk, 250, result); 61994c06356bSdh pmcs_lock_phy(pptr); 62004c06356bSdh pmcs_pwork(pwp, pwrk); 62014c06356bSdh 62024c06356bSdh if (result) { 62034c06356bSdh return (ETIMEDOUT); 62044c06356bSdh } 62054c06356bSdh status = LE_32(msg[2]); 62064c06356bSdh if (status != PMCOUT_STATUS_OK) { 6207c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 62084c06356bSdh "%s: status 0x%x when trying to deregister device %s", 62094c06356bSdh __func__, status, pptr->path); 62104c06356bSdh } 62114c06356bSdh 62124c06356bSdh pptr->device_id = PMCS_INVALID_DEVICE_ID; 62134c06356bSdh PHY_CHANGED(pwp, pptr); 62144c06356bSdh RESTART_DISCOVERY(pwp); 62154c06356bSdh pptr->valid_device_id = 0; 62164c06356bSdh return (0); 62174c06356bSdh } 62184c06356bSdh 62194c06356bSdh /* 62204c06356bSdh * Acknowledge the SAS h/w events that need acknowledgement. 62214c06356bSdh * This is only needed for first level PHYs. 62224c06356bSdh */ 62234c06356bSdh void 62244c06356bSdh pmcs_ack_events(pmcs_hw_t *pwp) 62254c06356bSdh { 62264c06356bSdh uint32_t msg[PMCS_MSG_SIZE], *ptr; 62274c06356bSdh struct pmcwork *pwrk; 62284c06356bSdh pmcs_phy_t *pptr; 62294c06356bSdh 62304c06356bSdh for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) { 62314c06356bSdh pmcs_lock_phy(pptr); 62324c06356bSdh if (pptr->hw_event_ack == 0) { 62334c06356bSdh pmcs_unlock_phy(pptr); 62344c06356bSdh continue; 62354c06356bSdh } 62364c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 62374c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 62384c06356bSdh 62394c06356bSdh if ((ptr == NULL) || (pwrk = 62404c06356bSdh pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) { 62414c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 62424c06356bSdh pmcs_unlock_phy(pptr); 62434c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_SAS_HW_ACK); 62444c06356bSdh break; 62454c06356bSdh } 62464c06356bSdh 62474c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 62484c06356bSdh PMCIN_SAW_HW_EVENT_ACK)); 62494c06356bSdh msg[1] = LE_32(pwrk->htag); 62504c06356bSdh msg[2] = LE_32(pptr->hw_event_ack); 62514c06356bSdh 62524c06356bSdh mutex_exit(&pwrk->lock); 62534c06356bSdh pwrk->dtype = pptr->dtype; 62544c06356bSdh pptr->hw_event_ack = 0; 62554c06356bSdh COPY_MESSAGE(ptr, msg, 3); 62564c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 62574c06356bSdh pmcs_unlock_phy(pptr); 62584c06356bSdh } 62594c06356bSdh } 62604c06356bSdh 62614c06356bSdh /* 62624c06356bSdh * Load DMA 62634c06356bSdh */ 62644c06356bSdh int 62654c06356bSdh pmcs_dma_load(pmcs_hw_t *pwp, pmcs_cmd_t *sp, uint32_t *msg) 62664c06356bSdh { 62674c06356bSdh ddi_dma_cookie_t *sg; 62684c06356bSdh pmcs_dmachunk_t *tc; 62694c06356bSdh pmcs_dmasgl_t *sgl, *prior; 62704c06356bSdh int seg, tsc; 62714c06356bSdh uint64_t sgl_addr; 62724c06356bSdh 62734c06356bSdh /* 62744c06356bSdh * If we have no data segments, we're done. 62754c06356bSdh */ 62764c06356bSdh if (CMD2PKT(sp)->pkt_numcookies == 0) { 62774c06356bSdh return (0); 62784c06356bSdh } 62794c06356bSdh 62804c06356bSdh /* 62814c06356bSdh * Get the S/G list pointer. 62824c06356bSdh */ 62834c06356bSdh sg = CMD2PKT(sp)->pkt_cookies; 62844c06356bSdh 62854c06356bSdh /* 62864c06356bSdh * If we only have one dma segment, we can directly address that 62874c06356bSdh * data within the Inbound message itself. 62884c06356bSdh */ 62894c06356bSdh if (CMD2PKT(sp)->pkt_numcookies == 1) { 62904c06356bSdh msg[12] = LE_32(DWORD0(sg->dmac_laddress)); 62914c06356bSdh msg[13] = LE_32(DWORD1(sg->dmac_laddress)); 62924c06356bSdh msg[14] = LE_32(sg->dmac_size); 62934c06356bSdh msg[15] = 0; 62944c06356bSdh return (0); 62954c06356bSdh } 62964c06356bSdh 62974c06356bSdh /* 62984c06356bSdh * Otherwise, we'll need one or more external S/G list chunks. 62994c06356bSdh * Get the first one and its dma address into the Inbound message. 63004c06356bSdh */ 63014c06356bSdh mutex_enter(&pwp->dma_lock); 63024c06356bSdh tc = pwp->dma_freelist; 63034c06356bSdh if (tc == NULL) { 63044c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS); 63054c06356bSdh mutex_exit(&pwp->dma_lock); 6306c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, 6307c3bc407cSdh "%s: out of SG lists", __func__); 63084c06356bSdh return (-1); 63094c06356bSdh } 63104c06356bSdh pwp->dma_freelist = tc->nxt; 63114c06356bSdh mutex_exit(&pwp->dma_lock); 63124c06356bSdh 63134c06356bSdh tc->nxt = NULL; 63144c06356bSdh sp->cmd_clist = tc; 63154c06356bSdh sgl = tc->chunks; 63164c06356bSdh (void) memset(tc->chunks, 0, PMCS_SGL_CHUNKSZ); 63174c06356bSdh sgl_addr = tc->addr; 63184c06356bSdh msg[12] = LE_32(DWORD0(sgl_addr)); 63194c06356bSdh msg[13] = LE_32(DWORD1(sgl_addr)); 63204c06356bSdh msg[14] = 0; 63214c06356bSdh msg[15] = LE_32(PMCS_DMASGL_EXTENSION); 63224c06356bSdh 63234c06356bSdh prior = sgl; 63244c06356bSdh tsc = 0; 63254c06356bSdh 63264c06356bSdh for (seg = 0; seg < CMD2PKT(sp)->pkt_numcookies; seg++) { 63274c06356bSdh /* 63284c06356bSdh * If the current segment count for this chunk is one less than 63294c06356bSdh * the number s/g lists per chunk and we have more than one seg 63304c06356bSdh * to go, we need another chunk. Get it, and make sure that the 63314c06356bSdh * tail end of the the previous chunk points the new chunk 63324c06356bSdh * (if remembering an offset can be called 'pointing to'). 63334c06356bSdh * 63344c06356bSdh * Note that we can store the offset into our command area that 63354c06356bSdh * represents the new chunk in the length field of the part 63364c06356bSdh * that points the PMC chip at the next chunk- the PMC chip 63374c06356bSdh * ignores this field when the EXTENSION bit is set. 63384c06356bSdh * 63394c06356bSdh * This is required for dma unloads later. 63404c06356bSdh */ 63414c06356bSdh if (tsc == (PMCS_SGL_NCHUNKS - 1) && 63424c06356bSdh seg < (CMD2PKT(sp)->pkt_numcookies - 1)) { 63434c06356bSdh mutex_enter(&pwp->dma_lock); 63444c06356bSdh tc = pwp->dma_freelist; 63454c06356bSdh if (tc == NULL) { 63464c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS); 63474c06356bSdh mutex_exit(&pwp->dma_lock); 63484c06356bSdh pmcs_dma_unload(pwp, sp); 6349c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, 63504c06356bSdh "%s: out of SG lists", __func__); 63514c06356bSdh return (-1); 63524c06356bSdh } 63534c06356bSdh pwp->dma_freelist = tc->nxt; 63544c06356bSdh tc->nxt = sp->cmd_clist; 63554c06356bSdh mutex_exit(&pwp->dma_lock); 63564c06356bSdh 63574c06356bSdh sp->cmd_clist = tc; 63584c06356bSdh (void) memset(tc->chunks, 0, PMCS_SGL_CHUNKSZ); 63594c06356bSdh sgl = tc->chunks; 63604c06356bSdh sgl_addr = tc->addr; 63614c06356bSdh prior[PMCS_SGL_NCHUNKS-1].sglal = 63624c06356bSdh LE_32(DWORD0(sgl_addr)); 63634c06356bSdh prior[PMCS_SGL_NCHUNKS-1].sglah = 63644c06356bSdh LE_32(DWORD1(sgl_addr)); 63654c06356bSdh prior[PMCS_SGL_NCHUNKS-1].sglen = 0; 63664c06356bSdh prior[PMCS_SGL_NCHUNKS-1].flags = 63674c06356bSdh LE_32(PMCS_DMASGL_EXTENSION); 63684c06356bSdh prior = sgl; 63694c06356bSdh tsc = 0; 63704c06356bSdh } 63714c06356bSdh sgl[tsc].sglal = LE_32(DWORD0(sg->dmac_laddress)); 63724c06356bSdh sgl[tsc].sglah = LE_32(DWORD1(sg->dmac_laddress)); 63734c06356bSdh sgl[tsc].sglen = LE_32(sg->dmac_size); 63744c06356bSdh sgl[tsc++].flags = 0; 63754c06356bSdh sg++; 63764c06356bSdh } 63774c06356bSdh return (0); 63784c06356bSdh } 63794c06356bSdh 63804c06356bSdh /* 63814c06356bSdh * Unload DMA 63824c06356bSdh */ 63834c06356bSdh void 63844c06356bSdh pmcs_dma_unload(pmcs_hw_t *pwp, pmcs_cmd_t *sp) 63854c06356bSdh { 63864c06356bSdh pmcs_dmachunk_t *cp; 63874c06356bSdh 63884c06356bSdh mutex_enter(&pwp->dma_lock); 63894c06356bSdh while ((cp = sp->cmd_clist) != NULL) { 63904c06356bSdh sp->cmd_clist = cp->nxt; 63914c06356bSdh cp->nxt = pwp->dma_freelist; 63924c06356bSdh pwp->dma_freelist = cp; 63934c06356bSdh } 63944c06356bSdh mutex_exit(&pwp->dma_lock); 63954c06356bSdh } 63964c06356bSdh 63974c06356bSdh /* 63984c06356bSdh * Take a chunk of consistent memory that has just been allocated and inserted 63994c06356bSdh * into the cip indices and prepare it for DMA chunk usage and add it to the 64004c06356bSdh * freelist. 64014c06356bSdh * 64024c06356bSdh * Called with dma_lock locked (except during attach when it's unnecessary) 64034c06356bSdh */ 64044c06356bSdh void 64054c06356bSdh pmcs_idma_chunks(pmcs_hw_t *pwp, pmcs_dmachunk_t *dcp, 64064c06356bSdh pmcs_chunk_t *pchunk, unsigned long lim) 64074c06356bSdh { 64084c06356bSdh unsigned long off, n; 64094c06356bSdh pmcs_dmachunk_t *np = dcp; 64104c06356bSdh pmcs_chunk_t *tmp_chunk; 64114c06356bSdh 64124c06356bSdh if (pwp->dma_chunklist == NULL) { 64134c06356bSdh pwp->dma_chunklist = pchunk; 64144c06356bSdh } else { 64154c06356bSdh tmp_chunk = pwp->dma_chunklist; 64164c06356bSdh while (tmp_chunk->next) { 64174c06356bSdh tmp_chunk = tmp_chunk->next; 64184c06356bSdh } 64194c06356bSdh tmp_chunk->next = pchunk; 64204c06356bSdh } 64214c06356bSdh 64224c06356bSdh /* 64234c06356bSdh * Install offsets into chunk lists. 64244c06356bSdh */ 64254c06356bSdh for (n = 0, off = 0; off < lim; off += PMCS_SGL_CHUNKSZ, n++) { 64264c06356bSdh np->chunks = (void *)&pchunk->addrp[off]; 64274c06356bSdh np->addr = pchunk->dma_addr + off; 64284c06356bSdh np->acc_handle = pchunk->acc_handle; 64294c06356bSdh np->dma_handle = pchunk->dma_handle; 64304c06356bSdh if ((off + PMCS_SGL_CHUNKSZ) < lim) { 64314c06356bSdh np = np->nxt; 64324c06356bSdh } 64334c06356bSdh } 64344c06356bSdh np->nxt = pwp->dma_freelist; 64354c06356bSdh pwp->dma_freelist = dcp; 6436c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, 64374c06356bSdh "added %lu DMA chunks ", n); 64384c06356bSdh } 64394c06356bSdh 64404c06356bSdh /* 64414c06356bSdh * Change the value of the interrupt coalescing timer. This is done currently 64424c06356bSdh * only for I/O completions. If we're using the "auto clear" feature, it can 64434c06356bSdh * be turned back on when interrupt coalescing is turned off and must be 64444c06356bSdh * turned off when the coalescing timer is on. 64454c06356bSdh * NOTE: PMCS_MSIX_GENERAL and PMCS_OQ_IODONE are the same value. As long 64464c06356bSdh * as that's true, we don't need to distinguish between them. 64474c06356bSdh */ 64484c06356bSdh 64494c06356bSdh void 64504c06356bSdh pmcs_set_intr_coal_timer(pmcs_hw_t *pwp, pmcs_coal_timer_adj_t adj) 64514c06356bSdh { 64524c06356bSdh if (adj == DECREASE_TIMER) { 64534c06356bSdh /* If the timer is already off, nothing to do. */ 64544c06356bSdh if (pwp->io_intr_coal.timer_on == B_FALSE) { 64554c06356bSdh return; 64564c06356bSdh } 64574c06356bSdh 64584c06356bSdh pwp->io_intr_coal.intr_coal_timer -= PMCS_COAL_TIMER_GRAN; 64594c06356bSdh 64604c06356bSdh if (pwp->io_intr_coal.intr_coal_timer == 0) { 64614c06356bSdh /* Disable the timer */ 64624c06356bSdh pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_CONTROL, 0); 64634c06356bSdh 64644c06356bSdh if (pwp->odb_auto_clear & (1 << PMCS_MSIX_IODONE)) { 64654c06356bSdh pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR, 64664c06356bSdh pwp->odb_auto_clear); 64674c06356bSdh } 64684c06356bSdh 64694c06356bSdh pwp->io_intr_coal.timer_on = B_FALSE; 64704c06356bSdh pwp->io_intr_coal.max_io_completions = B_FALSE; 64714c06356bSdh pwp->io_intr_coal.num_intrs = 0; 64724c06356bSdh pwp->io_intr_coal.int_cleared = B_FALSE; 64734c06356bSdh pwp->io_intr_coal.num_io_completions = 0; 64744c06356bSdh 64754c06356bSdh DTRACE_PROBE1(pmcs__intr__coalesce__timer__off, 64764c06356bSdh pmcs_io_intr_coal_t *, &pwp->io_intr_coal); 64774c06356bSdh } else { 64784c06356bSdh pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_TIMER, 64794c06356bSdh pwp->io_intr_coal.intr_coal_timer); 64804c06356bSdh } 64814c06356bSdh } else { 64824c06356bSdh /* 64834c06356bSdh * If the timer isn't on yet, do the setup for it now. 64844c06356bSdh */ 64854c06356bSdh if (pwp->io_intr_coal.timer_on == B_FALSE) { 64864c06356bSdh /* If auto clear is being used, turn it off. */ 64874c06356bSdh if (pwp->odb_auto_clear & (1 << PMCS_MSIX_IODONE)) { 64884c06356bSdh pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR, 64894c06356bSdh (pwp->odb_auto_clear & 64904c06356bSdh ~(1 << PMCS_MSIX_IODONE))); 64914c06356bSdh } 64924c06356bSdh 64934c06356bSdh pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_CONTROL, 64944c06356bSdh (1 << PMCS_MSIX_IODONE)); 64954c06356bSdh pwp->io_intr_coal.timer_on = B_TRUE; 64964c06356bSdh pwp->io_intr_coal.intr_coal_timer = 64974c06356bSdh PMCS_COAL_TIMER_GRAN; 64984c06356bSdh 64994c06356bSdh DTRACE_PROBE1(pmcs__intr__coalesce__timer__on, 65004c06356bSdh pmcs_io_intr_coal_t *, &pwp->io_intr_coal); 65014c06356bSdh } else { 65024c06356bSdh pwp->io_intr_coal.intr_coal_timer += 65034c06356bSdh PMCS_COAL_TIMER_GRAN; 65044c06356bSdh } 65054c06356bSdh 65064c06356bSdh if (pwp->io_intr_coal.intr_coal_timer > PMCS_MAX_COAL_TIMER) { 65074c06356bSdh pwp->io_intr_coal.intr_coal_timer = PMCS_MAX_COAL_TIMER; 65084c06356bSdh } 65094c06356bSdh 65104c06356bSdh pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_TIMER, 65114c06356bSdh pwp->io_intr_coal.intr_coal_timer); 65124c06356bSdh } 65134c06356bSdh 65144c06356bSdh /* 65154c06356bSdh * Adjust the interrupt threshold based on the current timer value 65164c06356bSdh */ 65174c06356bSdh pwp->io_intr_coal.intr_threshold = 65184c06356bSdh PMCS_INTR_THRESHOLD(PMCS_QUANTUM_TIME_USECS * 1000 / 65194c06356bSdh (pwp->io_intr_coal.intr_latency + 65204c06356bSdh (pwp->io_intr_coal.intr_coal_timer * 1000))); 65214c06356bSdh } 65224c06356bSdh 65234c06356bSdh /* 65244c06356bSdh * Register Access functions 65254c06356bSdh */ 65264c06356bSdh uint32_t 65274c06356bSdh pmcs_rd_iqci(pmcs_hw_t *pwp, uint32_t qnum) 65284c06356bSdh { 65294c06356bSdh uint32_t iqci; 65304c06356bSdh 65314c06356bSdh if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORKERNEL) != 65324c06356bSdh DDI_SUCCESS) { 6533c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6534c3bc407cSdh "%s: ddi_dma_sync failed?", __func__); 65354c06356bSdh } 65364c06356bSdh 65374c06356bSdh iqci = LE_32( 65384c06356bSdh ((uint32_t *)((void *)pwp->cip))[IQ_OFFSET(qnum) >> 2]); 65394c06356bSdh 65404c06356bSdh return (iqci); 65414c06356bSdh } 65424c06356bSdh 65434c06356bSdh uint32_t 65444c06356bSdh pmcs_rd_oqpi(pmcs_hw_t *pwp, uint32_t qnum) 65454c06356bSdh { 65464c06356bSdh uint32_t oqpi; 65474c06356bSdh 65484c06356bSdh if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORKERNEL) != 65494c06356bSdh DDI_SUCCESS) { 6550c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6551c3bc407cSdh "%s: ddi_dma_sync failed?", __func__); 65524c06356bSdh } 65534c06356bSdh 65544c06356bSdh oqpi = LE_32( 65554c06356bSdh ((uint32_t *)((void *)pwp->cip))[OQ_OFFSET(qnum) >> 2]); 65564c06356bSdh 65574c06356bSdh return (oqpi); 65584c06356bSdh } 65594c06356bSdh 65604c06356bSdh uint32_t 65612ac4abe8SDavid Hollister pmcs_rd_gsm_reg(pmcs_hw_t *pwp, uint8_t hi, uint32_t off) 65624c06356bSdh { 65632ac4abe8SDavid Hollister uint32_t rv, newaxil, oldaxil, oldaxih; 65644c06356bSdh 65654c06356bSdh newaxil = off & ~GSM_BASE_MASK; 65664c06356bSdh off &= GSM_BASE_MASK; 65674c06356bSdh mutex_enter(&pwp->axil_lock); 65684c06356bSdh oldaxil = ddi_get32(pwp->top_acc_handle, 65694c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2]); 65704c06356bSdh ddi_put32(pwp->top_acc_handle, 65714c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2], newaxil); 65724c06356bSdh drv_usecwait(10); 65734c06356bSdh if (ddi_get32(pwp->top_acc_handle, 65744c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != newaxil) { 6575c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6576c3bc407cSdh "AXIL register update failed"); 65774c06356bSdh } 65782ac4abe8SDavid Hollister if (hi) { 65792ac4abe8SDavid Hollister oldaxih = ddi_get32(pwp->top_acc_handle, 65802ac4abe8SDavid Hollister &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2]); 65812ac4abe8SDavid Hollister ddi_put32(pwp->top_acc_handle, 65822ac4abe8SDavid Hollister &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2], hi); 65832ac4abe8SDavid Hollister drv_usecwait(10); 65842ac4abe8SDavid Hollister if (ddi_get32(pwp->top_acc_handle, 65852ac4abe8SDavid Hollister &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2]) != hi) { 65862ac4abe8SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 65872ac4abe8SDavid Hollister "AXIH register update failed"); 65882ac4abe8SDavid Hollister } 65892ac4abe8SDavid Hollister } 65904c06356bSdh rv = ddi_get32(pwp->gsm_acc_handle, &pwp->gsm_regs[off >> 2]); 65912ac4abe8SDavid Hollister if (hi) { 65922ac4abe8SDavid Hollister ddi_put32(pwp->top_acc_handle, 65932ac4abe8SDavid Hollister &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2], oldaxih); 65942ac4abe8SDavid Hollister drv_usecwait(10); 65952ac4abe8SDavid Hollister if (ddi_get32(pwp->top_acc_handle, 65962ac4abe8SDavid Hollister &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2]) != oldaxih) { 65972ac4abe8SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 65982ac4abe8SDavid Hollister "AXIH register restore failed"); 65992ac4abe8SDavid Hollister } 66002ac4abe8SDavid Hollister } 66014c06356bSdh ddi_put32(pwp->top_acc_handle, 66024c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2], oldaxil); 66034c06356bSdh drv_usecwait(10); 66044c06356bSdh if (ddi_get32(pwp->top_acc_handle, 66054c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != oldaxil) { 6606c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6607c3bc407cSdh "AXIL register restore failed"); 66084c06356bSdh } 66094c06356bSdh mutex_exit(&pwp->axil_lock); 66104c06356bSdh return (rv); 66114c06356bSdh } 66124c06356bSdh 66134c06356bSdh void 66144c06356bSdh pmcs_wr_gsm_reg(pmcs_hw_t *pwp, uint32_t off, uint32_t val) 66154c06356bSdh { 66164c06356bSdh uint32_t newaxil, oldaxil; 66174c06356bSdh 66184c06356bSdh newaxil = off & ~GSM_BASE_MASK; 66194c06356bSdh off &= GSM_BASE_MASK; 66204c06356bSdh mutex_enter(&pwp->axil_lock); 66214c06356bSdh oldaxil = ddi_get32(pwp->top_acc_handle, 66224c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2]); 66234c06356bSdh ddi_put32(pwp->top_acc_handle, 66244c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2], newaxil); 66254c06356bSdh drv_usecwait(10); 66264c06356bSdh if (ddi_get32(pwp->top_acc_handle, 66274c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != newaxil) { 6628c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6629c3bc407cSdh "AXIL register update failed"); 66304c06356bSdh } 66314c06356bSdh ddi_put32(pwp->gsm_acc_handle, &pwp->gsm_regs[off >> 2], val); 66324c06356bSdh ddi_put32(pwp->top_acc_handle, 66334c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2], oldaxil); 66344c06356bSdh drv_usecwait(10); 66354c06356bSdh if (ddi_get32(pwp->top_acc_handle, 66364c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != oldaxil) { 6637c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6638c3bc407cSdh "AXIL register restore failed"); 66394c06356bSdh } 66404c06356bSdh mutex_exit(&pwp->axil_lock); 66414c06356bSdh } 66424c06356bSdh 66434c06356bSdh uint32_t 66444c06356bSdh pmcs_rd_topunit(pmcs_hw_t *pwp, uint32_t off) 66454c06356bSdh { 66464c06356bSdh switch (off) { 66474c06356bSdh case PMCS_SPC_RESET: 66484c06356bSdh case PMCS_SPC_BOOT_STRAP: 66494c06356bSdh case PMCS_SPC_DEVICE_ID: 66504c06356bSdh case PMCS_DEVICE_REVISION: 66512ac4abe8SDavid Hollister off = pmcs_rd_gsm_reg(pwp, 0, off); 66524c06356bSdh break; 66534c06356bSdh default: 66544c06356bSdh off = ddi_get32(pwp->top_acc_handle, 66554c06356bSdh &pwp->top_regs[off >> 2]); 66564c06356bSdh break; 66574c06356bSdh } 66584c06356bSdh return (off); 66594c06356bSdh } 66604c06356bSdh 66614c06356bSdh void 66624c06356bSdh pmcs_wr_topunit(pmcs_hw_t *pwp, uint32_t off, uint32_t val) 66634c06356bSdh { 66644c06356bSdh switch (off) { 66654c06356bSdh case PMCS_SPC_RESET: 66664c06356bSdh case PMCS_DEVICE_REVISION: 66674c06356bSdh pmcs_wr_gsm_reg(pwp, off, val); 66684c06356bSdh break; 66694c06356bSdh default: 66704c06356bSdh ddi_put32(pwp->top_acc_handle, &pwp->top_regs[off >> 2], val); 66714c06356bSdh break; 66724c06356bSdh } 66734c06356bSdh } 66744c06356bSdh 66754c06356bSdh uint32_t 66764c06356bSdh pmcs_rd_msgunit(pmcs_hw_t *pwp, uint32_t off) 66774c06356bSdh { 66784c06356bSdh return (ddi_get32(pwp->msg_acc_handle, &pwp->msg_regs[off >> 2])); 66794c06356bSdh } 66804c06356bSdh 66814c06356bSdh uint32_t 66824c06356bSdh pmcs_rd_mpi_tbl(pmcs_hw_t *pwp, uint32_t off) 66834c06356bSdh { 66844c06356bSdh return (ddi_get32(pwp->mpi_acc_handle, 66854c06356bSdh &pwp->mpi_regs[(pwp->mpi_offset + off) >> 2])); 66864c06356bSdh } 66874c06356bSdh 66884c06356bSdh uint32_t 66894c06356bSdh pmcs_rd_gst_tbl(pmcs_hw_t *pwp, uint32_t off) 66904c06356bSdh { 66914c06356bSdh return (ddi_get32(pwp->mpi_acc_handle, 66924c06356bSdh &pwp->mpi_regs[(pwp->mpi_gst_offset + off) >> 2])); 66934c06356bSdh } 66944c06356bSdh 66954c06356bSdh uint32_t 66964c06356bSdh pmcs_rd_iqc_tbl(pmcs_hw_t *pwp, uint32_t off) 66974c06356bSdh { 66984c06356bSdh return (ddi_get32(pwp->mpi_acc_handle, 66994c06356bSdh &pwp->mpi_regs[(pwp->mpi_iqc_offset + off) >> 2])); 67004c06356bSdh } 67014c06356bSdh 67024c06356bSdh uint32_t 67034c06356bSdh pmcs_rd_oqc_tbl(pmcs_hw_t *pwp, uint32_t off) 67044c06356bSdh { 67054c06356bSdh return (ddi_get32(pwp->mpi_acc_handle, 67064c06356bSdh &pwp->mpi_regs[(pwp->mpi_oqc_offset + off) >> 2])); 67074c06356bSdh } 67084c06356bSdh 67094c06356bSdh uint32_t 67104c06356bSdh pmcs_rd_iqpi(pmcs_hw_t *pwp, uint32_t qnum) 67114c06356bSdh { 67124c06356bSdh return (ddi_get32(pwp->mpi_acc_handle, 67134c06356bSdh &pwp->mpi_regs[pwp->iqpi_offset[qnum] >> 2])); 67144c06356bSdh } 67154c06356bSdh 67164c06356bSdh uint32_t 67174c06356bSdh pmcs_rd_oqci(pmcs_hw_t *pwp, uint32_t qnum) 67184c06356bSdh { 67194c06356bSdh return (ddi_get32(pwp->mpi_acc_handle, 67204c06356bSdh &pwp->mpi_regs[pwp->oqci_offset[qnum] >> 2])); 67214c06356bSdh } 67224c06356bSdh 67234c06356bSdh void 67244c06356bSdh pmcs_wr_msgunit(pmcs_hw_t *pwp, uint32_t off, uint32_t val) 67254c06356bSdh { 67264c06356bSdh ddi_put32(pwp->msg_acc_handle, &pwp->msg_regs[off >> 2], val); 67274c06356bSdh } 67284c06356bSdh 67294c06356bSdh void 67304c06356bSdh pmcs_wr_mpi_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val) 67314c06356bSdh { 67324c06356bSdh ddi_put32(pwp->mpi_acc_handle, 67334c06356bSdh &pwp->mpi_regs[(pwp->mpi_offset + off) >> 2], (val)); 67344c06356bSdh } 67354c06356bSdh 67364c06356bSdh void 67374c06356bSdh pmcs_wr_gst_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val) 67384c06356bSdh { 67394c06356bSdh ddi_put32(pwp->mpi_acc_handle, 67404c06356bSdh &pwp->mpi_regs[(pwp->mpi_gst_offset + off) >> 2], val); 67414c06356bSdh } 67424c06356bSdh 67434c06356bSdh void 67444c06356bSdh pmcs_wr_iqc_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val) 67454c06356bSdh { 67464c06356bSdh ddi_put32(pwp->mpi_acc_handle, 67474c06356bSdh &pwp->mpi_regs[(pwp->mpi_iqc_offset + off) >> 2], val); 67484c06356bSdh } 67494c06356bSdh 67504c06356bSdh void 67514c06356bSdh pmcs_wr_oqc_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val) 67524c06356bSdh { 67534c06356bSdh ddi_put32(pwp->mpi_acc_handle, 67544c06356bSdh &pwp->mpi_regs[(pwp->mpi_oqc_offset + off) >> 2], val); 67554c06356bSdh } 67564c06356bSdh 67574c06356bSdh void 67584c06356bSdh pmcs_wr_iqci(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val) 67594c06356bSdh { 67604c06356bSdh ((uint32_t *)((void *)pwp->cip))[IQ_OFFSET(qnum) >> 2] = val; 67614c06356bSdh if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORDEV) != 67624c06356bSdh DDI_SUCCESS) { 6763c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6764c3bc407cSdh "%s: ddi_dma_sync failed?", __func__); 67654c06356bSdh } 67664c06356bSdh } 67674c06356bSdh 67684c06356bSdh void 67694c06356bSdh pmcs_wr_iqpi(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val) 67704c06356bSdh { 67714c06356bSdh ddi_put32(pwp->mpi_acc_handle, 67724c06356bSdh &pwp->mpi_regs[pwp->iqpi_offset[qnum] >> 2], val); 67734c06356bSdh } 67744c06356bSdh 67754c06356bSdh void 67764c06356bSdh pmcs_wr_oqci(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val) 67774c06356bSdh { 67784c06356bSdh ddi_put32(pwp->mpi_acc_handle, 67794c06356bSdh &pwp->mpi_regs[pwp->oqci_offset[qnum] >> 2], val); 67804c06356bSdh } 67814c06356bSdh 67824c06356bSdh void 67834c06356bSdh pmcs_wr_oqpi(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val) 67844c06356bSdh { 67854c06356bSdh ((uint32_t *)((void *)pwp->cip))[OQ_OFFSET(qnum) >> 2] = val; 67864c06356bSdh if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORDEV) != 67874c06356bSdh DDI_SUCCESS) { 6788c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6789c3bc407cSdh "%s: ddi_dma_sync failed?", __func__); 67904c06356bSdh } 67914c06356bSdh } 67924c06356bSdh 67934c06356bSdh /* 67944c06356bSdh * Check the status value of an outbound IOMB and report anything bad 67954c06356bSdh */ 67964c06356bSdh 67974c06356bSdh void 67984c06356bSdh pmcs_check_iomb_status(pmcs_hw_t *pwp, uint32_t *iomb) 67994c06356bSdh { 68004c06356bSdh uint16_t opcode; 68014c06356bSdh int offset; 68024c06356bSdh 68034c06356bSdh if (iomb == NULL) { 68044c06356bSdh return; 68054c06356bSdh } 68064c06356bSdh 68074c06356bSdh opcode = LE_32(iomb[0]) & 0xfff; 68084c06356bSdh 68094c06356bSdh switch (opcode) { 68104c06356bSdh /* 68114c06356bSdh * The following have no status field, so ignore them 68124c06356bSdh */ 68134c06356bSdh case PMCOUT_ECHO: 68144c06356bSdh case PMCOUT_SAS_HW_EVENT: 68154c06356bSdh case PMCOUT_GET_DEVICE_HANDLE: 68164c06356bSdh case PMCOUT_SATA_EVENT: 68174c06356bSdh case PMCOUT_SSP_EVENT: 68184c06356bSdh case PMCOUT_DEVICE_HANDLE_ARRIVED: 68194c06356bSdh case PMCOUT_SMP_REQUEST_RECEIVED: 68204c06356bSdh case PMCOUT_GPIO: 68214c06356bSdh case PMCOUT_GPIO_EVENT: 68224c06356bSdh case PMCOUT_GET_TIME_STAMP: 68234c06356bSdh case PMCOUT_SKIP_ENTRIES: 68244c06356bSdh case PMCOUT_GET_NVMD_DATA: /* Actually lower 16 bits of word 3 */ 68254c06356bSdh case PMCOUT_SET_NVMD_DATA: /* but ignore - we don't use these */ 68264c06356bSdh case PMCOUT_DEVICE_HANDLE_REMOVED: 68274c06356bSdh case PMCOUT_SSP_REQUEST_RECEIVED: 68284c06356bSdh return; 68294c06356bSdh 68304c06356bSdh case PMCOUT_GENERAL_EVENT: 68314c06356bSdh offset = 1; 68324c06356bSdh break; 68334c06356bSdh 68344c06356bSdh case PMCOUT_SSP_COMPLETION: 68354c06356bSdh case PMCOUT_SMP_COMPLETION: 68364c06356bSdh case PMCOUT_DEVICE_REGISTRATION: 68374c06356bSdh case PMCOUT_DEREGISTER_DEVICE_HANDLE: 68384c06356bSdh case PMCOUT_SATA_COMPLETION: 68394c06356bSdh case PMCOUT_DEVICE_INFO: 68404c06356bSdh case PMCOUT_FW_FLASH_UPDATE: 68414c06356bSdh case PMCOUT_SSP_ABORT: 68424c06356bSdh case PMCOUT_SATA_ABORT: 68434c06356bSdh case PMCOUT_SAS_DIAG_MODE_START_END: 68444c06356bSdh case PMCOUT_SAS_HW_EVENT_ACK_ACK: 68454c06356bSdh case PMCOUT_SMP_ABORT: 68464c06356bSdh case PMCOUT_SET_DEVICE_STATE: 68474c06356bSdh case PMCOUT_GET_DEVICE_STATE: 68484c06356bSdh case PMCOUT_SET_DEVICE_INFO: 68494c06356bSdh offset = 2; 68504c06356bSdh break; 68514c06356bSdh 68524c06356bSdh case PMCOUT_LOCAL_PHY_CONTROL: 68534c06356bSdh case PMCOUT_SAS_DIAG_EXECUTE: 68544c06356bSdh case PMCOUT_PORT_CONTROL: 68554c06356bSdh offset = 3; 68564c06356bSdh break; 68574c06356bSdh 68584c06356bSdh case PMCOUT_GET_INFO: 68594c06356bSdh case PMCOUT_GET_VPD: 68604c06356bSdh case PMCOUT_SAS_ASSISTED_DISCOVERY_EVENT: 68614c06356bSdh case PMCOUT_SATA_ASSISTED_DISCOVERY_EVENT: 68624c06356bSdh case PMCOUT_SET_VPD: 68634c06356bSdh case PMCOUT_TWI: 68644c06356bSdh pmcs_print_entry(pwp, PMCS_PRT_DEBUG, 68654c06356bSdh "Got response for deprecated opcode", iomb); 68664c06356bSdh return; 68674c06356bSdh 68684c06356bSdh default: 68694c06356bSdh pmcs_print_entry(pwp, PMCS_PRT_DEBUG, 68704c06356bSdh "Got response for unknown opcode", iomb); 68714c06356bSdh return; 68724c06356bSdh } 68734c06356bSdh 68744c06356bSdh if (LE_32(iomb[offset]) != PMCOUT_STATUS_OK) { 68754c06356bSdh pmcs_print_entry(pwp, PMCS_PRT_DEBUG, 68764c06356bSdh "bad status on TAG_TYPE_NONE command", iomb); 68774c06356bSdh } 68784c06356bSdh } 68794c06356bSdh 68804c06356bSdh /* 68814c06356bSdh * Called with statlock held 68824c06356bSdh */ 68834c06356bSdh void 68844c06356bSdh pmcs_clear_xp(pmcs_hw_t *pwp, pmcs_xscsi_t *xp) 68854c06356bSdh { 68864c06356bSdh _NOTE(ARGUNUSED(pwp)); 68874c06356bSdh 68884c06356bSdh ASSERT(mutex_owned(&xp->statlock)); 68894c06356bSdh 6890c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, "%s: Device 0x%p is gone.", 6891c3bc407cSdh __func__, (void *)xp); 68924c06356bSdh 68934c06356bSdh /* 6894b18a19c2SJesse Butler * Clear the dip now. This keeps pmcs_remove_device from attempting 68954c06356bSdh * to call us on the same device while we're still flushing queues. 68964c06356bSdh * The only side effect is we can no longer update SM-HBA properties, 68974c06356bSdh * but this device is going away anyway, so no matter. 68984c06356bSdh */ 68994c06356bSdh xp->dip = NULL; 6900499cfd15SDavid Hollister xp->smpd = NULL; 69014c06356bSdh xp->special_running = 0; 69024c06356bSdh xp->recovering = 0; 69034c06356bSdh xp->recover_wait = 0; 69044c06356bSdh xp->draining = 0; 69054c06356bSdh xp->new = 0; 69064c06356bSdh xp->assigned = 0; 69074c06356bSdh xp->dev_state = 0; 69084c06356bSdh xp->tagmap = 0; 69094c06356bSdh xp->dev_gone = 1; 69104c06356bSdh xp->event_recovery = 0; 69114c06356bSdh xp->dtype = NOTHING; 69124c06356bSdh xp->wq_recovery_tail = NULL; 69134c06356bSdh /* Don't clear xp->phy */ 69144c06356bSdh /* Don't clear xp->actv_cnt */ 6915601c90f1SSrikanth, Ramana /* Don't clear xp->actv_pkts */ 6916b18a19c2SJesse Butler 6917b18a19c2SJesse Butler /* 6918b18a19c2SJesse Butler * Flush all target queues 6919b18a19c2SJesse Butler */ 6920b18a19c2SJesse Butler pmcs_flush_target_queues(pwp, xp, PMCS_TGT_ALL_QUEUES); 69214c06356bSdh } 69224c06356bSdh 69234c06356bSdh static int 69244c06356bSdh pmcs_smp_function_result(pmcs_hw_t *pwp, smp_response_frame_t *srf) 69254c06356bSdh { 69264c06356bSdh int result = srf->srf_result; 69274c06356bSdh 69284c06356bSdh switch (result) { 69294c06356bSdh case SMP_RES_UNKNOWN_FUNCTION: 6930c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6931c3bc407cSdh "%s: SMP DISCOVER Response " 69324c06356bSdh "Function Result: Unknown SMP Function(0x%x)", 69334c06356bSdh __func__, result); 69344c06356bSdh break; 69354c06356bSdh case SMP_RES_FUNCTION_FAILED: 6936c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6937c3bc407cSdh "%s: SMP DISCOVER Response " 69384c06356bSdh "Function Result: SMP Function Failed(0x%x)", 69394c06356bSdh __func__, result); 69404c06356bSdh break; 69414c06356bSdh case SMP_RES_INVALID_REQUEST_FRAME_LENGTH: 6942c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6943c3bc407cSdh "%s: SMP DISCOVER Response " 69444c06356bSdh "Function Result: Invalid Request Frame Length(0x%x)", 69454c06356bSdh __func__, result); 69464c06356bSdh break; 69474c06356bSdh case SMP_RES_INCOMPLETE_DESCRIPTOR_LIST: 6948c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6949c3bc407cSdh "%s: SMP DISCOVER Response " 69504c06356bSdh "Function Result: Incomplete Descriptor List(0x%x)", 69514c06356bSdh __func__, result); 69524c06356bSdh break; 69534c06356bSdh case SMP_RES_PHY_DOES_NOT_EXIST: 6954c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6955c3bc407cSdh "%s: SMP DISCOVER Response " 69564c06356bSdh "Function Result: PHY does not exist(0x%x)", 69574c06356bSdh __func__, result); 69584c06356bSdh break; 69594c06356bSdh case SMP_RES_PHY_VACANT: 6960c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6961c3bc407cSdh "%s: SMP DISCOVER Response " 69624c06356bSdh "Function Result: PHY Vacant(0x%x)", 69634c06356bSdh __func__, result); 69644c06356bSdh break; 69654c06356bSdh default: 6966c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6967c3bc407cSdh "%s: SMP DISCOVER Response " 69684c06356bSdh "Function Result: (0x%x)", 69694c06356bSdh __func__, result); 69704c06356bSdh break; 69714c06356bSdh } 69724c06356bSdh 69734c06356bSdh return (result); 69744c06356bSdh } 69754c06356bSdh 69764c06356bSdh /* 69774c06356bSdh * Do all the repetitive stuff necessary to setup for DMA 69784c06356bSdh * 69794c06356bSdh * pwp: Used for dip 69804c06356bSdh * dma_attr: ddi_dma_attr_t to use for the mapping 69814c06356bSdh * acch: ddi_acc_handle_t to use for the mapping 69824c06356bSdh * dmah: ddi_dma_handle_t to use 69834c06356bSdh * length: Amount of memory for mapping 6984af4c679fSSean McEnroe * kvap: Pointer filled in with kernel virtual address on successful return 69854c06356bSdh * dma_addr: Pointer filled in with DMA address on successful return 69864c06356bSdh */ 69874c06356bSdh boolean_t 69884c06356bSdh pmcs_dma_setup(pmcs_hw_t *pwp, ddi_dma_attr_t *dma_attr, ddi_acc_handle_t *acch, 6989af4c679fSSean McEnroe ddi_dma_handle_t *dmah, size_t length, caddr_t *kvap, uint64_t *dma_addr) 69904c06356bSdh { 69914c06356bSdh dev_info_t *dip = pwp->dip; 69924c06356bSdh ddi_dma_cookie_t cookie; 69934c06356bSdh size_t real_length; 69944c06356bSdh uint_t ddma_flag = DDI_DMA_CONSISTENT; 69954c06356bSdh uint_t ddabh_flag = DDI_DMA_CONSISTENT | DDI_DMA_RDWR; 69964c06356bSdh uint_t cookie_cnt; 69974c06356bSdh ddi_device_acc_attr_t mattr = { 69984c06356bSdh DDI_DEVICE_ATTR_V0, 69994c06356bSdh DDI_NEVERSWAP_ACC, 70004c06356bSdh DDI_STRICTORDER_ACC, 70014c06356bSdh DDI_DEFAULT_ACC 70024c06356bSdh }; 70034c06356bSdh 70044c06356bSdh *acch = NULL; 70054c06356bSdh *dmah = NULL; 70064c06356bSdh 70074c06356bSdh if (ddi_dma_alloc_handle(dip, dma_attr, DDI_DMA_SLEEP, NULL, dmah) != 70084c06356bSdh DDI_SUCCESS) { 7009c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 7010c3bc407cSdh "Failed to allocate DMA handle"); 70114c06356bSdh return (B_FALSE); 70124c06356bSdh } 70134c06356bSdh 70144c06356bSdh if (ddi_dma_mem_alloc(*dmah, length, &mattr, ddma_flag, DDI_DMA_SLEEP, 7015af4c679fSSean McEnroe NULL, kvap, &real_length, acch) != DDI_SUCCESS) { 7016c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 7017c3bc407cSdh "Failed to allocate DMA mem"); 70184c06356bSdh ddi_dma_free_handle(dmah); 70194c06356bSdh *dmah = NULL; 70204c06356bSdh return (B_FALSE); 70214c06356bSdh } 70224c06356bSdh 7023af4c679fSSean McEnroe if (ddi_dma_addr_bind_handle(*dmah, NULL, *kvap, real_length, 70244c06356bSdh ddabh_flag, DDI_DMA_SLEEP, NULL, &cookie, &cookie_cnt) 70254c06356bSdh != DDI_DMA_MAPPED) { 7026c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "Failed to bind DMA"); 70274c06356bSdh ddi_dma_free_handle(dmah); 70284c06356bSdh ddi_dma_mem_free(acch); 70294c06356bSdh *dmah = NULL; 70304c06356bSdh *acch = NULL; 70314c06356bSdh return (B_FALSE); 70324c06356bSdh } 70334c06356bSdh 70344c06356bSdh if (cookie_cnt != 1) { 7035c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "Multiple cookies"); 70364c06356bSdh if (ddi_dma_unbind_handle(*dmah) != DDI_SUCCESS) { 7037c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "Condition " 7038c3bc407cSdh "failed at %s():%d", __func__, __LINE__); 70394c06356bSdh } 70404c06356bSdh ddi_dma_free_handle(dmah); 70414c06356bSdh ddi_dma_mem_free(acch); 70424c06356bSdh *dmah = NULL; 70434c06356bSdh *acch = NULL; 70444c06356bSdh return (B_FALSE); 70454c06356bSdh } 70464c06356bSdh 70474c06356bSdh *dma_addr = cookie.dmac_laddress; 70484c06356bSdh 70494c06356bSdh return (B_TRUE); 70504c06356bSdh } 70514c06356bSdh 70524c06356bSdh /* 70534c06356bSdh * Flush requested queues for a particular target. Called with statlock held 70544c06356bSdh */ 70554c06356bSdh void 70564c06356bSdh pmcs_flush_target_queues(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt, uint8_t queues) 70574c06356bSdh { 70589aed1621SDavid Hollister pmcs_cmd_t *sp, *sp_next; 70594c06356bSdh pmcwork_t *pwrk; 70604c06356bSdh 70614c06356bSdh ASSERT(pwp != NULL); 70624c06356bSdh ASSERT(tgt != NULL); 70634c06356bSdh 7064c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, tgt, 70654c06356bSdh "%s: Flushing queues (%d) for target 0x%p", __func__, 70664c06356bSdh queues, (void *)tgt); 70674c06356bSdh 70684c06356bSdh /* 70694c06356bSdh * Commands on the wait queue (or the special queue below) don't have 70704c06356bSdh * work structures associated with them. 70714c06356bSdh */ 70724c06356bSdh if (queues & PMCS_TGT_WAIT_QUEUE) { 70734c06356bSdh mutex_enter(&tgt->wqlock); 70744c06356bSdh while ((sp = STAILQ_FIRST(&tgt->wq)) != NULL) { 70754c06356bSdh STAILQ_REMOVE(&tgt->wq, sp, pmcs_cmd, cmd_next); 7076c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, tgt, 70774c06356bSdh "%s: Removing cmd 0x%p from wq for target 0x%p", 70784c06356bSdh __func__, (void *)sp, (void *)tgt); 70794c06356bSdh CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE; 70804c06356bSdh CMD2PKT(sp)->pkt_state = STATE_GOT_BUS; 70814c06356bSdh mutex_exit(&tgt->wqlock); 70824c06356bSdh pmcs_dma_unload(pwp, sp); 70834c06356bSdh mutex_enter(&pwp->cq_lock); 70844c06356bSdh STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 70854c06356bSdh mutex_exit(&pwp->cq_lock); 70864c06356bSdh mutex_enter(&tgt->wqlock); 70874c06356bSdh } 70884c06356bSdh mutex_exit(&tgt->wqlock); 70894c06356bSdh } 70904c06356bSdh 70914c06356bSdh /* 70924c06356bSdh * Commands on the active queue will have work structures associated 70934c06356bSdh * with them. 70944c06356bSdh */ 70954c06356bSdh if (queues & PMCS_TGT_ACTIVE_QUEUE) { 709632b54db7SJesse Butler mutex_exit(&tgt->statlock); 70974c06356bSdh mutex_enter(&tgt->aqlock); 70989aed1621SDavid Hollister sp = STAILQ_FIRST(&tgt->aq); 70999aed1621SDavid Hollister while (sp) { 71009aed1621SDavid Hollister sp_next = STAILQ_NEXT(sp, cmd_next); 71014c06356bSdh pwrk = pmcs_tag2wp(pwp, sp->cmd_tag); 71029aed1621SDavid Hollister 71039aed1621SDavid Hollister /* 71049aed1621SDavid Hollister * If we don't find a work structure, it's because 71059aed1621SDavid Hollister * the command is already complete. If so, move on 71069aed1621SDavid Hollister * to the next one. 71079aed1621SDavid Hollister */ 71089aed1621SDavid Hollister if (pwrk == NULL) { 71099aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG1, tgt->phy, tgt, 71109aed1621SDavid Hollister "%s: Not removing cmd 0x%p (htag 0x%x) " 71119aed1621SDavid Hollister "from aq", __func__, (void *)sp, 71129aed1621SDavid Hollister sp->cmd_tag); 71139aed1621SDavid Hollister sp = sp_next; 71149aed1621SDavid Hollister continue; 71159aed1621SDavid Hollister } 71169aed1621SDavid Hollister 71179aed1621SDavid Hollister STAILQ_REMOVE(&tgt->aq, sp, pmcs_cmd, cmd_next); 71189aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG1, tgt->phy, tgt, 71199aed1621SDavid Hollister "%s: Removing cmd 0x%p (htag 0x%x) from aq for " 71209aed1621SDavid Hollister "target 0x%p", __func__, (void *)sp, sp->cmd_tag, 71219aed1621SDavid Hollister (void *)tgt); 71224c06356bSdh mutex_exit(&tgt->aqlock); 71234c06356bSdh /* 71249aed1621SDavid Hollister * Mark the work structure as dead and complete it 71254c06356bSdh */ 71269aed1621SDavid Hollister pwrk->dead = 1; 71279aed1621SDavid Hollister CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE; 71289aed1621SDavid Hollister CMD2PKT(sp)->pkt_state = STATE_GOT_BUS; 71299aed1621SDavid Hollister pmcs_complete_work_impl(pwp, pwrk, NULL, 0); 71304c06356bSdh pmcs_dma_unload(pwp, sp); 71314c06356bSdh mutex_enter(&pwp->cq_lock); 71324c06356bSdh STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 71334c06356bSdh mutex_exit(&pwp->cq_lock); 71344c06356bSdh mutex_enter(&tgt->aqlock); 71359aed1621SDavid Hollister sp = sp_next; 71364c06356bSdh } 71374c06356bSdh mutex_exit(&tgt->aqlock); 713832b54db7SJesse Butler mutex_enter(&tgt->statlock); 71394c06356bSdh } 71404c06356bSdh 71414c06356bSdh if (queues & PMCS_TGT_SPECIAL_QUEUE) { 71424c06356bSdh while ((sp = STAILQ_FIRST(&tgt->sq)) != NULL) { 71434c06356bSdh STAILQ_REMOVE(&tgt->sq, sp, pmcs_cmd, cmd_next); 71449aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG1, tgt->phy, tgt, 71454c06356bSdh "%s: Removing cmd 0x%p from sq for target 0x%p", 71464c06356bSdh __func__, (void *)sp, (void *)tgt); 71474c06356bSdh CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE; 71484c06356bSdh CMD2PKT(sp)->pkt_state = STATE_GOT_BUS; 71494c06356bSdh pmcs_dma_unload(pwp, sp); 71504c06356bSdh mutex_enter(&pwp->cq_lock); 71514c06356bSdh STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 71524c06356bSdh mutex_exit(&pwp->cq_lock); 71534c06356bSdh } 71544c06356bSdh } 71554c06356bSdh } 71564c06356bSdh 71574c06356bSdh void 71584c06356bSdh pmcs_complete_work_impl(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *iomb, 71594c06356bSdh size_t amt) 71604c06356bSdh { 71614c06356bSdh switch (PMCS_TAG_TYPE(pwrk->htag)) { 71624c06356bSdh case PMCS_TAG_TYPE_CBACK: 71634c06356bSdh { 71644c06356bSdh pmcs_cb_t callback = (pmcs_cb_t)pwrk->ptr; 71654c06356bSdh (*callback)(pwp, pwrk, iomb); 71664c06356bSdh break; 71674c06356bSdh } 71684c06356bSdh case PMCS_TAG_TYPE_WAIT: 71694c06356bSdh if (pwrk->arg && iomb && amt) { 71704c06356bSdh (void) memcpy(pwrk->arg, iomb, amt); 71714c06356bSdh } 71724c06356bSdh cv_signal(&pwrk->sleep_cv); 71734c06356bSdh mutex_exit(&pwrk->lock); 71744c06356bSdh break; 71754c06356bSdh case PMCS_TAG_TYPE_NONE: 71764c06356bSdh #ifdef DEBUG 71774c06356bSdh pmcs_check_iomb_status(pwp, iomb); 71784c06356bSdh #endif 71794c06356bSdh pmcs_pwork(pwp, pwrk); 71804c06356bSdh break; 71814c06356bSdh default: 71824c06356bSdh /* 71834c06356bSdh * We will leak a structure here if we don't know 71844c06356bSdh * what happened 71854c06356bSdh */ 7186c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 7187c3bc407cSdh "%s: Unknown PMCS_TAG_TYPE (%x)", 71884c06356bSdh __func__, PMCS_TAG_TYPE(pwrk->htag)); 71894c06356bSdh break; 71904c06356bSdh } 71914c06356bSdh } 71924c06356bSdh 71934c06356bSdh /* 71944c06356bSdh * Determine if iport still has targets. During detach(9E), if SCSA is 71954c06356bSdh * successfull in its guarantee of tran_tgt_free(9E) before detach(9E), 71964c06356bSdh * this should always return B_FALSE. 71974c06356bSdh */ 71984c06356bSdh boolean_t 71994c06356bSdh pmcs_iport_has_targets(pmcs_hw_t *pwp, pmcs_iport_t *iport) 72004c06356bSdh { 72014c06356bSdh pmcs_xscsi_t *xp; 72024c06356bSdh int i; 72034c06356bSdh 72044c06356bSdh mutex_enter(&pwp->lock); 72054c06356bSdh 72064c06356bSdh if (!pwp->targets || !pwp->max_dev) { 72074c06356bSdh mutex_exit(&pwp->lock); 72084c06356bSdh return (B_FALSE); 72094c06356bSdh } 72104c06356bSdh 72114c06356bSdh for (i = 0; i < pwp->max_dev; i++) { 72124c06356bSdh xp = pwp->targets[i]; 72134c06356bSdh if ((xp == NULL) || (xp->phy == NULL) || 72144c06356bSdh (xp->phy->iport != iport)) { 72154c06356bSdh continue; 72164c06356bSdh } 72174c06356bSdh 72184c06356bSdh mutex_exit(&pwp->lock); 72194c06356bSdh return (B_TRUE); 72204c06356bSdh } 72214c06356bSdh 72224c06356bSdh mutex_exit(&pwp->lock); 72234c06356bSdh return (B_FALSE); 72244c06356bSdh } 72254c06356bSdh 72264c06356bSdh /* 72274c06356bSdh * Called with softstate lock held 72284c06356bSdh */ 72294c06356bSdh void 72304c06356bSdh pmcs_destroy_target(pmcs_xscsi_t *target) 72314c06356bSdh { 72324c06356bSdh pmcs_hw_t *pwp = target->pwp; 72334c06356bSdh pmcs_iport_t *iport; 72344c06356bSdh 72354c06356bSdh ASSERT(pwp); 72364c06356bSdh ASSERT(mutex_owned(&pwp->lock)); 72374c06356bSdh 72384c06356bSdh if (!target->ua) { 7239c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, target, 7240c3bc407cSdh "%s: target %p iport address is null", 72414c06356bSdh __func__, (void *)target); 72424c06356bSdh } 72434c06356bSdh 72444c06356bSdh iport = pmcs_get_iport_by_ua(pwp, target->ua); 72454c06356bSdh if (iport == NULL) { 7246c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, target, 72474c06356bSdh "%s: no iport associated with tgt(0x%p)", 72484c06356bSdh __func__, (void *)target); 72494c06356bSdh return; 72504c06356bSdh } 72514c06356bSdh 7252c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, target, 72534c06356bSdh "%s: free target %p", __func__, (void *)target); 72544c06356bSdh if (target->ua) { 72554c06356bSdh strfree(target->ua); 72564c06356bSdh } 72574c06356bSdh 72584c06356bSdh mutex_destroy(&target->wqlock); 72594c06356bSdh mutex_destroy(&target->aqlock); 72604c06356bSdh mutex_destroy(&target->statlock); 72614c06356bSdh cv_destroy(&target->reset_cv); 72624c06356bSdh cv_destroy(&target->abort_cv); 72634c06356bSdh ddi_soft_state_bystr_fini(&target->lun_sstate); 72644c06356bSdh ddi_soft_state_bystr_free(iport->tgt_sstate, target->unit_address); 72654c06356bSdh pmcs_rele_iport(iport); 72664c06356bSdh } 72674c06356bSdh 72684c06356bSdh /* 72694c06356bSdh * pmcs_lock_phy_impl 72704c06356bSdh * 72714c06356bSdh * This function is what does the actual work for pmcs_lock_phy. It will 72724c06356bSdh * lock all PHYs from phyp down in a top-down fashion. 72734c06356bSdh * 72744c06356bSdh * Locking notes: 72754c06356bSdh * 1. level starts from 0 for the PHY ("parent") that's passed in. It is 72764c06356bSdh * not a reflection of the actual level of the PHY in the SAS topology. 72774c06356bSdh * 2. If parent is an expander, then parent is locked along with all its 72784c06356bSdh * descendents. 72794c06356bSdh * 3. Expander subsidiary PHYs at level 0 are not locked. It is the 72804c06356bSdh * responsibility of the caller to individually lock expander subsidiary PHYs 72814c06356bSdh * at level 0 if necessary. 72824c06356bSdh * 4. Siblings at level 0 are not traversed due to the possibility that we're 72834c06356bSdh * locking a PHY on the dead list. The siblings could be pointing to invalid 72844c06356bSdh * PHYs. We don't lock siblings at level 0 anyway. 72854c06356bSdh */ 72864c06356bSdh static void 72874c06356bSdh pmcs_lock_phy_impl(pmcs_phy_t *phyp, int level) 72884c06356bSdh { 72894c06356bSdh pmcs_phy_t *tphyp; 72904c06356bSdh 72914c06356bSdh ASSERT((phyp->dtype == SAS) || (phyp->dtype == SATA) || 72924c06356bSdh (phyp->dtype == EXPANDER) || (phyp->dtype == NOTHING)); 72934c06356bSdh 72944c06356bSdh /* 72954c06356bSdh * Start walking the PHYs. 72964c06356bSdh */ 72974c06356bSdh tphyp = phyp; 72984c06356bSdh while (tphyp) { 72994c06356bSdh /* 73004c06356bSdh * If we're at the top level, only lock ourselves. For anything 73014c06356bSdh * at level > 0, traverse children while locking everything. 73024c06356bSdh */ 73034c06356bSdh if ((level > 0) || (tphyp == phyp)) { 7304c3bc407cSdh pmcs_prt(tphyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, tphyp, 7305c3bc407cSdh NULL, "%s: PHY 0x%p parent 0x%p path %s lvl %d", 73064c06356bSdh __func__, (void *)tphyp, (void *)tphyp->parent, 73074c06356bSdh tphyp->path, level); 73084c06356bSdh mutex_enter(&tphyp->phy_lock); 73094c06356bSdh 73104c06356bSdh if (tphyp->children) { 73114c06356bSdh pmcs_lock_phy_impl(tphyp->children, level + 1); 73124c06356bSdh } 73134c06356bSdh } 73144c06356bSdh 73154c06356bSdh if (level == 0) { 73164c06356bSdh return; 73174c06356bSdh } 73184c06356bSdh 73194c06356bSdh tphyp = tphyp->sibling; 73204c06356bSdh } 73214c06356bSdh } 73224c06356bSdh 73234c06356bSdh /* 73244c06356bSdh * pmcs_lock_phy 73254c06356bSdh * 73264c06356bSdh * This function is responsible for locking a PHY and all its descendents 73274c06356bSdh */ 73284c06356bSdh void 73294c06356bSdh pmcs_lock_phy(pmcs_phy_t *phyp) 73304c06356bSdh { 73314c06356bSdh #ifdef DEBUG 73324c06356bSdh char *callername = NULL; 73334c06356bSdh ulong_t off; 73344c06356bSdh 73354c06356bSdh ASSERT(phyp != NULL); 73364c06356bSdh 73374c06356bSdh callername = modgetsymname((uintptr_t)caller(), &off); 73384c06356bSdh 73394c06356bSdh if (callername == NULL) { 7340c3bc407cSdh pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL, 73414c06356bSdh "%s: PHY 0x%p path %s caller: unknown", __func__, 73424c06356bSdh (void *)phyp, phyp->path); 73434c06356bSdh } else { 7344c3bc407cSdh pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL, 73454c06356bSdh "%s: PHY 0x%p path %s caller: %s+%lx", __func__, 73464c06356bSdh (void *)phyp, phyp->path, callername, off); 73474c06356bSdh } 73484c06356bSdh #else 7349c3bc407cSdh pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL, 73504c06356bSdh "%s: PHY 0x%p path %s", __func__, (void *)phyp, phyp->path); 73514c06356bSdh #endif 73524c06356bSdh pmcs_lock_phy_impl(phyp, 0); 73534c06356bSdh } 73544c06356bSdh 73554c06356bSdh /* 73564c06356bSdh * pmcs_unlock_phy_impl 73574c06356bSdh * 73584c06356bSdh * Unlock all PHYs from phyp down in a bottom-up fashion. 73594c06356bSdh */ 73604c06356bSdh static void 73614c06356bSdh pmcs_unlock_phy_impl(pmcs_phy_t *phyp, int level) 73624c06356bSdh { 73634c06356bSdh pmcs_phy_t *phy_next; 73644c06356bSdh 73654c06356bSdh ASSERT((phyp->dtype == SAS) || (phyp->dtype == SATA) || 73664c06356bSdh (phyp->dtype == EXPANDER) || (phyp->dtype == NOTHING)); 73674c06356bSdh 73684c06356bSdh /* 73694c06356bSdh * Recurse down to the bottom PHYs 73704c06356bSdh */ 73714c06356bSdh if (level == 0) { 73724c06356bSdh if (phyp->children) { 73734c06356bSdh pmcs_unlock_phy_impl(phyp->children, level + 1); 73744c06356bSdh } 73754c06356bSdh } else { 73764c06356bSdh phy_next = phyp; 73774c06356bSdh while (phy_next) { 73784c06356bSdh if (phy_next->children) { 73794c06356bSdh pmcs_unlock_phy_impl(phy_next->children, 73804c06356bSdh level + 1); 73814c06356bSdh } 73824c06356bSdh phy_next = phy_next->sibling; 73834c06356bSdh } 73844c06356bSdh } 73854c06356bSdh 73864c06356bSdh /* 73874c06356bSdh * Iterate through PHYs unlocking all at level > 0 as well the top PHY 73884c06356bSdh */ 73894c06356bSdh phy_next = phyp; 73904c06356bSdh while (phy_next) { 73914c06356bSdh if ((level > 0) || (phy_next == phyp)) { 73924c06356bSdh pmcs_prt(phy_next->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, 7393c3bc407cSdh phy_next, NULL, 73944c06356bSdh "%s: PHY 0x%p parent 0x%p path %s lvl %d", 73954c06356bSdh __func__, (void *)phy_next, 73964c06356bSdh (void *)phy_next->parent, phy_next->path, level); 73974c06356bSdh mutex_exit(&phy_next->phy_lock); 73984c06356bSdh } 73994c06356bSdh 74004c06356bSdh if (level == 0) { 74014c06356bSdh return; 74024c06356bSdh } 74034c06356bSdh 74044c06356bSdh phy_next = phy_next->sibling; 74054c06356bSdh } 74064c06356bSdh } 74074c06356bSdh 74084c06356bSdh /* 74094c06356bSdh * pmcs_unlock_phy 74104c06356bSdh * 74114c06356bSdh * Unlock a PHY and all its descendents 74124c06356bSdh */ 74134c06356bSdh void 74144c06356bSdh pmcs_unlock_phy(pmcs_phy_t *phyp) 74154c06356bSdh { 74164c06356bSdh #ifdef DEBUG 74174c06356bSdh char *callername = NULL; 74184c06356bSdh ulong_t off; 74194c06356bSdh 74204c06356bSdh ASSERT(phyp != NULL); 74214c06356bSdh 74224c06356bSdh callername = modgetsymname((uintptr_t)caller(), &off); 74234c06356bSdh 74244c06356bSdh if (callername == NULL) { 7425c3bc407cSdh pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL, 74264c06356bSdh "%s: PHY 0x%p path %s caller: unknown", __func__, 74274c06356bSdh (void *)phyp, phyp->path); 74284c06356bSdh } else { 7429c3bc407cSdh pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL, 74304c06356bSdh "%s: PHY 0x%p path %s caller: %s+%lx", __func__, 74314c06356bSdh (void *)phyp, phyp->path, callername, off); 74324c06356bSdh } 74334c06356bSdh #else 7434c3bc407cSdh pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL, 74354c06356bSdh "%s: PHY 0x%p path %s", __func__, (void *)phyp, phyp->path); 74364c06356bSdh #endif 74374c06356bSdh pmcs_unlock_phy_impl(phyp, 0); 74384c06356bSdh } 74394c06356bSdh 74404c06356bSdh /* 74414c06356bSdh * pmcs_get_root_phy 74424c06356bSdh * 74434c06356bSdh * For a given phy pointer return its root phy. 74449aed1621SDavid Hollister * This function must only be called during discovery in order to ensure that 74459aed1621SDavid Hollister * the chain of PHYs from phyp up to the root PHY doesn't change. 74464c06356bSdh */ 74474c06356bSdh pmcs_phy_t * 74484c06356bSdh pmcs_get_root_phy(pmcs_phy_t *phyp) 74494c06356bSdh { 74504c06356bSdh ASSERT(phyp); 74514c06356bSdh 74524c06356bSdh while (phyp) { 74534c06356bSdh if (IS_ROOT_PHY(phyp)) { 74544c06356bSdh break; 74554c06356bSdh } 74564c06356bSdh phyp = phyp->parent; 74574c06356bSdh } 74584c06356bSdh 74594c06356bSdh return (phyp); 74604c06356bSdh } 74614c06356bSdh 74624c06356bSdh /* 74634c06356bSdh * pmcs_free_dma_chunklist 74644c06356bSdh * 74654c06356bSdh * Free DMA S/G chunk list 74664c06356bSdh */ 74674c06356bSdh void 74684c06356bSdh pmcs_free_dma_chunklist(pmcs_hw_t *pwp) 74694c06356bSdh { 74704c06356bSdh pmcs_chunk_t *pchunk; 74714c06356bSdh 74724c06356bSdh while (pwp->dma_chunklist) { 74734c06356bSdh pchunk = pwp->dma_chunklist; 74744c06356bSdh pwp->dma_chunklist = pwp->dma_chunklist->next; 74754c06356bSdh if (pchunk->dma_handle) { 74764c06356bSdh if (ddi_dma_unbind_handle(pchunk->dma_handle) != 74774c06356bSdh DDI_SUCCESS) { 7478c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 7479c3bc407cSdh "Condition failed at %s():%d", 7480c3bc407cSdh __func__, __LINE__); 74814c06356bSdh } 74824c06356bSdh ddi_dma_free_handle(&pchunk->dma_handle); 74834c06356bSdh ddi_dma_mem_free(&pchunk->acc_handle); 74844c06356bSdh } 74854c06356bSdh kmem_free(pchunk, sizeof (pmcs_chunk_t)); 74864c06356bSdh } 74874c06356bSdh } 74884c06356bSdh 74894c06356bSdh /*ARGSUSED2*/ 74904c06356bSdh int 74914c06356bSdh pmcs_phy_constructor(void *buf, void *arg, int kmflags) 74924c06356bSdh { 74934c06356bSdh pmcs_hw_t *pwp = (pmcs_hw_t *)arg; 74944c06356bSdh pmcs_phy_t *phyp = (pmcs_phy_t *)buf; 74954c06356bSdh 74964c06356bSdh mutex_init(&phyp->phy_lock, NULL, MUTEX_DRIVER, 74974c06356bSdh DDI_INTR_PRI(pwp->intr_pri)); 74984c06356bSdh cv_init(&phyp->abort_all_cv, NULL, CV_DRIVER, NULL); 74994c06356bSdh return (0); 75004c06356bSdh } 75014c06356bSdh 75024c06356bSdh /*ARGSUSED1*/ 75034c06356bSdh void 75044c06356bSdh pmcs_phy_destructor(void *buf, void *arg) 75054c06356bSdh { 75064c06356bSdh pmcs_phy_t *phyp = (pmcs_phy_t *)buf; 75074c06356bSdh 75084c06356bSdh cv_destroy(&phyp->abort_all_cv); 75094c06356bSdh mutex_destroy(&phyp->phy_lock); 75104c06356bSdh } 75114c06356bSdh 75124c06356bSdh /* 75134c06356bSdh * Free all PHYs from the kmem_cache starting at phyp as well as everything 75144c06356bSdh * on the dead_phys list. 75154c06356bSdh * 75164c06356bSdh * NOTE: This function does not free root PHYs as they are not allocated 75174c06356bSdh * from the kmem_cache. 75184c06356bSdh * 75194c06356bSdh * No PHY locks are acquired as this should only be called during DDI_DETACH 75204c06356bSdh * or soft reset (while pmcs interrupts are disabled). 75214c06356bSdh */ 75224c06356bSdh void 75234c06356bSdh pmcs_free_all_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp) 75244c06356bSdh { 75254c06356bSdh pmcs_phy_t *tphyp, *nphyp; 75264c06356bSdh 75274c06356bSdh if (phyp == NULL) { 75284c06356bSdh return; 75294c06356bSdh } 75304c06356bSdh 75314c06356bSdh tphyp = phyp; 75324c06356bSdh while (tphyp) { 75334c06356bSdh nphyp = tphyp->sibling; 75344c06356bSdh 75354c06356bSdh if (tphyp->children) { 75364c06356bSdh pmcs_free_all_phys(pwp, tphyp->children); 75374c06356bSdh tphyp->children = NULL; 75384c06356bSdh } 75394c06356bSdh if (!IS_ROOT_PHY(tphyp)) { 75404c06356bSdh kmem_cache_free(pwp->phy_cache, tphyp); 75414c06356bSdh } 75424c06356bSdh 75434c06356bSdh tphyp = nphyp; 75444c06356bSdh } 75454c06356bSdh 75464c06356bSdh tphyp = pwp->dead_phys; 75474c06356bSdh while (tphyp) { 75484c06356bSdh nphyp = tphyp->sibling; 75494c06356bSdh kmem_cache_free(pwp->phy_cache, tphyp); 75504c06356bSdh tphyp = nphyp; 75514c06356bSdh } 75524c06356bSdh pwp->dead_phys = NULL; 75534c06356bSdh } 75544c06356bSdh 75554c06356bSdh /* 75564c06356bSdh * Free a list of PHYs linked together by the sibling pointer back to the 75574c06356bSdh * kmem cache from whence they came. This function does not recurse, so the 75584c06356bSdh * caller must ensure there are no children. 75594c06356bSdh */ 75604c06356bSdh void 75614c06356bSdh pmcs_free_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp) 75624c06356bSdh { 75634c06356bSdh pmcs_phy_t *next_phy; 75644c06356bSdh 75654c06356bSdh while (phyp) { 75664c06356bSdh next_phy = phyp->sibling; 75674c06356bSdh ASSERT(!mutex_owned(&phyp->phy_lock)); 75684c06356bSdh kmem_cache_free(pwp->phy_cache, phyp); 75694c06356bSdh phyp = next_phy; 75704c06356bSdh } 75714c06356bSdh } 75724c06356bSdh 75734c06356bSdh /* 75744c06356bSdh * Make a copy of an existing PHY structure. This is used primarily in 75754c06356bSdh * discovery to compare the contents of an existing PHY with what gets 75764c06356bSdh * reported back by an expander. 75774c06356bSdh * 75784c06356bSdh * This function must not be called from any context where sleeping is 75794c06356bSdh * not possible. 75804c06356bSdh * 75814c06356bSdh * The new PHY is returned unlocked. 75824c06356bSdh */ 75834c06356bSdh static pmcs_phy_t * 75844c06356bSdh pmcs_clone_phy(pmcs_phy_t *orig_phy) 75854c06356bSdh { 75864c06356bSdh pmcs_phy_t *local; 75874c06356bSdh 75884c06356bSdh local = kmem_cache_alloc(orig_phy->pwp->phy_cache, KM_SLEEP); 75894c06356bSdh 75904c06356bSdh /* 75914c06356bSdh * Go ahead and just copy everything... 75924c06356bSdh */ 75934c06356bSdh *local = *orig_phy; 75944c06356bSdh 75954c06356bSdh /* 75964c06356bSdh * But the following must be set appropriately for this copy 75974c06356bSdh */ 75984c06356bSdh local->sibling = NULL; 75994c06356bSdh local->children = NULL; 76004c06356bSdh mutex_init(&local->phy_lock, NULL, MUTEX_DRIVER, 76014c06356bSdh DDI_INTR_PRI(orig_phy->pwp->intr_pri)); 76024c06356bSdh 76034c06356bSdh return (local); 76044c06356bSdh } 76054c06356bSdh 76064c06356bSdh int 76074c06356bSdh pmcs_check_acc_handle(ddi_acc_handle_t handle) 76084c06356bSdh { 76094c06356bSdh ddi_fm_error_t de; 76104c06356bSdh 76114c06356bSdh if (handle == NULL) { 76124c06356bSdh return (DDI_FAILURE); 76134c06356bSdh } 76144c06356bSdh ddi_fm_acc_err_get(handle, &de, DDI_FME_VER0); 76154c06356bSdh return (de.fme_status); 76164c06356bSdh } 76174c06356bSdh 76184c06356bSdh int 76194c06356bSdh pmcs_check_dma_handle(ddi_dma_handle_t handle) 76204c06356bSdh { 76214c06356bSdh ddi_fm_error_t de; 76224c06356bSdh 76234c06356bSdh if (handle == NULL) { 76244c06356bSdh return (DDI_FAILURE); 76254c06356bSdh } 76264c06356bSdh ddi_fm_dma_err_get(handle, &de, DDI_FME_VER0); 76274c06356bSdh return (de.fme_status); 76284c06356bSdh } 76294c06356bSdh 76304c06356bSdh 76314c06356bSdh void 76324c06356bSdh pmcs_fm_ereport(pmcs_hw_t *pwp, char *detail) 76334c06356bSdh { 76344c06356bSdh uint64_t ena; 76354c06356bSdh char buf[FM_MAX_CLASS]; 76364c06356bSdh 76374c06356bSdh (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", DDI_FM_DEVICE, detail); 76384c06356bSdh ena = fm_ena_generate(0, FM_ENA_FMT1); 76394c06356bSdh if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities)) { 76404c06356bSdh ddi_fm_ereport_post(pwp->dip, buf, ena, DDI_NOSLEEP, 76414c06356bSdh FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, NULL); 76424c06356bSdh } 76434c06356bSdh } 76444c06356bSdh 76454c06356bSdh int 76464c06356bSdh pmcs_check_acc_dma_handle(pmcs_hw_t *pwp) 76474c06356bSdh { 76484c06356bSdh pmcs_chunk_t *pchunk; 76494c06356bSdh int i; 76504c06356bSdh 76514c06356bSdh /* check all acc & dma handles allocated in attach */ 76524c06356bSdh if ((pmcs_check_acc_handle(pwp->pci_acc_handle) != DDI_SUCCESS) || 76534c06356bSdh (pmcs_check_acc_handle(pwp->msg_acc_handle) != DDI_SUCCESS) || 76544c06356bSdh (pmcs_check_acc_handle(pwp->top_acc_handle) != DDI_SUCCESS) || 76554c06356bSdh (pmcs_check_acc_handle(pwp->mpi_acc_handle) != DDI_SUCCESS) || 76564c06356bSdh (pmcs_check_acc_handle(pwp->gsm_acc_handle) != DDI_SUCCESS)) { 76574c06356bSdh goto check_failed; 76584c06356bSdh } 76594c06356bSdh 76604c06356bSdh for (i = 0; i < PMCS_NIQ; i++) { 76614c06356bSdh if ((pmcs_check_dma_handle( 76624c06356bSdh pwp->iqp_handles[i]) != DDI_SUCCESS) || 76634c06356bSdh (pmcs_check_acc_handle( 76644c06356bSdh pwp->iqp_acchdls[i]) != DDI_SUCCESS)) { 76654c06356bSdh goto check_failed; 76664c06356bSdh } 76674c06356bSdh } 76684c06356bSdh 76694c06356bSdh for (i = 0; i < PMCS_NOQ; i++) { 76704c06356bSdh if ((pmcs_check_dma_handle( 76714c06356bSdh pwp->oqp_handles[i]) != DDI_SUCCESS) || 76724c06356bSdh (pmcs_check_acc_handle( 76734c06356bSdh pwp->oqp_acchdls[i]) != DDI_SUCCESS)) { 76744c06356bSdh goto check_failed; 76754c06356bSdh } 76764c06356bSdh } 76774c06356bSdh 76784c06356bSdh if ((pmcs_check_dma_handle(pwp->cip_handles) != DDI_SUCCESS) || 76794c06356bSdh (pmcs_check_acc_handle(pwp->cip_acchdls) != DDI_SUCCESS)) { 76804c06356bSdh goto check_failed; 76814c06356bSdh } 76824c06356bSdh 76834c06356bSdh if (pwp->fwlog && 76844c06356bSdh ((pmcs_check_dma_handle(pwp->fwlog_hndl) != DDI_SUCCESS) || 76854c06356bSdh (pmcs_check_acc_handle(pwp->fwlog_acchdl) != DDI_SUCCESS))) { 76864c06356bSdh goto check_failed; 76874c06356bSdh } 76884c06356bSdh 76894c06356bSdh if (pwp->regdump_hndl && pwp->regdump_acchdl && 76904c06356bSdh ((pmcs_check_dma_handle(pwp->regdump_hndl) != DDI_SUCCESS) || 76914c06356bSdh (pmcs_check_acc_handle(pwp->regdump_acchdl) 76924c06356bSdh != DDI_SUCCESS))) { 76934c06356bSdh goto check_failed; 76944c06356bSdh } 76954c06356bSdh 76964c06356bSdh 76974c06356bSdh pchunk = pwp->dma_chunklist; 76984c06356bSdh while (pchunk) { 76994c06356bSdh if ((pmcs_check_acc_handle(pchunk->acc_handle) 77004c06356bSdh != DDI_SUCCESS) || 77014c06356bSdh (pmcs_check_dma_handle(pchunk->dma_handle) 77024c06356bSdh != DDI_SUCCESS)) { 77034c06356bSdh goto check_failed; 77044c06356bSdh } 77054c06356bSdh pchunk = pchunk->next; 77064c06356bSdh } 77074c06356bSdh 77084c06356bSdh return (0); 77094c06356bSdh 77104c06356bSdh check_failed: 77114c06356bSdh 77124c06356bSdh return (1); 77134c06356bSdh } 77144c06356bSdh 77154c06356bSdh /* 77164c06356bSdh * pmcs_handle_dead_phys 77174c06356bSdh * 77184c06356bSdh * If the PHY has no outstanding work associated with it, remove it from 77194c06356bSdh * the dead PHY list and free it. 77204c06356bSdh * 77214c06356bSdh * If pwp->ds_err_recovering or pwp->configuring is set, don't run. 77224c06356bSdh * This keeps routines that need to submit work to the chip from having to 77234c06356bSdh * hold PHY locks to ensure that PHYs don't disappear while they do their work. 77244c06356bSdh */ 77254c06356bSdh void 77264c06356bSdh pmcs_handle_dead_phys(pmcs_hw_t *pwp) 77274c06356bSdh { 77284c06356bSdh pmcs_phy_t *phyp, *nphyp, *pphyp; 77294c06356bSdh 77304c06356bSdh mutex_enter(&pwp->lock); 77314c06356bSdh mutex_enter(&pwp->config_lock); 77324c06356bSdh 77334c06356bSdh if (pwp->configuring | pwp->ds_err_recovering) { 77344c06356bSdh mutex_exit(&pwp->config_lock); 77354c06356bSdh mutex_exit(&pwp->lock); 77364c06356bSdh return; 77374c06356bSdh } 77384c06356bSdh 77394c06356bSdh /* 77404c06356bSdh * Check every PHY in the dead PHY list 77414c06356bSdh */ 77424c06356bSdh mutex_enter(&pwp->dead_phylist_lock); 77434c06356bSdh phyp = pwp->dead_phys; 77444c06356bSdh pphyp = NULL; /* Set previous PHY to NULL */ 77454c06356bSdh 77464c06356bSdh while (phyp != NULL) { 77474c06356bSdh pmcs_lock_phy(phyp); 77484c06356bSdh ASSERT(phyp->dead); 77494c06356bSdh 77504c06356bSdh nphyp = phyp->dead_next; 77514c06356bSdh 77524c06356bSdh /* 77534c06356bSdh * Check for outstanding work 77544c06356bSdh */ 77554c06356bSdh if (phyp->ref_count > 0) { 77564c06356bSdh pmcs_unlock_phy(phyp); 77574c06356bSdh pphyp = phyp; /* This PHY becomes "previous" */ 77584c06356bSdh } else if (phyp->target) { 77594c06356bSdh pmcs_unlock_phy(phyp); 7760c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG1, phyp, phyp->target, 77614c06356bSdh "%s: Not freeing PHY 0x%p: target 0x%p is not free", 77624c06356bSdh __func__, (void *)phyp, (void *)phyp->target); 77634c06356bSdh pphyp = phyp; 77644c06356bSdh } else { 77654c06356bSdh /* 77664c06356bSdh * No outstanding work or target references. Remove it 77674c06356bSdh * from the list and free it 77684c06356bSdh */ 7769c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, phyp->target, 77704c06356bSdh "%s: Freeing inactive dead PHY 0x%p @ %s " 77714c06356bSdh "target = 0x%p", __func__, (void *)phyp, 77724c06356bSdh phyp->path, (void *)phyp->target); 77734c06356bSdh /* 77744c06356bSdh * If pphyp is NULL, then phyp was the head of the list, 77754c06356bSdh * so just reset the head to nphyp. Otherwise, the 77764c06356bSdh * previous PHY will now point to nphyp (the next PHY) 77774c06356bSdh */ 77784c06356bSdh if (pphyp == NULL) { 77794c06356bSdh pwp->dead_phys = nphyp; 77804c06356bSdh } else { 77814c06356bSdh pphyp->dead_next = nphyp; 77824c06356bSdh } 77834c06356bSdh /* 77844c06356bSdh * If the target still points to this PHY, remove 77854c06356bSdh * that linkage now. 77864c06356bSdh */ 77874c06356bSdh if (phyp->target) { 77884c06356bSdh mutex_enter(&phyp->target->statlock); 77894c06356bSdh if (phyp->target->phy == phyp) { 77904c06356bSdh phyp->target->phy = NULL; 77914c06356bSdh } 77924c06356bSdh mutex_exit(&phyp->target->statlock); 77934c06356bSdh } 7794b18a19c2SJesse Butler pmcs_unlock_phy(phyp); 77954c06356bSdh kmem_cache_free(pwp->phy_cache, phyp); 77964c06356bSdh } 77974c06356bSdh 77984c06356bSdh phyp = nphyp; 77994c06356bSdh } 78004c06356bSdh 78014c06356bSdh mutex_exit(&pwp->dead_phylist_lock); 78024c06356bSdh mutex_exit(&pwp->config_lock); 78034c06356bSdh mutex_exit(&pwp->lock); 78044c06356bSdh } 78054c06356bSdh 78064c06356bSdh void 78074c06356bSdh pmcs_inc_phy_ref_count(pmcs_phy_t *phyp) 78084c06356bSdh { 78094c06356bSdh atomic_inc_32(&phyp->ref_count); 78104c06356bSdh } 78114c06356bSdh 78124c06356bSdh void 78134c06356bSdh pmcs_dec_phy_ref_count(pmcs_phy_t *phyp) 78144c06356bSdh { 78154c06356bSdh ASSERT(phyp->ref_count != 0); 78164c06356bSdh atomic_dec_32(&phyp->ref_count); 78174c06356bSdh } 78184c06356bSdh 78194c06356bSdh /* 78204c06356bSdh * pmcs_reap_dead_phy 78214c06356bSdh * 78224c06356bSdh * This function is called from pmcs_new_tport when we have a PHY 78234c06356bSdh * without a target pointer. It's possible in that case that this PHY 78244c06356bSdh * may have a "brother" on the dead_phys list. That is, it may be the same as 78254c06356bSdh * this one but with a different root PHY number (e.g. pp05 vs. pp04). If 78264c06356bSdh * that's the case, update the dead PHY and this new PHY. If that's not the 78274c06356bSdh * case, we should get a tran_tgt_init on this after it's reported to SCSA. 78284c06356bSdh * 78294c06356bSdh * Called with PHY locked. 78304c06356bSdh */ 78314c06356bSdh static void 78324c06356bSdh pmcs_reap_dead_phy(pmcs_phy_t *phyp) 78334c06356bSdh { 78344c06356bSdh pmcs_hw_t *pwp = phyp->pwp; 78354c06356bSdh pmcs_phy_t *ctmp; 783673a3eccdSDavid Hollister pmcs_iport_t *iport_cmp; 78374c06356bSdh 78384c06356bSdh ASSERT(mutex_owned(&phyp->phy_lock)); 78394c06356bSdh 78404c06356bSdh /* 78414c06356bSdh * Check the dead PHYs list 78424c06356bSdh */ 78434c06356bSdh mutex_enter(&pwp->dead_phylist_lock); 78444c06356bSdh ctmp = pwp->dead_phys; 78454c06356bSdh while (ctmp) { 784673a3eccdSDavid Hollister /* 784773a3eccdSDavid Hollister * If the iport is NULL, compare against last_iport. 784873a3eccdSDavid Hollister */ 784973a3eccdSDavid Hollister if (ctmp->iport) { 785073a3eccdSDavid Hollister iport_cmp = ctmp->iport; 785173a3eccdSDavid Hollister } else { 785273a3eccdSDavid Hollister iport_cmp = ctmp->last_iport; 785373a3eccdSDavid Hollister } 785473a3eccdSDavid Hollister 785573a3eccdSDavid Hollister if ((iport_cmp != phyp->iport) || 78564c06356bSdh (memcmp((void *)&ctmp->sas_address[0], 78574c06356bSdh (void *)&phyp->sas_address[0], 8))) { 78584c06356bSdh ctmp = ctmp->dead_next; 78594c06356bSdh continue; 78604c06356bSdh } 78614c06356bSdh 78624c06356bSdh /* 78634c06356bSdh * Same SAS address on same iport. Now check to see if 78644c06356bSdh * the PHY path is the same with the possible exception 78654c06356bSdh * of the root PHY number. 78664c06356bSdh * The "5" is the string length of "pp00." 78674c06356bSdh */ 78684c06356bSdh if ((strnlen(phyp->path, 5) >= 5) && 78694c06356bSdh (strnlen(ctmp->path, 5) >= 5)) { 78704c06356bSdh if (memcmp((void *)&phyp->path[5], 78714c06356bSdh (void *)&ctmp->path[5], 78724c06356bSdh strnlen(phyp->path, 32) - 5) == 0) { 78734c06356bSdh break; 78744c06356bSdh } 78754c06356bSdh } 78764c06356bSdh 78774c06356bSdh ctmp = ctmp->dead_next; 78784c06356bSdh } 78794c06356bSdh mutex_exit(&pwp->dead_phylist_lock); 78804c06356bSdh 78814c06356bSdh /* 78824c06356bSdh * Found a match. Remove the target linkage and drop the 78834c06356bSdh * ref count on the old PHY. Then, increment the ref count 78844c06356bSdh * on the new PHY to compensate. 78854c06356bSdh */ 78864c06356bSdh if (ctmp) { 7887c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL, 788873a3eccdSDavid Hollister "%s: Found match in dead PHY list (0x%p) for new PHY %s", 788973a3eccdSDavid Hollister __func__, (void *)ctmp, phyp->path); 789073a3eccdSDavid Hollister /* 789173a3eccdSDavid Hollister * If there is a pointer to the target in the dead PHY, move 789273a3eccdSDavid Hollister * all reference counts to the new PHY. 789373a3eccdSDavid Hollister */ 78944c06356bSdh if (ctmp->target) { 789573a3eccdSDavid Hollister mutex_enter(&ctmp->target->statlock); 78964c06356bSdh phyp->target = ctmp->target; 789773a3eccdSDavid Hollister 789873a3eccdSDavid Hollister while (ctmp->ref_count != 0) { 789973a3eccdSDavid Hollister pmcs_inc_phy_ref_count(phyp); 790073a3eccdSDavid Hollister pmcs_dec_phy_ref_count(ctmp); 790173a3eccdSDavid Hollister } 79024c06356bSdh /* 79034c06356bSdh * Update the target's linkage as well 79044c06356bSdh */ 79054c06356bSdh phyp->target->phy = phyp; 79064c06356bSdh phyp->target->dtype = phyp->dtype; 790773a3eccdSDavid Hollister ctmp->target = NULL; 79084c06356bSdh mutex_exit(&phyp->target->statlock); 79094c06356bSdh } 79104c06356bSdh } 79114c06356bSdh } 79124c06356bSdh 79134c06356bSdh /* 79144c06356bSdh * Called with iport lock held 79154c06356bSdh */ 79164c06356bSdh void 79174c06356bSdh pmcs_add_phy_to_iport(pmcs_iport_t *iport, pmcs_phy_t *phyp) 79184c06356bSdh { 79194c06356bSdh ASSERT(mutex_owned(&iport->lock)); 79204c06356bSdh ASSERT(phyp); 79214c06356bSdh ASSERT(!list_link_active(&phyp->list_node)); 79224c06356bSdh iport->nphy++; 7923145e0143Sdh list_insert_tail(&iport->phys, phyp); 79244c06356bSdh pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS, 79254c06356bSdh &iport->nphy); 792635dae232SSrikanth Suravajhala mutex_enter(&phyp->phy_lock); 792735dae232SSrikanth Suravajhala pmcs_create_one_phy_stats(iport, phyp); 792835dae232SSrikanth Suravajhala mutex_exit(&phyp->phy_lock); 79294c06356bSdh mutex_enter(&iport->refcnt_lock); 79304c06356bSdh iport->refcnt++; 79314c06356bSdh mutex_exit(&iport->refcnt_lock); 79324c06356bSdh } 79334c06356bSdh 79344c06356bSdh /* 79354c06356bSdh * Called with the iport lock held 79364c06356bSdh */ 79374c06356bSdh void 79384c06356bSdh pmcs_remove_phy_from_iport(pmcs_iport_t *iport, pmcs_phy_t *phyp) 79394c06356bSdh { 79404c06356bSdh pmcs_phy_t *pptr, *next_pptr; 79414c06356bSdh 79424c06356bSdh ASSERT(mutex_owned(&iport->lock)); 79434c06356bSdh 79444c06356bSdh /* 79454c06356bSdh * If phyp is NULL, remove all PHYs from the iport 79464c06356bSdh */ 79474c06356bSdh if (phyp == NULL) { 79484c06356bSdh for (pptr = list_head(&iport->phys); pptr != NULL; 79494c06356bSdh pptr = next_pptr) { 79504c06356bSdh next_pptr = list_next(&iport->phys, pptr); 79514c06356bSdh mutex_enter(&pptr->phy_lock); 795235dae232SSrikanth Suravajhala if (pptr->phy_stats != NULL) { 795335dae232SSrikanth Suravajhala kstat_delete(pptr->phy_stats); 795435dae232SSrikanth Suravajhala pptr->phy_stats = NULL; 795535dae232SSrikanth Suravajhala } 79564c06356bSdh pptr->iport = NULL; 79575c45adf0SJesse Butler pmcs_update_phy_pm_props(pptr, pptr->att_port_pm_tmp, 79585c45adf0SJesse Butler pptr->tgt_port_pm_tmp, B_FALSE); 79594c06356bSdh mutex_exit(&pptr->phy_lock); 79604c06356bSdh pmcs_rele_iport(iport); 79614c06356bSdh list_remove(&iport->phys, pptr); 79629aed1621SDavid Hollister pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, 79639aed1621SDavid Hollister PMCS_NUM_PHYS, &iport->nphy); 79644c06356bSdh } 79654c06356bSdh iport->nphy = 0; 79664c06356bSdh return; 79674c06356bSdh } 79684c06356bSdh 79694c06356bSdh ASSERT(phyp); 79704c06356bSdh ASSERT(iport->nphy > 0); 79714c06356bSdh ASSERT(list_link_active(&phyp->list_node)); 79724c06356bSdh iport->nphy--; 7973145e0143Sdh list_remove(&iport->phys, phyp); 7974499cfd15SDavid Hollister pmcs_update_phy_pm_props(phyp, phyp->att_port_pm_tmp, 7975499cfd15SDavid Hollister phyp->tgt_port_pm_tmp, B_FALSE); 79764c06356bSdh pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS, 79774c06356bSdh &iport->nphy); 79784c06356bSdh pmcs_rele_iport(iport); 79794c06356bSdh } 79804c06356bSdh 79814c06356bSdh /* 79824c06356bSdh * This function checks to see if the target pointed to by phyp is still 79834c06356bSdh * correct. This is done by comparing the target's unit address with the 79844c06356bSdh * SAS address in phyp. 79854c06356bSdh * 79864c06356bSdh * Called with PHY locked and target statlock held 79874c06356bSdh */ 79884c06356bSdh static boolean_t 79894c06356bSdh pmcs_phy_target_match(pmcs_phy_t *phyp) 79904c06356bSdh { 79914c06356bSdh uint64_t wwn; 79924c06356bSdh char unit_address[PMCS_MAX_UA_SIZE]; 79934c06356bSdh boolean_t rval = B_FALSE; 79944c06356bSdh 79954c06356bSdh ASSERT(phyp); 79964c06356bSdh ASSERT(phyp->target); 79974c06356bSdh ASSERT(mutex_owned(&phyp->phy_lock)); 79984c06356bSdh ASSERT(mutex_owned(&phyp->target->statlock)); 79994c06356bSdh 80004c06356bSdh wwn = pmcs_barray2wwn(phyp->sas_address); 80014c06356bSdh (void) scsi_wwn_to_wwnstr(wwn, 1, unit_address); 80024c06356bSdh 80034c06356bSdh if (memcmp((void *)unit_address, (void *)phyp->target->unit_address, 80044c06356bSdh strnlen(phyp->target->unit_address, PMCS_MAX_UA_SIZE)) == 0) { 80054c06356bSdh rval = B_TRUE; 80064c06356bSdh } 80074c06356bSdh 80084c06356bSdh return (rval); 80094c06356bSdh } 80106745c559SJesse Butler /* 80116745c559SJesse Butler * Commands used to serialize SMP requests. 80126745c559SJesse Butler * 80136745c559SJesse Butler * The SPC only allows 2 SMP commands per SMP target: 1 cmd pending and 1 cmd 80146745c559SJesse Butler * queued for the same SMP target. If a third SMP cmd is sent to the SPC for an 80156745c559SJesse Butler * SMP target that already has a SMP cmd pending and one queued, then the 80166745c559SJesse Butler * SPC responds with the ERROR_INTERNAL_SMP_RESOURCE response. 80176745c559SJesse Butler * 80186745c559SJesse Butler * Additionally, the SPC has an 8 entry deep cmd queue and the number of SMP 80196745c559SJesse Butler * cmds that can be queued is controlled by the PORT_CONTROL IOMB. The 80206745c559SJesse Butler * SPC default is 1 SMP command/port (iport). These 2 queued SMP cmds would 80216745c559SJesse Butler * have to be for different SMP targets. The INTERNAL_SMP_RESOURCE error will 80226745c559SJesse Butler * also be returned if a 2nd SMP cmd is sent to the controller when there is 80236745c559SJesse Butler * already 1 SMP cmd queued for that port or if a 3rd SMP cmd is sent to the 80246745c559SJesse Butler * queue if there are already 2 queued SMP cmds. 80256745c559SJesse Butler */ 80266745c559SJesse Butler void 80276745c559SJesse Butler pmcs_smp_acquire(pmcs_iport_t *iport) 80286745c559SJesse Butler { 80296745c559SJesse Butler if (iport == NULL) { 80306745c559SJesse Butler return; 80316745c559SJesse Butler } 80326745c559SJesse Butler 80336745c559SJesse Butler mutex_enter(&iport->smp_lock); 80346745c559SJesse Butler while (iport->smp_active) { 80356745c559SJesse Butler pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL, 80366745c559SJesse Butler "%s: SMP is active on thread 0x%p, waiting", __func__, 80376745c559SJesse Butler (void *)iport->smp_active_thread); 80386745c559SJesse Butler cv_wait(&iport->smp_cv, &iport->smp_lock); 80396745c559SJesse Butler } 80406745c559SJesse Butler iport->smp_active = B_TRUE; 80416745c559SJesse Butler iport->smp_active_thread = curthread; 80429aed1621SDavid Hollister pmcs_prt(iport->pwp, PMCS_PRT_DEBUG3, NULL, NULL, 80436745c559SJesse Butler "%s: SMP acquired by thread 0x%p", __func__, 80446745c559SJesse Butler (void *)iport->smp_active_thread); 80456745c559SJesse Butler mutex_exit(&iport->smp_lock); 80466745c559SJesse Butler } 80476745c559SJesse Butler 80486745c559SJesse Butler void 80496745c559SJesse Butler pmcs_smp_release(pmcs_iport_t *iport) 80506745c559SJesse Butler { 80516745c559SJesse Butler if (iport == NULL) { 80526745c559SJesse Butler return; 80536745c559SJesse Butler } 80546745c559SJesse Butler 80556745c559SJesse Butler mutex_enter(&iport->smp_lock); 80569aed1621SDavid Hollister pmcs_prt(iport->pwp, PMCS_PRT_DEBUG3, NULL, NULL, 80576745c559SJesse Butler "%s: SMP released by thread 0x%p", __func__, (void *)curthread); 80586745c559SJesse Butler iport->smp_active = B_FALSE; 80596745c559SJesse Butler iport->smp_active_thread = NULL; 80606745c559SJesse Butler cv_signal(&iport->smp_cv); 80616745c559SJesse Butler mutex_exit(&iport->smp_lock); 80626745c559SJesse Butler } 8063499cfd15SDavid Hollister 8064499cfd15SDavid Hollister /* 8065499cfd15SDavid Hollister * Update a PHY's attached-port-pm and target-port-pm properties 8066499cfd15SDavid Hollister * 8067499cfd15SDavid Hollister * phyp: PHY whose properties are to be updated 8068499cfd15SDavid Hollister * 8069499cfd15SDavid Hollister * att_bv: Bit value of the attached-port-pm property to be updated in the 8070499cfd15SDavid Hollister * 64-bit holding area for the PHY. 8071499cfd15SDavid Hollister * 8072499cfd15SDavid Hollister * tgt_bv: Bit value of the target-port-pm property to update in the 64-bit 8073499cfd15SDavid Hollister * holding area for the PHY. 8074499cfd15SDavid Hollister * 8075499cfd15SDavid Hollister * prop_add_val: If TRUE, we're adding bits into the property value. 8076499cfd15SDavid Hollister * Otherwise, we're taking them out. Either way, the properties for this 8077499cfd15SDavid Hollister * PHY will be updated. 8078499cfd15SDavid Hollister */ 8079499cfd15SDavid Hollister void 8080499cfd15SDavid Hollister pmcs_update_phy_pm_props(pmcs_phy_t *phyp, uint64_t att_bv, uint64_t tgt_bv, 8081499cfd15SDavid Hollister boolean_t prop_add_val) 8082499cfd15SDavid Hollister { 8083499cfd15SDavid Hollister if (prop_add_val) { 8084499cfd15SDavid Hollister /* 8085499cfd15SDavid Hollister * If the values are currently 0, then we're setting the 8086499cfd15SDavid Hollister * phymask for just this PHY as well. 8087499cfd15SDavid Hollister */ 8088499cfd15SDavid Hollister if (phyp->att_port_pm_tmp == 0) { 8089616875b4SDavid Hollister phyp->att_port_pm = att_bv; 8090616875b4SDavid Hollister phyp->tgt_port_pm = tgt_bv; 8091499cfd15SDavid Hollister } 8092616875b4SDavid Hollister phyp->att_port_pm_tmp |= att_bv; 8093616875b4SDavid Hollister phyp->tgt_port_pm_tmp |= tgt_bv; 8094499cfd15SDavid Hollister (void) snprintf(phyp->att_port_pm_str, PMCS_PM_MAX_NAMELEN, 8095499cfd15SDavid Hollister "%"PRIx64, phyp->att_port_pm_tmp); 8096499cfd15SDavid Hollister (void) snprintf(phyp->tgt_port_pm_str, PMCS_PM_MAX_NAMELEN, 8097499cfd15SDavid Hollister "%"PRIx64, phyp->tgt_port_pm_tmp); 8098499cfd15SDavid Hollister } else { 8099616875b4SDavid Hollister phyp->att_port_pm_tmp &= ~att_bv; 8100616875b4SDavid Hollister phyp->tgt_port_pm_tmp &= ~tgt_bv; 8101499cfd15SDavid Hollister if (phyp->att_port_pm_tmp) { 8102499cfd15SDavid Hollister (void) snprintf(phyp->att_port_pm_str, 8103499cfd15SDavid Hollister PMCS_PM_MAX_NAMELEN, "%"PRIx64, 8104499cfd15SDavid Hollister phyp->att_port_pm_tmp); 8105499cfd15SDavid Hollister } else { 8106499cfd15SDavid Hollister phyp->att_port_pm_str[0] = '\0'; 8107499cfd15SDavid Hollister phyp->att_port_pm = 0; 8108499cfd15SDavid Hollister } 8109499cfd15SDavid Hollister if (phyp->tgt_port_pm_tmp) { 8110499cfd15SDavid Hollister (void) snprintf(phyp->tgt_port_pm_str, 8111499cfd15SDavid Hollister PMCS_PM_MAX_NAMELEN, "%"PRIx64, 8112499cfd15SDavid Hollister phyp->tgt_port_pm_tmp); 8113499cfd15SDavid Hollister } else { 8114499cfd15SDavid Hollister phyp->tgt_port_pm_str[0] = '\0'; 8115499cfd15SDavid Hollister phyp->tgt_port_pm = 0; 8116499cfd15SDavid Hollister } 8117499cfd15SDavid Hollister } 8118499cfd15SDavid Hollister 8119499cfd15SDavid Hollister if (phyp->target == NULL) { 8120499cfd15SDavid Hollister return; 8121499cfd15SDavid Hollister } 8122499cfd15SDavid Hollister 812373a3eccdSDavid Hollister mutex_enter(&phyp->target->statlock); 812473a3eccdSDavid Hollister if (!list_is_empty(&phyp->target->lun_list)) { 812573a3eccdSDavid Hollister pmcs_lun_t *lunp; 812673a3eccdSDavid Hollister 812773a3eccdSDavid Hollister lunp = list_head(&phyp->target->lun_list); 812873a3eccdSDavid Hollister while (lunp) { 812973a3eccdSDavid Hollister (void) scsi_device_prop_update_string(lunp->sd, 813073a3eccdSDavid Hollister SCSI_DEVICE_PROP_PATH, 813173a3eccdSDavid Hollister SCSI_ADDR_PROP_ATTACHED_PORT_PM, 813273a3eccdSDavid Hollister phyp->att_port_pm_str); 813373a3eccdSDavid Hollister (void) scsi_device_prop_update_string(lunp->sd, 813473a3eccdSDavid Hollister SCSI_DEVICE_PROP_PATH, 813573a3eccdSDavid Hollister SCSI_ADDR_PROP_TARGET_PORT_PM, 813673a3eccdSDavid Hollister phyp->tgt_port_pm_str); 813773a3eccdSDavid Hollister lunp = list_next(&phyp->target->lun_list, lunp); 813873a3eccdSDavid Hollister } 8139499cfd15SDavid Hollister } else if (phyp->target->smpd) { 8140499cfd15SDavid Hollister (void) smp_device_prop_update_string(phyp->target->smpd, 8141499cfd15SDavid Hollister SCSI_ADDR_PROP_ATTACHED_PORT_PM, 8142499cfd15SDavid Hollister phyp->att_port_pm_str); 8143499cfd15SDavid Hollister (void) smp_device_prop_update_string(phyp->target->smpd, 8144499cfd15SDavid Hollister SCSI_ADDR_PROP_TARGET_PORT_PM, 8145499cfd15SDavid Hollister phyp->tgt_port_pm_str); 8146499cfd15SDavid Hollister } 814773a3eccdSDavid Hollister mutex_exit(&phyp->target->statlock); 8148499cfd15SDavid Hollister } 8149601c90f1SSrikanth, Ramana 8150601c90f1SSrikanth, Ramana /* ARGSUSED */ 8151601c90f1SSrikanth, Ramana void 8152601c90f1SSrikanth, Ramana pmcs_deregister_device_work(pmcs_hw_t *pwp, pmcs_phy_t *phyp) 8153601c90f1SSrikanth, Ramana { 8154601c90f1SSrikanth, Ramana pmcs_phy_t *pptr; 8155601c90f1SSrikanth, Ramana 8156601c90f1SSrikanth, Ramana for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) { 8157601c90f1SSrikanth, Ramana pmcs_lock_phy(pptr); 8158601c90f1SSrikanth, Ramana if (pptr->deregister_wait) { 8159601c90f1SSrikanth, Ramana pmcs_deregister_device(pwp, pptr); 8160601c90f1SSrikanth, Ramana } 8161601c90f1SSrikanth, Ramana pmcs_unlock_phy(pptr); 8162601c90f1SSrikanth, Ramana } 8163601c90f1SSrikanth, Ramana } 81649aed1621SDavid Hollister 81659aed1621SDavid Hollister /* 81669aed1621SDavid Hollister * pmcs_iport_active 81679aed1621SDavid Hollister * 81689aed1621SDavid Hollister * Mark this iport as active. Called with the iport lock held. 81699aed1621SDavid Hollister */ 81709aed1621SDavid Hollister static void 81719aed1621SDavid Hollister pmcs_iport_active(pmcs_iport_t *iport) 81729aed1621SDavid Hollister { 81739aed1621SDavid Hollister ASSERT(mutex_owned(&iport->lock)); 81749aed1621SDavid Hollister 81759aed1621SDavid Hollister iport->ua_state = UA_ACTIVE; 81769aed1621SDavid Hollister iport->smp_active = B_FALSE; 81779aed1621SDavid Hollister iport->smp_active_thread = NULL; 81789aed1621SDavid Hollister } 81799aed1621SDavid Hollister 81809aed1621SDavid Hollister /* ARGSUSED */ 81819aed1621SDavid Hollister static void 81829aed1621SDavid Hollister pmcs_tgtmap_activate_cb(void *tgtmap_priv, char *tgt_addr, 81839aed1621SDavid Hollister scsi_tgtmap_tgt_type_t tgt_type, void **tgt_privp) 81849aed1621SDavid Hollister { 81859aed1621SDavid Hollister pmcs_iport_t *iport = (pmcs_iport_t *)tgtmap_priv; 81865c45adf0SJesse Butler pmcs_hw_t *pwp = iport->pwp; 81875c45adf0SJesse Butler pmcs_xscsi_t *target; 81889aed1621SDavid Hollister 81895c45adf0SJesse Butler /* 81905c45adf0SJesse Butler * Look up the target. If there is one, and it doesn't have a PHY 81915c45adf0SJesse Butler * pointer, re-establish that linkage here. 81925c45adf0SJesse Butler */ 81935c45adf0SJesse Butler mutex_enter(&pwp->lock); 81945c45adf0SJesse Butler target = pmcs_get_target(iport, tgt_addr, B_FALSE); 81955c45adf0SJesse Butler mutex_exit(&pwp->lock); 81965c45adf0SJesse Butler 81975c45adf0SJesse Butler /* 81985c45adf0SJesse Butler * If we got a target, it will now have a PHY pointer and the PHY 81995c45adf0SJesse Butler * will point to the target. The PHY will be locked, so we'll need 82005c45adf0SJesse Butler * to unlock it. 82015c45adf0SJesse Butler */ 82025c45adf0SJesse Butler if (target) { 82035c45adf0SJesse Butler pmcs_unlock_phy(target->phy); 82045c45adf0SJesse Butler } 82059aed1621SDavid Hollister 82069aed1621SDavid Hollister /* 82079aed1621SDavid Hollister * Update config_restart_time so we don't try to restart discovery 82089aed1621SDavid Hollister * while enumeration is still in progress. 82099aed1621SDavid Hollister */ 82105c45adf0SJesse Butler mutex_enter(&pwp->config_lock); 82115c45adf0SJesse Butler pwp->config_restart_time = ddi_get_lbolt() + 82129aed1621SDavid Hollister drv_usectohz(PMCS_REDISCOVERY_DELAY); 82135c45adf0SJesse Butler mutex_exit(&pwp->config_lock); 82149aed1621SDavid Hollister } 82159aed1621SDavid Hollister 82169aed1621SDavid Hollister /* ARGSUSED */ 82179aed1621SDavid Hollister static boolean_t 82189aed1621SDavid Hollister pmcs_tgtmap_deactivate_cb(void *tgtmap_priv, char *tgt_addr, 82199aed1621SDavid Hollister scsi_tgtmap_tgt_type_t tgt_type, void *tgt_priv, 82209aed1621SDavid Hollister scsi_tgtmap_deact_rsn_t tgt_deact_rsn) 82219aed1621SDavid Hollister { 82229aed1621SDavid Hollister pmcs_iport_t *iport = (pmcs_iport_t *)tgtmap_priv; 82239aed1621SDavid Hollister pmcs_phy_t *phyp; 82249aed1621SDavid Hollister boolean_t rediscover = B_FALSE; 82259aed1621SDavid Hollister 82269aed1621SDavid Hollister ASSERT(iport); 82279aed1621SDavid Hollister 82289aed1621SDavid Hollister phyp = pmcs_find_phy_by_sas_address(iport->pwp, iport, NULL, tgt_addr); 82299aed1621SDavid Hollister if (phyp == NULL) { 82309aed1621SDavid Hollister pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL, 82319aed1621SDavid Hollister "%s: Couldn't find PHY at %s", __func__, tgt_addr); 82329aed1621SDavid Hollister return (rediscover); 82339aed1621SDavid Hollister } 82349aed1621SDavid Hollister /* phyp is locked */ 82359aed1621SDavid Hollister 82369aed1621SDavid Hollister if (!phyp->reenumerate && phyp->configured) { 82379aed1621SDavid Hollister pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, phyp, phyp->target, 82389aed1621SDavid Hollister "%s: PHY @ %s is configured... re-enumerate", __func__, 82399aed1621SDavid Hollister tgt_addr); 82409aed1621SDavid Hollister phyp->reenumerate = 1; 82419aed1621SDavid Hollister } 82429aed1621SDavid Hollister 82439aed1621SDavid Hollister /* 82449aed1621SDavid Hollister * Check to see if reenumerate is set, and if so, if we've reached our 82459aed1621SDavid Hollister * maximum number of retries. 82469aed1621SDavid Hollister */ 82479aed1621SDavid Hollister if (phyp->reenumerate) { 82489aed1621SDavid Hollister if (phyp->enum_attempts == PMCS_MAX_REENUMERATE) { 82499aed1621SDavid Hollister pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, phyp, 82509aed1621SDavid Hollister phyp->target, 82519aed1621SDavid Hollister "%s: No more enumeration attempts for %s", __func__, 82529aed1621SDavid Hollister tgt_addr); 82539aed1621SDavid Hollister } else { 82549aed1621SDavid Hollister pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, phyp, 82559aed1621SDavid Hollister phyp->target, "%s: Re-attempt enumeration for %s", 82569aed1621SDavid Hollister __func__, tgt_addr); 82579aed1621SDavid Hollister ++phyp->enum_attempts; 82589aed1621SDavid Hollister rediscover = B_TRUE; 82599aed1621SDavid Hollister } 82609aed1621SDavid Hollister 82619aed1621SDavid Hollister phyp->reenumerate = 0; 82629aed1621SDavid Hollister } 82639aed1621SDavid Hollister 82649aed1621SDavid Hollister pmcs_unlock_phy(phyp); 82659aed1621SDavid Hollister 82669aed1621SDavid Hollister mutex_enter(&iport->pwp->config_lock); 82679aed1621SDavid Hollister iport->pwp->config_restart_time = ddi_get_lbolt() + 82689aed1621SDavid Hollister drv_usectohz(PMCS_REDISCOVERY_DELAY); 82699aed1621SDavid Hollister if (rediscover) { 82709aed1621SDavid Hollister iport->pwp->config_restart = B_TRUE; 82719aed1621SDavid Hollister } else if (iport->pwp->config_restart == B_TRUE) { 82729aed1621SDavid Hollister /* 82739aed1621SDavid Hollister * If we aren't asking for rediscovery because of this PHY, 82749aed1621SDavid Hollister * check to see if we're already asking for it on behalf of 82759aed1621SDavid Hollister * some other PHY. If so, we'll want to return TRUE, so reset 82769aed1621SDavid Hollister * "rediscover" here. 82779aed1621SDavid Hollister */ 82789aed1621SDavid Hollister rediscover = B_TRUE; 82799aed1621SDavid Hollister } 82809aed1621SDavid Hollister 82819aed1621SDavid Hollister mutex_exit(&iport->pwp->config_lock); 82829aed1621SDavid Hollister 82839aed1621SDavid Hollister return (rediscover); 82849aed1621SDavid Hollister } 82859aed1621SDavid Hollister 82869aed1621SDavid Hollister void 82879aed1621SDavid Hollister pmcs_status_disposition(pmcs_phy_t *phyp, uint32_t status) 82889aed1621SDavid Hollister { 82899aed1621SDavid Hollister ASSERT(phyp); 82909aed1621SDavid Hollister ASSERT(!mutex_owned(&phyp->phy_lock)); 82919aed1621SDavid Hollister 82929aed1621SDavid Hollister if (phyp == NULL) { 82939aed1621SDavid Hollister return; 82949aed1621SDavid Hollister } 82959aed1621SDavid Hollister 82969aed1621SDavid Hollister pmcs_lock_phy(phyp); 82979aed1621SDavid Hollister 82989aed1621SDavid Hollister /* 82999aed1621SDavid Hollister * XXX: Do we need to call this function from an SSP_EVENT? 83009aed1621SDavid Hollister */ 83019aed1621SDavid Hollister 83029aed1621SDavid Hollister switch (status) { 83039aed1621SDavid Hollister case PMCOUT_STATUS_NO_DEVICE: 83049aed1621SDavid Hollister case PMCOUT_STATUS_ERROR_HW_TIMEOUT: 83059aed1621SDavid Hollister case PMCOUT_STATUS_XFER_ERR_BREAK: 83069aed1621SDavid Hollister case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY: 83079aed1621SDavid Hollister case PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED: 83089aed1621SDavid Hollister case PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION: 83099aed1621SDavid Hollister case PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK: 83109aed1621SDavid Hollister case PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION: 83119aed1621SDavid Hollister case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: 83129aed1621SDavid Hollister case PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY: 83139aed1621SDavid Hollister case PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION: 83149aed1621SDavid Hollister case PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_ERROR: 83159aed1621SDavid Hollister case PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED: 83169aed1621SDavid Hollister case PMCOUT_STATUS_XFER_ERROR_RX_FRAME: 83179aed1621SDavid Hollister case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT: 83189aed1621SDavid Hollister case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE: 83199aed1621SDavid Hollister case PMCOUT_STATUS_IO_PORT_IN_RESET: 83209aed1621SDavid Hollister case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL: 83219aed1621SDavid Hollister case PMCOUT_STATUS_IO_DS_IN_RECOVERY: 83229aed1621SDavid Hollister case PMCOUT_STATUS_IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY: 83239aed1621SDavid Hollister pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG, phyp, phyp->target, 83249aed1621SDavid Hollister "%s: status = 0x%x for " SAS_ADDR_FMT ", reenumerate", 83259aed1621SDavid Hollister __func__, status, SAS_ADDR_PRT(phyp->sas_address)); 83269aed1621SDavid Hollister phyp->reenumerate = 1; 83279aed1621SDavid Hollister break; 83289aed1621SDavid Hollister 83299aed1621SDavid Hollister default: 83309aed1621SDavid Hollister pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG, phyp, phyp->target, 83319aed1621SDavid Hollister "%s: status = 0x%x for " SAS_ADDR_FMT ", no reenumeration", 83329aed1621SDavid Hollister __func__, status, SAS_ADDR_PRT(phyp->sas_address)); 83339aed1621SDavid Hollister break; 83349aed1621SDavid Hollister } 83359aed1621SDavid Hollister 83369aed1621SDavid Hollister pmcs_unlock_phy(phyp); 83379aed1621SDavid Hollister } 83389aed1621SDavid Hollister 83399aed1621SDavid Hollister /* 83409aed1621SDavid Hollister * Add the list of PHYs pointed to by phyp to the dead_phys_list 83419aed1621SDavid Hollister * 83429aed1621SDavid Hollister * Called with all PHYs in the list locked 83439aed1621SDavid Hollister */ 83449aed1621SDavid Hollister static void 83459aed1621SDavid Hollister pmcs_add_dead_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp) 83469aed1621SDavid Hollister { 83479aed1621SDavid Hollister mutex_enter(&pwp->dead_phylist_lock); 83489aed1621SDavid Hollister while (phyp) { 83499aed1621SDavid Hollister pmcs_phy_t *nxt = phyp->sibling; 83509aed1621SDavid Hollister ASSERT(phyp->dead); 83519aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, NULL, 83529aed1621SDavid Hollister "%s: dead PHY 0x%p (%s) (ref_count %d)", __func__, 83539aed1621SDavid Hollister (void *)phyp, phyp->path, phyp->ref_count); 83549aed1621SDavid Hollister /* 83559aed1621SDavid Hollister * Put this PHY on the dead PHY list for the watchdog to 83569aed1621SDavid Hollister * clean up after any outstanding work has completed. 83579aed1621SDavid Hollister */ 83589aed1621SDavid Hollister phyp->dead_next = pwp->dead_phys; 83599aed1621SDavid Hollister pwp->dead_phys = phyp; 83609aed1621SDavid Hollister pmcs_unlock_phy(phyp); 83619aed1621SDavid Hollister phyp = nxt; 83629aed1621SDavid Hollister } 83639aed1621SDavid Hollister mutex_exit(&pwp->dead_phylist_lock); 83649aed1621SDavid Hollister } 83652ac4abe8SDavid Hollister 83662ac4abe8SDavid Hollister static void 83672ac4abe8SDavid Hollister pmcs_get_fw_version(pmcs_hw_t *pwp) 83682ac4abe8SDavid Hollister { 83692ac4abe8SDavid Hollister uint32_t ila_len, ver_hi, ver_lo; 83702ac4abe8SDavid Hollister uint8_t ila_ver_string[9], img_flag; 83712ac4abe8SDavid Hollister char uc, *ucp = &uc; 83722ac4abe8SDavid Hollister unsigned long ila_ver; 83732ac4abe8SDavid Hollister uint64_t ver_hilo; 83742ac4abe8SDavid Hollister 83752ac4abe8SDavid Hollister /* Firmware version is easy. */ 83762ac4abe8SDavid Hollister pwp->fw = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_FW); 83772ac4abe8SDavid Hollister 83782ac4abe8SDavid Hollister /* 83792ac4abe8SDavid Hollister * Get the image size (2nd to last dword) 83802ac4abe8SDavid Hollister * NOTE: The GSM registers are mapped little-endian, but the data 83812ac4abe8SDavid Hollister * on the flash is actually big-endian, so we need to swap these values 83822ac4abe8SDavid Hollister * regardless of which platform we're on. 83832ac4abe8SDavid Hollister */ 83842ac4abe8SDavid Hollister ila_len = BSWAP_32(pmcs_rd_gsm_reg(pwp, GSM_FLASH_BASE_UPPER, 83852ac4abe8SDavid Hollister GSM_FLASH_BASE + GSM_SM_BLKSZ - (2 << 2))); 83862ac4abe8SDavid Hollister if (ila_len > 65535) { 83872ac4abe8SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 83882ac4abe8SDavid Hollister "%s: Invalid ILA image size (0x%x)?", __func__, ila_len); 83892ac4abe8SDavid Hollister return; 83902ac4abe8SDavid Hollister } 83912ac4abe8SDavid Hollister 83922ac4abe8SDavid Hollister /* 83932ac4abe8SDavid Hollister * The numeric version is at ila_len - PMCS_ILA_VER_OFFSET 83942ac4abe8SDavid Hollister */ 83952ac4abe8SDavid Hollister ver_hi = BSWAP_32(pmcs_rd_gsm_reg(pwp, GSM_FLASH_BASE_UPPER, 83962ac4abe8SDavid Hollister GSM_FLASH_BASE + ila_len - PMCS_ILA_VER_OFFSET)); 83972ac4abe8SDavid Hollister ver_lo = BSWAP_32(pmcs_rd_gsm_reg(pwp, GSM_FLASH_BASE_UPPER, 83982ac4abe8SDavid Hollister GSM_FLASH_BASE + ila_len - PMCS_ILA_VER_OFFSET + 4)); 83992ac4abe8SDavid Hollister ver_hilo = BE_64(((uint64_t)ver_hi << 32) | ver_lo); 84002ac4abe8SDavid Hollister bcopy((const void *)&ver_hilo, &ila_ver_string[0], 8); 84012ac4abe8SDavid Hollister ila_ver_string[8] = '\0'; 84022ac4abe8SDavid Hollister 84032ac4abe8SDavid Hollister (void) ddi_strtoul((const char *)ila_ver_string, &ucp, 16, &ila_ver); 84042ac4abe8SDavid Hollister pwp->ila_ver = (int)(ila_ver & 0xffffffff); 84052ac4abe8SDavid Hollister 84062ac4abe8SDavid Hollister img_flag = (BSWAP_32(pmcs_rd_gsm_reg(pwp, GSM_FLASH_BASE_UPPER, 84072ac4abe8SDavid Hollister GSM_FLASH_IMG_FLAGS)) & 0xff000000) >> 24; 84082ac4abe8SDavid Hollister if (img_flag & PMCS_IMG_FLAG_A) { 84092ac4abe8SDavid Hollister pwp->fw_active_img = 1; 84102ac4abe8SDavid Hollister } else { 84112ac4abe8SDavid Hollister pwp->fw_active_img = 0; 84122ac4abe8SDavid Hollister } 84132ac4abe8SDavid Hollister } 8414