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 */ 3460aabb4cSChris Horne static int tgtmap_stable_usec = MICROSEC; /* 1 second */ 3560aabb4cSChris Horne static int tgtmap_csync_usec = 10 * MICROSEC; /* 10 seconds */ 364c06356bSdh 374c06356bSdh /* 384c06356bSdh * SAS Topology Configuration 394c06356bSdh */ 404c06356bSdh static void pmcs_new_tport(pmcs_hw_t *, pmcs_phy_t *); 414c06356bSdh static void pmcs_configure_expander(pmcs_hw_t *, pmcs_phy_t *, pmcs_iport_t *); 424c06356bSdh 4373a3eccdSDavid Hollister static void pmcs_check_expanders(pmcs_hw_t *, pmcs_phy_t *); 444c06356bSdh static void pmcs_check_expander(pmcs_hw_t *, pmcs_phy_t *); 454c06356bSdh static void pmcs_clear_expander(pmcs_hw_t *, pmcs_phy_t *, int); 464c06356bSdh 474c06356bSdh static int pmcs_expander_get_nphy(pmcs_hw_t *, pmcs_phy_t *); 484c06356bSdh static int pmcs_expander_content_discover(pmcs_hw_t *, pmcs_phy_t *, 494c06356bSdh pmcs_phy_t *); 504c06356bSdh 514c06356bSdh static int pmcs_smp_function_result(pmcs_hw_t *, smp_response_frame_t *); 52*3be32c0fSJesse Butler static void pmcs_flush_nonio_cmds(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt); 534c06356bSdh static boolean_t pmcs_validate_devid(pmcs_phy_t *, pmcs_phy_t *, uint32_t); 544c06356bSdh static void pmcs_clear_phys(pmcs_hw_t *, pmcs_phy_t *); 554c06356bSdh static int pmcs_configure_new_devices(pmcs_hw_t *, pmcs_phy_t *); 56601c90f1SSrikanth, Ramana static void pmcs_begin_observations(pmcs_hw_t *); 570b53804eSReed static void pmcs_flush_observations(pmcs_hw_t *); 584c06356bSdh static boolean_t pmcs_report_observations(pmcs_hw_t *); 594c06356bSdh static boolean_t pmcs_report_iport_observations(pmcs_hw_t *, pmcs_iport_t *, 604c06356bSdh pmcs_phy_t *); 614c06356bSdh static pmcs_phy_t *pmcs_find_phy_needing_work(pmcs_hw_t *, pmcs_phy_t *); 624c06356bSdh static int pmcs_kill_devices(pmcs_hw_t *, pmcs_phy_t *); 634c06356bSdh static void pmcs_lock_phy_impl(pmcs_phy_t *, int); 644c06356bSdh static void pmcs_unlock_phy_impl(pmcs_phy_t *, int); 654c06356bSdh static pmcs_phy_t *pmcs_clone_phy(pmcs_phy_t *); 664c06356bSdh static boolean_t pmcs_configure_phy(pmcs_hw_t *, pmcs_phy_t *); 674c06356bSdh static void pmcs_reap_dead_phy(pmcs_phy_t *); 684c06356bSdh static pmcs_iport_t *pmcs_get_iport_by_ua(pmcs_hw_t *, char *); 694c06356bSdh static boolean_t pmcs_phy_target_match(pmcs_phy_t *); 709aed1621SDavid Hollister static void pmcs_iport_active(pmcs_iport_t *); 719aed1621SDavid Hollister static void pmcs_tgtmap_activate_cb(void *, char *, scsi_tgtmap_tgt_type_t, 729aed1621SDavid Hollister void **); 739aed1621SDavid Hollister static boolean_t pmcs_tgtmap_deactivate_cb(void *, char *, 749aed1621SDavid Hollister scsi_tgtmap_tgt_type_t, void *, scsi_tgtmap_deact_rsn_t); 759aed1621SDavid Hollister static void pmcs_add_dead_phys(pmcs_hw_t *, pmcs_phy_t *); 762ac4abe8SDavid Hollister static void pmcs_get_fw_version(pmcs_hw_t *); 771f81b464SDavid Hollister static int pmcs_get_time_stamp(pmcs_hw_t *, uint64_t *, hrtime_t *); 784c06356bSdh 794c06356bSdh /* 804c06356bSdh * Often used strings 814c06356bSdh */ 824c06356bSdh const char pmcs_nowrk[] = "%s: unable to get work structure"; 834c06356bSdh const char pmcs_nomsg[] = "%s: unable to get Inbound Message entry"; 846745c559SJesse Butler const char pmcs_timeo[] = "%s: command timed out"; 854c06356bSdh 864c06356bSdh extern const ddi_dma_attr_t pmcs_dattr; 871f81b464SDavid Hollister extern kmutex_t pmcs_trace_lock; 884c06356bSdh 894c06356bSdh /* 904c06356bSdh * Some Initial setup steps. 914c06356bSdh */ 924c06356bSdh 934c06356bSdh int 944c06356bSdh pmcs_setup(pmcs_hw_t *pwp) 954c06356bSdh { 964c06356bSdh uint32_t barval = pwp->mpibar; 974c06356bSdh uint32_t i, scratch, regbar, regoff, barbar, baroff; 984c06356bSdh uint32_t new_ioq_depth, ferr = 0; 994c06356bSdh 1004c06356bSdh /* 1014c06356bSdh * Check current state. If we're not at READY state, 1024c06356bSdh * we can't go further. 1034c06356bSdh */ 1044c06356bSdh scratch = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1); 1054c06356bSdh if ((scratch & PMCS_MSGU_AAP_STATE_MASK) == PMCS_MSGU_AAP_STATE_ERROR) { 106c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 107c3bc407cSdh "%s: AAP Error State (0x%x)", 1084c06356bSdh __func__, pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) & 1094c06356bSdh PMCS_MSGU_AAP_ERROR_MASK); 1104c06356bSdh pmcs_fm_ereport(pwp, DDI_FM_DEVICE_INVAL_STATE); 1114c06356bSdh ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST); 1124c06356bSdh return (-1); 1134c06356bSdh } 1144c06356bSdh if ((scratch & PMCS_MSGU_AAP_STATE_MASK) != PMCS_MSGU_AAP_STATE_READY) { 115c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1164c06356bSdh "%s: AAP unit not ready (state 0x%x)", 1174c06356bSdh __func__, scratch & PMCS_MSGU_AAP_STATE_MASK); 1184c06356bSdh pmcs_fm_ereport(pwp, DDI_FM_DEVICE_INVAL_STATE); 1194c06356bSdh ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST); 1204c06356bSdh return (-1); 1214c06356bSdh } 1224c06356bSdh 1234c06356bSdh /* 1244c06356bSdh * Read the offset from the Message Unit scratchpad 0 register. 1254c06356bSdh * This allows us to read the MPI Configuration table. 1264c06356bSdh * 1274c06356bSdh * Check its signature for validity. 1284c06356bSdh */ 1294c06356bSdh baroff = barval; 1304c06356bSdh barbar = barval >> PMCS_MSGU_MPI_BAR_SHIFT; 1314c06356bSdh baroff &= PMCS_MSGU_MPI_OFFSET_MASK; 1324c06356bSdh 1334c06356bSdh regoff = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH0); 1344c06356bSdh regbar = regoff >> PMCS_MSGU_MPI_BAR_SHIFT; 1354c06356bSdh regoff &= PMCS_MSGU_MPI_OFFSET_MASK; 1364c06356bSdh 1374c06356bSdh if (regoff > baroff) { 138c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 139c3bc407cSdh "%s: bad MPI Table Length (register offset=0x%08x, " 140c3bc407cSdh "passed offset=0x%08x)", __func__, regoff, baroff); 1414c06356bSdh return (-1); 1424c06356bSdh } 1434c06356bSdh if (regbar != barbar) { 144c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 145c3bc407cSdh "%s: bad MPI BAR (register BAROFF=0x%08x, " 146c3bc407cSdh "passed BAROFF=0x%08x)", __func__, regbar, barbar); 1474c06356bSdh return (-1); 1484c06356bSdh } 1494c06356bSdh pwp->mpi_offset = regoff; 1504c06356bSdh if (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_AS) != PMCS_SIGNATURE) { 151c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1524c06356bSdh "%s: Bad MPI Configuration Table Signature 0x%x", __func__, 1534c06356bSdh pmcs_rd_mpi_tbl(pwp, PMCS_MPI_AS)); 1544c06356bSdh return (-1); 1554c06356bSdh } 1564c06356bSdh 1574c06356bSdh if (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IR) != PMCS_MPI_REVISION1) { 158c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1594c06356bSdh "%s: Bad MPI Configuration Revision 0x%x", __func__, 1604c06356bSdh pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IR)); 1614c06356bSdh return (-1); 1624c06356bSdh } 1634c06356bSdh 1644c06356bSdh /* 1654c06356bSdh * Generate offsets for the General System, Inbound Queue Configuration 1664c06356bSdh * and Outbound Queue configuration tables. This way the macros to 1674c06356bSdh * access those tables will work correctly. 1684c06356bSdh */ 1694c06356bSdh pwp->mpi_gst_offset = 1704c06356bSdh pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_GSTO); 1714c06356bSdh pwp->mpi_iqc_offset = 1724c06356bSdh pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IQCTO); 1734c06356bSdh pwp->mpi_oqc_offset = 1744c06356bSdh pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_OQCTO); 1754c06356bSdh 1762ac4abe8SDavid Hollister pmcs_get_fw_version(pwp); 1774c06356bSdh 1784c06356bSdh pwp->max_cmd = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_MOIO); 1794c06356bSdh pwp->max_dev = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO0) >> 16; 1804c06356bSdh 1814c06356bSdh pwp->max_iq = PMCS_MNIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1)); 1824c06356bSdh pwp->max_oq = PMCS_MNOQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1)); 1834c06356bSdh pwp->nphy = PMCS_NPHY(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1)); 1844c06356bSdh if (pwp->max_iq <= PMCS_NIQ) { 185c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 186c3bc407cSdh "%s: not enough Inbound Queues supported " 187c3bc407cSdh "(need %d, max_oq=%d)", __func__, pwp->max_iq, PMCS_NIQ); 1884c06356bSdh return (-1); 1894c06356bSdh } 1904c06356bSdh if (pwp->max_oq <= PMCS_NOQ) { 191c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 192c3bc407cSdh "%s: not enough Outbound Queues supported " 193c3bc407cSdh "(need %d, max_oq=%d)", __func__, pwp->max_oq, PMCS_NOQ); 1944c06356bSdh return (-1); 1954c06356bSdh } 1964c06356bSdh if (pwp->nphy == 0) { 197c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 198c3bc407cSdh "%s: zero phys reported", __func__); 1994c06356bSdh return (-1); 2004c06356bSdh } 2014c06356bSdh if (PMCS_HPIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1))) { 2024c06356bSdh pwp->hipri_queue = (1 << PMCS_IQ_OTHER); 2034c06356bSdh } 2044c06356bSdh 2054c06356bSdh 2064c06356bSdh for (i = 0; i < pwp->nphy; i++) { 2074c06356bSdh PMCS_MPI_EVQSET(pwp, PMCS_OQ_EVENTS, i); 2084c06356bSdh PMCS_MPI_NCQSET(pwp, PMCS_OQ_EVENTS, i); 2094c06356bSdh } 2104c06356bSdh 2114c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_INFO2, 2124c06356bSdh (PMCS_OQ_EVENTS << GENERAL_EVENT_OQ_SHIFT) | 2134c06356bSdh (PMCS_OQ_EVENTS << DEVICE_HANDLE_REMOVED_SHIFT)); 2144c06356bSdh 2154c06356bSdh /* 2164c06356bSdh * Verify that ioq_depth is valid (> 0 and not so high that it 2174c06356bSdh * would cause us to overrun the chip with commands). 2184c06356bSdh */ 2194c06356bSdh if (pwp->ioq_depth == 0) { 220c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2214c06356bSdh "%s: I/O queue depth set to 0. Setting to %d", 2224c06356bSdh __func__, PMCS_NQENTRY); 2234c06356bSdh pwp->ioq_depth = PMCS_NQENTRY; 2244c06356bSdh } 2254c06356bSdh 2264c06356bSdh if (pwp->ioq_depth < PMCS_MIN_NQENTRY) { 227c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2284c06356bSdh "%s: I/O queue depth set too low (%d). Setting to %d", 2294c06356bSdh __func__, pwp->ioq_depth, PMCS_MIN_NQENTRY); 2304c06356bSdh pwp->ioq_depth = PMCS_MIN_NQENTRY; 2314c06356bSdh } 2324c06356bSdh 2334c06356bSdh if (pwp->ioq_depth > (pwp->max_cmd / (PMCS_IO_IQ_MASK + 1))) { 2344c06356bSdh new_ioq_depth = pwp->max_cmd / (PMCS_IO_IQ_MASK + 1); 235c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2364c06356bSdh "%s: I/O queue depth set too high (%d). Setting to %d", 2374c06356bSdh __func__, pwp->ioq_depth, new_ioq_depth); 2384c06356bSdh pwp->ioq_depth = new_ioq_depth; 2394c06356bSdh } 2404c06356bSdh 2414c06356bSdh /* 2424c06356bSdh * Allocate consistent memory for OQs and IQs. 2434c06356bSdh */ 2444c06356bSdh pwp->iqp_dma_attr = pwp->oqp_dma_attr = pmcs_dattr; 2454c06356bSdh pwp->iqp_dma_attr.dma_attr_align = 2464c06356bSdh pwp->oqp_dma_attr.dma_attr_align = PMCS_QENTRY_SIZE; 2474c06356bSdh 2484c06356bSdh /* 2494c06356bSdh * The Rev C chip has the ability to do PIO to or from consistent 2504c06356bSdh * memory anywhere in a 64 bit address space, but the firmware is 2514c06356bSdh * not presently set up to do so. 2524c06356bSdh */ 2534c06356bSdh pwp->iqp_dma_attr.dma_attr_addr_hi = 2544c06356bSdh pwp->oqp_dma_attr.dma_attr_addr_hi = 0x000000FFFFFFFFFFull; 2554c06356bSdh 2564c06356bSdh for (i = 0; i < PMCS_NIQ; i++) { 2574c06356bSdh if (pmcs_dma_setup(pwp, &pwp->iqp_dma_attr, 2584c06356bSdh &pwp->iqp_acchdls[i], 2594c06356bSdh &pwp->iqp_handles[i], PMCS_QENTRY_SIZE * pwp->ioq_depth, 2604c06356bSdh (caddr_t *)&pwp->iqp[i], &pwp->iqaddr[i]) == B_FALSE) { 261c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2624c06356bSdh "Failed to setup DMA for iqp[%d]", i); 2634c06356bSdh return (-1); 2644c06356bSdh } 2654c06356bSdh bzero(pwp->iqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth); 2664c06356bSdh } 2674c06356bSdh 2684c06356bSdh for (i = 0; i < PMCS_NOQ; i++) { 2694c06356bSdh if (pmcs_dma_setup(pwp, &pwp->oqp_dma_attr, 2704c06356bSdh &pwp->oqp_acchdls[i], 2714c06356bSdh &pwp->oqp_handles[i], PMCS_QENTRY_SIZE * pwp->ioq_depth, 2724c06356bSdh (caddr_t *)&pwp->oqp[i], &pwp->oqaddr[i]) == B_FALSE) { 273c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2744c06356bSdh "Failed to setup DMA for oqp[%d]", i); 2754c06356bSdh return (-1); 2764c06356bSdh } 2774c06356bSdh bzero(pwp->oqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth); 2784c06356bSdh } 2794c06356bSdh 2804c06356bSdh /* 2814c06356bSdh * Install the IQ and OQ addresses (and null out the rest). 2824c06356bSdh */ 2834c06356bSdh for (i = 0; i < pwp->max_iq; i++) { 2844c06356bSdh pwp->iqpi_offset[i] = pmcs_rd_iqc_tbl(pwp, PMCS_IQPIOFFX(i)); 2854c06356bSdh if (i < PMCS_NIQ) { 2864c06356bSdh if (i != PMCS_IQ_OTHER) { 2874c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 2884c06356bSdh pwp->ioq_depth | (PMCS_QENTRY_SIZE << 16)); 2894c06356bSdh } else { 2904c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 2914c06356bSdh (1 << 30) | pwp->ioq_depth | 2924c06356bSdh (PMCS_QENTRY_SIZE << 16)); 2934c06356bSdh } 2944c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 2954c06356bSdh DWORD1(pwp->iqaddr[i])); 2964c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 2974c06356bSdh DWORD0(pwp->iqaddr[i])); 2984c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 2994c06356bSdh DWORD1(pwp->ciaddr+IQ_OFFSET(i))); 3004c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 3014c06356bSdh DWORD0(pwp->ciaddr+IQ_OFFSET(i))); 3024c06356bSdh } else { 3034c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 0); 3044c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 0); 3054c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 0); 3064c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 0); 3074c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 0); 3084c06356bSdh } 3094c06356bSdh } 3104c06356bSdh 3114c06356bSdh for (i = 0; i < pwp->max_oq; i++) { 3124c06356bSdh pwp->oqci_offset[i] = pmcs_rd_oqc_tbl(pwp, PMCS_OQCIOFFX(i)); 3134c06356bSdh if (i < PMCS_NOQ) { 3144c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), pwp->ioq_depth | 3154c06356bSdh (PMCS_QENTRY_SIZE << 16) | OQIEX); 3164c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 3174c06356bSdh DWORD1(pwp->oqaddr[i])); 3184c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 3194c06356bSdh DWORD0(pwp->oqaddr[i])); 3204c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 3214c06356bSdh DWORD1(pwp->ciaddr+OQ_OFFSET(i))); 3224c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 3234c06356bSdh DWORD0(pwp->ciaddr+OQ_OFFSET(i))); 3244c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 3254c06356bSdh pwp->oqvec[i] << 24); 3264c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0); 3274c06356bSdh } else { 3284c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), 0); 3294c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 0); 3304c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 0); 3314c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 0); 3324c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 0); 3334c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 0); 3344c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0); 3354c06356bSdh } 3364c06356bSdh } 3374c06356bSdh 3384c06356bSdh /* 3394c06356bSdh * Set up logging, if defined. 3404c06356bSdh */ 3414c06356bSdh if (pwp->fwlog) { 3424c06356bSdh uint64_t logdma = pwp->fwaddr; 3434c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBAH, DWORD1(logdma)); 3444c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBAL, DWORD0(logdma)); 3454c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBS, PMCS_FWLOG_SIZE >> 1); 3464c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELSEV, pwp->fwlog); 3474c06356bSdh logdma += (PMCS_FWLOG_SIZE >> 1); 3484c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBAH, DWORD1(logdma)); 3494c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBAL, DWORD0(logdma)); 3504c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBS, PMCS_FWLOG_SIZE >> 1); 3514c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELSEV, pwp->fwlog); 3524c06356bSdh } 3534c06356bSdh 3544c06356bSdh /* 3554c06356bSdh * Interrupt vectors, outbound queues, and odb_auto_clear 3564c06356bSdh * 3574c06356bSdh * MSI/MSI-X: 3584c06356bSdh * If we got 4 interrupt vectors, we'll assign one to each outbound 3594c06356bSdh * queue as well as the fatal interrupt, and auto clear can be set 3604c06356bSdh * for each. 3614c06356bSdh * 3624c06356bSdh * If we only got 2 vectors, one will be used for I/O completions 3634c06356bSdh * and the other for the other two vectors. In this case, auto_ 3644c06356bSdh * clear can only be set for I/Os, which is fine. The fatal 3654c06356bSdh * interrupt will be mapped to the PMCS_FATAL_INTERRUPT bit, which 3664c06356bSdh * is not an interrupt vector. 3674c06356bSdh * 3684c06356bSdh * MSI/MSI-X/INT-X: 3694c06356bSdh * If we only got 1 interrupt vector, auto_clear must be set to 0, 3704c06356bSdh * and again the fatal interrupt will be mapped to the 3714c06356bSdh * PMCS_FATAL_INTERRUPT bit (again, not an interrupt vector). 3724c06356bSdh */ 3734c06356bSdh 3744c06356bSdh switch (pwp->int_type) { 3754c06356bSdh case PMCS_INT_MSIX: 3764c06356bSdh case PMCS_INT_MSI: 3774c06356bSdh switch (pwp->intr_cnt) { 3784c06356bSdh case 1: 3794c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE | 3804c06356bSdh (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT)); 3814c06356bSdh pwp->odb_auto_clear = 0; 3824c06356bSdh break; 3834c06356bSdh case 2: 3844c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE | 3854c06356bSdh (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT)); 3864c06356bSdh pwp->odb_auto_clear = (1 << PMCS_FATAL_INTERRUPT) | 3874c06356bSdh (1 << PMCS_MSIX_IODONE); 3884c06356bSdh break; 3894c06356bSdh case 4: 3904c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE | 3914c06356bSdh (PMCS_MSIX_FATAL << PMCS_FERIV_SHIFT)); 3924c06356bSdh pwp->odb_auto_clear = (1 << PMCS_MSIX_FATAL) | 3934c06356bSdh (1 << PMCS_MSIX_GENERAL) | (1 << PMCS_MSIX_IODONE) | 3944c06356bSdh (1 << PMCS_MSIX_EVENTS); 3954c06356bSdh break; 3964c06356bSdh } 3974c06356bSdh break; 3984c06356bSdh 3994c06356bSdh case PMCS_INT_FIXED: 4004c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, 4014c06356bSdh PMCS_FERRIE | (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT)); 4024c06356bSdh pwp->odb_auto_clear = 0; 4034c06356bSdh break; 4044c06356bSdh } 4054c06356bSdh 406658280b6SDavid Hollister /* 407658280b6SDavid Hollister * If the open retry interval is non-zero, set it. 408658280b6SDavid Hollister */ 409658280b6SDavid Hollister if (pwp->open_retry_interval != 0) { 410658280b6SDavid Hollister int phynum; 411658280b6SDavid Hollister 412658280b6SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 413658280b6SDavid Hollister "%s: Setting open retry interval to %d usecs", __func__, 414658280b6SDavid Hollister pwp->open_retry_interval); 415658280b6SDavid Hollister for (phynum = 0; phynum < pwp->nphy; phynum ++) { 416658280b6SDavid Hollister pmcs_wr_gsm_reg(pwp, OPEN_RETRY_INTERVAL(phynum), 417658280b6SDavid Hollister pwp->open_retry_interval); 418658280b6SDavid Hollister } 419658280b6SDavid Hollister } 420658280b6SDavid Hollister 4214c06356bSdh /* 4224c06356bSdh * Enable Interrupt Reassertion 4234c06356bSdh * Default Delay 1000us 4244c06356bSdh */ 4254c06356bSdh ferr = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_FERR); 4264c06356bSdh if ((ferr & PMCS_MPI_IRAE) == 0) { 4274c06356bSdh ferr &= ~(PMCS_MPI_IRAU | PMCS_MPI_IRAD_MASK); 4284c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, ferr | PMCS_MPI_IRAE); 4294c06356bSdh } 4304c06356bSdh 4314c06356bSdh pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR, pwp->odb_auto_clear); 4324c06356bSdh pwp->mpi_table_setup = 1; 4334c06356bSdh return (0); 4344c06356bSdh } 4354c06356bSdh 4364c06356bSdh /* 4374c06356bSdh * Start the Message Passing protocol with the PMC chip. 4384c06356bSdh */ 4394c06356bSdh int 4404c06356bSdh pmcs_start_mpi(pmcs_hw_t *pwp) 4414c06356bSdh { 4424c06356bSdh int i; 4434c06356bSdh 4444c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_IBDB, PMCS_MSGU_IBDB_MPIINI); 4454c06356bSdh for (i = 0; i < 1000; i++) { 4464c06356bSdh if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & 4474c06356bSdh PMCS_MSGU_IBDB_MPIINI) == 0) { 4484c06356bSdh break; 4494c06356bSdh } 4504c06356bSdh drv_usecwait(1000); 4514c06356bSdh } 4524c06356bSdh if (pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & PMCS_MSGU_IBDB_MPIINI) { 4534c06356bSdh return (-1); 4544c06356bSdh } 4554c06356bSdh drv_usecwait(500000); 4564c06356bSdh 4574c06356bSdh /* 4584c06356bSdh * Check to make sure we got to INIT state. 4594c06356bSdh */ 4604c06356bSdh if (PMCS_MPI_S(pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE)) != 4614c06356bSdh PMCS_MPI_STATE_INIT) { 462c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 463c3bc407cSdh "%s: MPI launch failed (GST 0x%x DBCLR 0x%x)", __func__, 4644c06356bSdh pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE), 4654c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB_CLEAR)); 4664c06356bSdh return (-1); 4674c06356bSdh } 4684c06356bSdh return (0); 4694c06356bSdh } 4704c06356bSdh 4714c06356bSdh /* 4724c06356bSdh * Stop the Message Passing protocol with the PMC chip. 4734c06356bSdh */ 4744c06356bSdh int 4754c06356bSdh pmcs_stop_mpi(pmcs_hw_t *pwp) 4764c06356bSdh { 4774c06356bSdh int i; 4784c06356bSdh 4794c06356bSdh for (i = 0; i < pwp->max_iq; i++) { 4804c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 0); 4814c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 0); 4824c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 0); 4834c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 0); 4844c06356bSdh pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 0); 4854c06356bSdh } 4864c06356bSdh for (i = 0; i < pwp->max_oq; i++) { 4874c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), 0); 4884c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 0); 4894c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 0); 4904c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 0); 4914c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 0); 4924c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 0); 4934c06356bSdh pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0); 4944c06356bSdh } 4954c06356bSdh pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, 0); 4964c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_IBDB, PMCS_MSGU_IBDB_MPICTU); 4974c06356bSdh for (i = 0; i < 2000; i++) { 4984c06356bSdh if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & 4994c06356bSdh PMCS_MSGU_IBDB_MPICTU) == 0) { 5004c06356bSdh break; 5014c06356bSdh } 5024c06356bSdh drv_usecwait(1000); 5034c06356bSdh } 5044c06356bSdh if (pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & PMCS_MSGU_IBDB_MPICTU) { 505c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 506c3bc407cSdh "%s: MPI stop failed", __func__); 5074c06356bSdh return (-1); 5084c06356bSdh } 5094c06356bSdh return (0); 5104c06356bSdh } 5114c06356bSdh 5124c06356bSdh /* 5134c06356bSdh * Do a sequence of ECHO messages to test for MPI functionality, 5144c06356bSdh * all inbound and outbound queue functionality and interrupts. 5154c06356bSdh */ 5164c06356bSdh int 5174c06356bSdh pmcs_echo_test(pmcs_hw_t *pwp) 5184c06356bSdh { 5194c06356bSdh echo_test_t fred; 5204c06356bSdh struct pmcwork *pwrk; 5214c06356bSdh uint32_t *msg, count; 5224c06356bSdh int iqe = 0, iqo = 0, result, rval = 0; 5234c06356bSdh int iterations; 5244c06356bSdh hrtime_t echo_start, echo_end, echo_total; 5254c06356bSdh 5264c06356bSdh ASSERT(pwp->max_cmd > 0); 5274c06356bSdh 5284c06356bSdh /* 5294c06356bSdh * We want iterations to be max_cmd * 3 to ensure that we run the 5304c06356bSdh * echo test enough times to iterate through every inbound queue 5314c06356bSdh * at least twice. 5324c06356bSdh */ 5334c06356bSdh iterations = pwp->max_cmd * 3; 5344c06356bSdh 5354c06356bSdh echo_total = 0; 5364c06356bSdh count = 0; 5374c06356bSdh 5384c06356bSdh while (count < iterations) { 5394c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL); 5404c06356bSdh if (pwrk == NULL) { 541c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 542c3bc407cSdh pmcs_nowrk, __func__); 5434c06356bSdh rval = -1; 5444c06356bSdh break; 5454c06356bSdh } 5464c06356bSdh 5474c06356bSdh mutex_enter(&pwp->iqp_lock[iqe]); 5484c06356bSdh msg = GET_IQ_ENTRY(pwp, iqe); 5494c06356bSdh if (msg == NULL) { 5504c06356bSdh mutex_exit(&pwp->iqp_lock[iqe]); 5514c06356bSdh pmcs_pwork(pwp, pwrk); 552c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 553c3bc407cSdh pmcs_nomsg, __func__); 5544c06356bSdh rval = -1; 5554c06356bSdh break; 5564c06356bSdh } 5574c06356bSdh 5584c06356bSdh bzero(msg, PMCS_QENTRY_SIZE); 5594c06356bSdh 5604c06356bSdh if (iqe == PMCS_IQ_OTHER) { 5614c06356bSdh /* This is on the high priority queue */ 5624c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, iqo, PMCIN_ECHO)); 5634c06356bSdh } else { 5644c06356bSdh msg[0] = LE_32(PMCS_IOMB_IN_SAS(iqo, PMCIN_ECHO)); 5654c06356bSdh } 5664c06356bSdh msg[1] = LE_32(pwrk->htag); 5674c06356bSdh fred.signature = 0xdeadbeef; 5684c06356bSdh fred.count = count; 5694c06356bSdh fred.ptr = &count; 5704c06356bSdh (void) memcpy(&msg[2], &fred, sizeof (fred)); 5714c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 5724c06356bSdh 5734c06356bSdh INC_IQ_ENTRY(pwp, iqe); 5744c06356bSdh 5754c06356bSdh echo_start = gethrtime(); 5764c06356bSdh DTRACE_PROBE2(pmcs__echo__test__wait__start, 5774c06356bSdh hrtime_t, echo_start, uint32_t, pwrk->htag); 5784c06356bSdh 5794c06356bSdh if (++iqe == PMCS_NIQ) { 5804c06356bSdh iqe = 0; 5814c06356bSdh } 5824c06356bSdh if (++iqo == PMCS_NOQ) { 5834c06356bSdh iqo = 0; 5844c06356bSdh } 5854c06356bSdh 5864c06356bSdh WAIT_FOR(pwrk, 250, result); 587*3be32c0fSJesse Butler pmcs_pwork(pwp, pwrk); 5884c06356bSdh 5894c06356bSdh echo_end = gethrtime(); 5904c06356bSdh DTRACE_PROBE2(pmcs__echo__test__wait__end, 5914c06356bSdh hrtime_t, echo_end, int, result); 5924c06356bSdh echo_total += (echo_end - echo_start); 5934c06356bSdh 5944c06356bSdh if (result) { 595c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 5964c06356bSdh "%s: command timed out on echo test #%d", 5974c06356bSdh __func__, count); 5984c06356bSdh rval = -1; 5994c06356bSdh break; 6004c06356bSdh } 6014c06356bSdh } 6024c06356bSdh 6034c06356bSdh /* 6044c06356bSdh * The intr_threshold is adjusted by PMCS_INTR_THRESHOLD in order to 6054c06356bSdh * remove the overhead of things like the delay in getting signaled 6064c06356bSdh * for completion. 6074c06356bSdh */ 6084c06356bSdh if (echo_total != 0) { 6094c06356bSdh pwp->io_intr_coal.intr_latency = 6104c06356bSdh (echo_total / iterations) / 2; 6114c06356bSdh pwp->io_intr_coal.intr_threshold = 6124c06356bSdh PMCS_INTR_THRESHOLD(PMCS_QUANTUM_TIME_USECS * 1000 / 6134c06356bSdh pwp->io_intr_coal.intr_latency); 6144c06356bSdh } 6154c06356bSdh 6164c06356bSdh return (rval); 6174c06356bSdh } 6184c06356bSdh 6194c06356bSdh /* 6204c06356bSdh * Start the (real) phys 6214c06356bSdh */ 6224c06356bSdh int 6234c06356bSdh pmcs_start_phy(pmcs_hw_t *pwp, int phynum, int linkmode, int speed) 6244c06356bSdh { 6254c06356bSdh int result; 6264c06356bSdh uint32_t *msg; 6274c06356bSdh struct pmcwork *pwrk; 6284c06356bSdh pmcs_phy_t *pptr; 6294c06356bSdh sas_identify_af_t sap; 6304c06356bSdh 6314c06356bSdh mutex_enter(&pwp->lock); 6324c06356bSdh pptr = pwp->root_phys + phynum; 6334c06356bSdh if (pptr == NULL) { 6344c06356bSdh mutex_exit(&pwp->lock); 635c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 636c3bc407cSdh "%s: cannot find port %d", __func__, phynum); 6374c06356bSdh return (0); 6384c06356bSdh } 6394c06356bSdh 6404c06356bSdh pmcs_lock_phy(pptr); 6414c06356bSdh mutex_exit(&pwp->lock); 6424c06356bSdh 6434c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 6444c06356bSdh if (pwrk == NULL) { 6454c06356bSdh pmcs_unlock_phy(pptr); 646c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__); 6474c06356bSdh return (-1); 6484c06356bSdh } 6494c06356bSdh 6504c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 6514c06356bSdh msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 6524c06356bSdh 6534c06356bSdh if (msg == NULL) { 6544c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 6554c06356bSdh pmcs_unlock_phy(pptr); 6564c06356bSdh pmcs_pwork(pwp, pwrk); 657c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__); 6584c06356bSdh return (-1); 6594c06356bSdh } 6604c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_PHY_START)); 6614c06356bSdh msg[1] = LE_32(pwrk->htag); 6624c06356bSdh msg[2] = LE_32(linkmode | speed | phynum); 6634c06356bSdh bzero(&sap, sizeof (sap)); 6644c06356bSdh sap.device_type = SAS_IF_DTYPE_ENDPOINT; 6654c06356bSdh sap.ssp_ini_port = 1; 6664c06356bSdh 6674c06356bSdh if (pwp->separate_ports) { 6684c06356bSdh pmcs_wwn2barray(pwp->sas_wwns[phynum], sap.sas_address); 6694c06356bSdh } else { 6704c06356bSdh pmcs_wwn2barray(pwp->sas_wwns[0], sap.sas_address); 6714c06356bSdh } 6724c06356bSdh 6734c06356bSdh ASSERT(phynum < SAS2_PHYNUM_MAX); 6744c06356bSdh sap.phy_identifier = phynum & SAS2_PHYNUM_MASK; 6754c06356bSdh (void) memcpy(&msg[3], &sap, sizeof (sas_identify_af_t)); 6764c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 6774c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 6784c06356bSdh 6794c06356bSdh pptr->state.prog_min_rate = (lowbit((ulong_t)speed) - 1); 6804c06356bSdh pptr->state.prog_max_rate = (highbit((ulong_t)speed) - 1); 6814c06356bSdh pptr->state.hw_min_rate = PMCS_HW_MIN_LINK_RATE; 6824c06356bSdh pptr->state.hw_max_rate = PMCS_HW_MAX_LINK_RATE; 6834c06356bSdh 6844c06356bSdh pmcs_unlock_phy(pptr); 6854c06356bSdh WAIT_FOR(pwrk, 1000, result); 6864c06356bSdh pmcs_pwork(pwp, pwrk); 6874c06356bSdh 6884c06356bSdh if (result) { 6896745c559SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__); 6904c06356bSdh } else { 6914c06356bSdh mutex_enter(&pwp->lock); 6924c06356bSdh pwp->phys_started |= (1 << phynum); 6934c06356bSdh mutex_exit(&pwp->lock); 6944c06356bSdh } 6954c06356bSdh 6964c06356bSdh return (0); 6974c06356bSdh } 6984c06356bSdh 6994c06356bSdh int 7004c06356bSdh pmcs_start_phys(pmcs_hw_t *pwp) 7014c06356bSdh { 7021f81b464SDavid Hollister int i, rval; 7034c06356bSdh 7044c06356bSdh for (i = 0; i < pwp->nphy; i++) { 7054c06356bSdh if ((pwp->phyid_block_mask & (1 << i)) == 0) { 7064c06356bSdh if (pmcs_start_phy(pwp, i, 7074c06356bSdh (pwp->phymode << PHY_MODE_SHIFT), 7084c06356bSdh pwp->physpeed << PHY_LINK_SHIFT)) { 7094c06356bSdh return (-1); 7104c06356bSdh } 7114c06356bSdh if (pmcs_clear_diag_counters(pwp, i)) { 712c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 713c3bc407cSdh "%s: failed to reset counters on PHY (%d)", 714c3bc407cSdh __func__, i); 7154c06356bSdh } 7164c06356bSdh } 7174c06356bSdh } 7181f81b464SDavid Hollister 7191f81b464SDavid Hollister rval = pmcs_get_time_stamp(pwp, &pwp->fw_timestamp, &pwp->hrtimestamp); 7201f81b464SDavid Hollister if (rval) { 7211f81b464SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 7221f81b464SDavid Hollister "%s: Failed to obtain firmware timestamp", __func__); 7231f81b464SDavid Hollister } else { 7241f81b464SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 7251f81b464SDavid Hollister "Firmware timestamp: 0x%" PRIx64, pwp->fw_timestamp); 7261f81b464SDavid Hollister } 7271f81b464SDavid Hollister 7284c06356bSdh return (0); 7294c06356bSdh } 7304c06356bSdh 7314c06356bSdh /* 7324c06356bSdh * Called with PHY locked 7334c06356bSdh */ 7344c06356bSdh int 7354c06356bSdh pmcs_reset_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint8_t type) 7364c06356bSdh { 7374c06356bSdh uint32_t *msg; 7384c06356bSdh uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2]; 7394c06356bSdh const char *mbar; 7404c06356bSdh uint32_t amt; 7414c06356bSdh uint32_t pdevid; 7424c06356bSdh uint32_t stsoff; 7434c06356bSdh uint32_t status; 7444c06356bSdh int result, level, phynum; 7454c06356bSdh struct pmcwork *pwrk; 746*3be32c0fSJesse Butler pmcs_iport_t *iport; 7474c06356bSdh uint32_t htag; 7484c06356bSdh 7494c06356bSdh ASSERT(mutex_owned(&pptr->phy_lock)); 7504c06356bSdh 7514c06356bSdh bzero(iomb, PMCS_QENTRY_SIZE); 7524c06356bSdh phynum = pptr->phynum; 7534c06356bSdh level = pptr->level; 7544c06356bSdh if (level > 0) { 7554c06356bSdh pdevid = pptr->parent->device_id; 756601c90f1SSrikanth, Ramana } else if ((level == 0) && (pptr->dtype == EXPANDER)) { 757601c90f1SSrikanth, Ramana pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, pptr->target, 758601c90f1SSrikanth, Ramana "%s: Not resetting HBA PHY @ %s", __func__, pptr->path); 759601c90f1SSrikanth, Ramana return (0); 7604c06356bSdh } 7614c06356bSdh 7629aed1621SDavid Hollister if (!pptr->iport || !pptr->valid_device_id) { 7639aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, pptr->target, 7649aed1621SDavid Hollister "%s: Can't reach PHY %s", __func__, pptr->path); 7659aed1621SDavid Hollister return (0); 7669aed1621SDavid Hollister } 7679aed1621SDavid Hollister 7684c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 7694c06356bSdh 7704c06356bSdh if (pwrk == NULL) { 771c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__); 7724c06356bSdh return (ENOMEM); 7734c06356bSdh } 7744c06356bSdh 7754c06356bSdh pwrk->arg = iomb; 7764c06356bSdh 7774c06356bSdh /* 7784c06356bSdh * If level > 0, we need to issue an SMP_REQUEST with a PHY_CONTROL 7794c06356bSdh * function to do either a link reset or hard reset. If level == 0, 7804c06356bSdh * then we do a LOCAL_PHY_CONTROL IOMB to do link/hard reset to the 7814c06356bSdh * root (local) PHY 7824c06356bSdh */ 7834c06356bSdh if (level) { 7844c06356bSdh stsoff = 2; 7854c06356bSdh iomb[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 7864c06356bSdh PMCIN_SMP_REQUEST)); 7874c06356bSdh iomb[1] = LE_32(pwrk->htag); 7884c06356bSdh iomb[2] = LE_32(pdevid); 7894c06356bSdh iomb[3] = LE_32(40 << SMP_REQUEST_LENGTH_SHIFT); 7904c06356bSdh /* 7914c06356bSdh * Send SMP PHY CONTROL/HARD or LINK RESET 7924c06356bSdh */ 7934c06356bSdh iomb[4] = BE_32(0x40910000); 7944c06356bSdh iomb[5] = 0; 7954c06356bSdh 7964c06356bSdh if (type == PMCS_PHYOP_HARD_RESET) { 7974c06356bSdh mbar = "SMP PHY CONTROL/HARD RESET"; 798c80dec56SDavid Hollister iomb[6] = BE_32((phynum << 16) | 799c80dec56SDavid Hollister (PMCS_PHYOP_HARD_RESET << 8)); 8004c06356bSdh } else { 8014c06356bSdh mbar = "SMP PHY CONTROL/LINK RESET"; 802c80dec56SDavid Hollister iomb[6] = BE_32((phynum << 16) | 803c80dec56SDavid Hollister (PMCS_PHYOP_LINK_RESET << 8)); 8044c06356bSdh } 805c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 8064c06356bSdh "%s: sending %s to %s for phy 0x%x", 8074c06356bSdh __func__, mbar, pptr->parent->path, pptr->phynum); 8084c06356bSdh amt = 7; 8094c06356bSdh } else { 8104c06356bSdh /* 8114c06356bSdh * Unlike most other Outbound messages, status for 8124c06356bSdh * a local phy operation is in DWORD 3. 8134c06356bSdh */ 8144c06356bSdh stsoff = 3; 8154c06356bSdh iomb[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 8164c06356bSdh PMCIN_LOCAL_PHY_CONTROL)); 8174c06356bSdh iomb[1] = LE_32(pwrk->htag); 8184c06356bSdh if (type == PMCS_PHYOP_LINK_RESET) { 8194c06356bSdh mbar = "LOCAL PHY LINK RESET"; 8204c06356bSdh iomb[2] = LE_32((PMCS_PHYOP_LINK_RESET << 8) | phynum); 8214c06356bSdh } else { 8224c06356bSdh mbar = "LOCAL PHY HARD RESET"; 8234c06356bSdh iomb[2] = LE_32((PMCS_PHYOP_HARD_RESET << 8) | phynum); 8244c06356bSdh } 825c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 8264c06356bSdh "%s: sending %s to %s", __func__, mbar, pptr->path); 8274c06356bSdh amt = 3; 8284c06356bSdh } 8294c06356bSdh 8304c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 8314c06356bSdh msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 8324c06356bSdh if (msg == NULL) { 8334c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 8344c06356bSdh pmcs_pwork(pwp, pwrk); 835c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__); 8364c06356bSdh return (ENOMEM); 8374c06356bSdh } 8384c06356bSdh COPY_MESSAGE(msg, iomb, amt); 8394c06356bSdh htag = pwrk->htag; 8406745c559SJesse Butler 841*3be32c0fSJesse Butler pmcs_hold_iport(pptr->iport); 842*3be32c0fSJesse Butler iport = pptr->iport; 843*3be32c0fSJesse Butler pmcs_smp_acquire(iport); 8444c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 8454c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 8464c06356bSdh pmcs_unlock_phy(pptr); 8474c06356bSdh WAIT_FOR(pwrk, 1000, result); 8484c06356bSdh pmcs_pwork(pwp, pwrk); 849*3be32c0fSJesse Butler pmcs_smp_release(iport); 850*3be32c0fSJesse Butler pmcs_rele_iport(iport); 851601c90f1SSrikanth, Ramana pmcs_lock_phy(pptr); 8526745c559SJesse Butler 8534c06356bSdh if (result) { 8546745c559SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__); 8554c06356bSdh 8564c06356bSdh if (pmcs_abort(pwp, pptr, htag, 0, 0)) { 857c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 8584c06356bSdh "%s: Unable to issue SMP abort for htag 0x%08x", 8594c06356bSdh __func__, htag); 8604c06356bSdh } else { 861c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 8624c06356bSdh "%s: Issuing SMP ABORT for htag 0x%08x", 8634c06356bSdh __func__, htag); 8644c06356bSdh } 8654c06356bSdh return (EIO); 8664c06356bSdh } 8674c06356bSdh status = LE_32(iomb[stsoff]); 8684c06356bSdh 8694c06356bSdh if (status != PMCOUT_STATUS_OK) { 8704c06356bSdh char buf[32]; 8714c06356bSdh const char *es = pmcs_status_str(status); 8724c06356bSdh if (es == NULL) { 8734c06356bSdh (void) snprintf(buf, sizeof (buf), "Status 0x%x", 8744c06356bSdh status); 8754c06356bSdh es = buf; 8764c06356bSdh } 877c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 8784c06356bSdh "%s: %s action returned %s for %s", __func__, mbar, es, 8794c06356bSdh pptr->path); 880601c90f1SSrikanth, Ramana return (status); 8814c06356bSdh } 8824c06356bSdh 8834c06356bSdh return (0); 8844c06356bSdh } 8854c06356bSdh 8864c06356bSdh /* 8874c06356bSdh * Stop the (real) phys. No PHY or softstate locks are required as this only 8884c06356bSdh * happens during detach. 8894c06356bSdh */ 8904c06356bSdh void 8914c06356bSdh pmcs_stop_phy(pmcs_hw_t *pwp, int phynum) 8924c06356bSdh { 8934c06356bSdh int result; 8944c06356bSdh pmcs_phy_t *pptr; 8954c06356bSdh uint32_t *msg; 8964c06356bSdh struct pmcwork *pwrk; 8974c06356bSdh 8984c06356bSdh pptr = pwp->root_phys + phynum; 8994c06356bSdh if (pptr == NULL) { 900c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 9014c06356bSdh "%s: unable to find port %d", __func__, phynum); 9024c06356bSdh return; 9034c06356bSdh } 9044c06356bSdh 9054c06356bSdh if (pwp->phys_started & (1 << phynum)) { 9064c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 9074c06356bSdh 9084c06356bSdh if (pwrk == NULL) { 909c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, 910c3bc407cSdh pmcs_nowrk, __func__); 9114c06356bSdh return; 9124c06356bSdh } 9134c06356bSdh 9144c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 9154c06356bSdh msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 9164c06356bSdh 9174c06356bSdh if (msg == NULL) { 9184c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 9194c06356bSdh pmcs_pwork(pwp, pwrk); 920c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, 921c3bc407cSdh pmcs_nomsg, __func__); 9224c06356bSdh return; 9234c06356bSdh } 9244c06356bSdh 9254c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_PHY_STOP)); 9264c06356bSdh msg[1] = LE_32(pwrk->htag); 9274c06356bSdh msg[2] = LE_32(phynum); 9284c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 9294c06356bSdh /* 9304c06356bSdh * Make this unconfigured now. 9314c06356bSdh */ 9324c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 9334c06356bSdh WAIT_FOR(pwrk, 1000, result); 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 pmcs_pwork(pwp, pwrk); 9994c06356bSdh if (result) { 10004c06356bSdh pmcs_timed_out(pwp, htag, __func__); 10014c06356bSdh return (DDI_FAILURE); 10024c06356bSdh } 10034c06356bSdh 10044c06356bSdh status = LE_32(msg[3]); 10054c06356bSdh 10064c06356bSdh /* Return for counter reset */ 10074c06356bSdh if (cmd == PMCS_ERR_CNT_RESET) 10084c06356bSdh return (status); 10094c06356bSdh 10104c06356bSdh /* Return for counter value */ 10114c06356bSdh if (status) { 1012c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1013c3bc407cSdh "%s: failed, status (0x%x)", __func__, status); 10144c06356bSdh return (DDI_FAILURE); 10154c06356bSdh } 10164c06356bSdh return (LE_32(msg[4])); 10174c06356bSdh } 10184c06356bSdh 10194c06356bSdh /* Get the current value of the counter for desc on phynum and return it. */ 10204c06356bSdh int 10214c06356bSdh pmcs_get_diag_report(pmcs_hw_t *pwp, uint32_t desc, uint8_t phynum) 10224c06356bSdh { 10234c06356bSdh return (pmcs_sas_diag_execute(pwp, PMCS_DIAG_REPORT_GET, desc, phynum)); 10244c06356bSdh } 10254c06356bSdh 10264c06356bSdh /* Clear all of the counters for phynum. Returns the status of the command. */ 10274c06356bSdh int 10284c06356bSdh pmcs_clear_diag_counters(pmcs_hw_t *pwp, uint8_t phynum) 10294c06356bSdh { 10304c06356bSdh uint32_t cmd = PMCS_ERR_CNT_RESET; 10314c06356bSdh uint32_t cmd_desc; 10324c06356bSdh 10334c06356bSdh cmd_desc = PMCS_INVALID_DWORD_CNT; 10344c06356bSdh if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum)) 10354c06356bSdh return (DDI_FAILURE); 10364c06356bSdh 10374c06356bSdh cmd_desc = PMCS_DISPARITY_ERR_CNT; 10384c06356bSdh if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum)) 10394c06356bSdh return (DDI_FAILURE); 10404c06356bSdh 10414c06356bSdh cmd_desc = PMCS_LOST_DWORD_SYNC_CNT; 10424c06356bSdh if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum)) 10434c06356bSdh return (DDI_FAILURE); 10444c06356bSdh 10454c06356bSdh cmd_desc = PMCS_RESET_FAILED_CNT; 10464c06356bSdh if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum)) 10474c06356bSdh return (DDI_FAILURE); 10484c06356bSdh 10494c06356bSdh return (DDI_SUCCESS); 10504c06356bSdh } 10514c06356bSdh 10524c06356bSdh /* 10534c06356bSdh * Get firmware timestamp 10544c06356bSdh */ 10551f81b464SDavid Hollister static int 10561f81b464SDavid Hollister pmcs_get_time_stamp(pmcs_hw_t *pwp, uint64_t *fw_ts, hrtime_t *sys_hr_ts) 10574c06356bSdh { 10584c06356bSdh uint32_t htag, *ptr, msg[PMCS_MSG_SIZE << 1]; 10594c06356bSdh int result; 10604c06356bSdh struct pmcwork *pwrk; 10614c06356bSdh 10624c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL); 10634c06356bSdh if (pwrk == NULL) { 1064c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nowrk, __func__); 10654c06356bSdh return (-1); 10664c06356bSdh } 10674c06356bSdh pwrk->arg = msg; 10684c06356bSdh htag = pwrk->htag; 10694c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_GET_TIME_STAMP)); 10704c06356bSdh msg[1] = LE_32(pwrk->htag); 10714c06356bSdh 10724c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 10734c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 10744c06356bSdh if (ptr == NULL) { 10754c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 10764c06356bSdh pmcs_pwork(pwp, pwrk); 1077c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nomsg, __func__); 10784c06356bSdh return (-1); 10794c06356bSdh } 10804c06356bSdh COPY_MESSAGE(ptr, msg, 2); 10814c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 10824c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 10834c06356bSdh 10844c06356bSdh WAIT_FOR(pwrk, 1000, result); 10854c06356bSdh pmcs_pwork(pwp, pwrk); 10864c06356bSdh if (result) { 10874c06356bSdh pmcs_timed_out(pwp, htag, __func__); 10884c06356bSdh return (-1); 10894c06356bSdh } 10901f81b464SDavid Hollister 10911f81b464SDavid Hollister mutex_enter(&pmcs_trace_lock); 10921f81b464SDavid Hollister *sys_hr_ts = gethrtime(); 10931f81b464SDavid Hollister gethrestime(&pwp->sys_timestamp); 10941f81b464SDavid Hollister *fw_ts = LE_32(msg[2]) | (((uint64_t)LE_32(msg[3])) << 32); 10951f81b464SDavid Hollister mutex_exit(&pmcs_trace_lock); 10964c06356bSdh return (0); 10974c06356bSdh } 10984c06356bSdh 10994c06356bSdh /* 11004c06356bSdh * Dump all pertinent registers 11014c06356bSdh */ 11024c06356bSdh 11034c06356bSdh void 11044c06356bSdh pmcs_register_dump(pmcs_hw_t *pwp) 11054c06356bSdh { 11064c06356bSdh int i; 11074c06356bSdh uint32_t val; 11084c06356bSdh 1109c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "pmcs%d: Register dump start", 11104c06356bSdh ddi_get_instance(pwp->dip)); 1111c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, 11124c06356bSdh "OBDB (intr): 0x%08x (mask): 0x%08x (clear): 0x%08x", 11134c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB), 11144c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB_MASK), 11154c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR)); 1116c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH0: 0x%08x", 11174c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH0)); 1118c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH1: 0x%08x", 11194c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1)); 1120c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH2: 0x%08x", 11214c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2)); 1122c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH3: 0x%08x", 11234c06356bSdh pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH3)); 11244c06356bSdh for (i = 0; i < PMCS_NIQ; i++) { 1125c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "IQ %d: CI %u PI %u", 11264c06356bSdh i, pmcs_rd_iqci(pwp, i), pmcs_rd_iqpi(pwp, i)); 11274c06356bSdh } 11284c06356bSdh for (i = 0; i < PMCS_NOQ; i++) { 1129c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "OQ %d: CI %u PI %u", 11304c06356bSdh i, pmcs_rd_oqci(pwp, i), pmcs_rd_oqpi(pwp, i)); 11314c06356bSdh } 11324c06356bSdh val = pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE); 1133c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, 11344c06356bSdh "GST TABLE BASE: 0x%08x (STATE=0x%x QF=%d GSTLEN=%d HMI_ERR=0x%x)", 11354c06356bSdh val, PMCS_MPI_S(val), PMCS_QF(val), PMCS_GSTLEN(val) * 4, 11364c06356bSdh PMCS_HMI_ERR(val)); 1137c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE IQFRZ0: 0x%08x", 11384c06356bSdh pmcs_rd_gst_tbl(pwp, PMCS_GST_IQFRZ0)); 1139c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE IQFRZ1: 0x%08x", 11404c06356bSdh pmcs_rd_gst_tbl(pwp, PMCS_GST_IQFRZ1)); 1141c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE MSGU TICK: 0x%08x", 11424c06356bSdh pmcs_rd_gst_tbl(pwp, PMCS_GST_MSGU_TICK)); 1143c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE IOP TICK: 0x%08x", 11444c06356bSdh pmcs_rd_gst_tbl(pwp, PMCS_GST_IOP_TICK)); 11454c06356bSdh for (i = 0; i < pwp->nphy; i++) { 11464c06356bSdh uint32_t rerrf, pinfo, started = 0, link = 0; 11474c06356bSdh pinfo = pmcs_rd_gst_tbl(pwp, PMCS_GST_PHY_INFO(i)); 11484c06356bSdh if (pinfo & 1) { 11494c06356bSdh started = 1; 11504c06356bSdh link = pinfo & 2; 11514c06356bSdh } 11524c06356bSdh rerrf = pmcs_rd_gst_tbl(pwp, PMCS_GST_RERR_INFO(i)); 1153c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, 11544c06356bSdh "GST TABLE PHY%d STARTED=%d LINK=%d RERR=0x%08x", 11554c06356bSdh i, started, link, rerrf); 11564c06356bSdh } 1157c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "pmcs%d: Register dump end", 11584c06356bSdh ddi_get_instance(pwp->dip)); 11594c06356bSdh } 11604c06356bSdh 11614c06356bSdh /* 11624c06356bSdh * Handle SATA Abort and other error processing 11634c06356bSdh */ 11644c06356bSdh int 11654c06356bSdh pmcs_abort_handler(pmcs_hw_t *pwp) 11664c06356bSdh { 11674c06356bSdh pmcs_phy_t *pptr, *pnext, *pnext_uplevel[PMCS_MAX_XPND]; 1168601c90f1SSrikanth, Ramana pmcs_xscsi_t *tgt; 11694c06356bSdh int r, level = 0; 11704c06356bSdh 1171c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s", __func__); 11724c06356bSdh 11734c06356bSdh mutex_enter(&pwp->lock); 11744c06356bSdh pptr = pwp->root_phys; 11754c06356bSdh mutex_exit(&pwp->lock); 11764c06356bSdh 11774c06356bSdh while (pptr) { 11784c06356bSdh /* 11794c06356bSdh * XXX: Need to make sure this doesn't happen 11804c06356bSdh * XXX: when non-NCQ commands are running. 11814c06356bSdh */ 11824c06356bSdh pmcs_lock_phy(pptr); 11834c06356bSdh if (pptr->need_rl_ext) { 11844c06356bSdh ASSERT(pptr->dtype == SATA); 11854c06356bSdh if (pmcs_acquire_scratch(pwp, B_FALSE)) { 11864c06356bSdh goto next_phy; 11874c06356bSdh } 11884c06356bSdh r = pmcs_sata_abort_ncq(pwp, pptr); 11894c06356bSdh pmcs_release_scratch(pwp); 11904c06356bSdh if (r == ENOMEM) { 11914c06356bSdh goto next_phy; 11924c06356bSdh } 11934c06356bSdh if (r) { 11944c06356bSdh r = pmcs_reset_phy(pwp, pptr, 11954c06356bSdh PMCS_PHYOP_LINK_RESET); 11964c06356bSdh if (r == ENOMEM) { 11974c06356bSdh goto next_phy; 11984c06356bSdh } 11994c06356bSdh /* what if other failures happened? */ 12004c06356bSdh pptr->abort_pending = 1; 12014c06356bSdh pptr->abort_sent = 0; 12024c06356bSdh } 12034c06356bSdh } 12044c06356bSdh if (pptr->abort_pending == 0 || pptr->abort_sent) { 12054c06356bSdh goto next_phy; 12064c06356bSdh } 12074c06356bSdh pptr->abort_pending = 0; 12084c06356bSdh if (pmcs_abort(pwp, pptr, pptr->device_id, 1, 1) == ENOMEM) { 12094c06356bSdh pptr->abort_pending = 1; 12104c06356bSdh goto next_phy; 12114c06356bSdh } 12124c06356bSdh pptr->abort_sent = 1; 12134c06356bSdh 1214601c90f1SSrikanth, Ramana /* 1215601c90f1SSrikanth, Ramana * If the iport is no longer active, flush the queues 1216601c90f1SSrikanth, Ramana */ 1217601c90f1SSrikanth, Ramana if ((pptr->iport == NULL) || 1218601c90f1SSrikanth, Ramana (pptr->iport->ua_state != UA_ACTIVE)) { 1219601c90f1SSrikanth, Ramana tgt = pptr->target; 1220*3be32c0fSJesse Butler if (tgt != NULL) { 1221601c90f1SSrikanth, Ramana pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, tgt, 1222601c90f1SSrikanth, Ramana "%s: Clearing target 0x%p, inactive iport", 1223601c90f1SSrikanth, Ramana __func__, (void *) tgt); 1224601c90f1SSrikanth, Ramana mutex_enter(&tgt->statlock); 1225601c90f1SSrikanth, Ramana pmcs_clear_xp(pwp, tgt); 1226601c90f1SSrikanth, Ramana mutex_exit(&tgt->statlock); 1227601c90f1SSrikanth, Ramana } 1228601c90f1SSrikanth, Ramana } 1229601c90f1SSrikanth, Ramana 12304c06356bSdh next_phy: 12314c06356bSdh if (pptr->children) { 12324c06356bSdh pnext = pptr->children; 12334c06356bSdh pnext_uplevel[level++] = pptr->sibling; 12344c06356bSdh } else { 12354c06356bSdh pnext = pptr->sibling; 12364c06356bSdh while ((pnext == NULL) && (level > 0)) { 12374c06356bSdh pnext = pnext_uplevel[--level]; 12384c06356bSdh } 12394c06356bSdh } 12404c06356bSdh 12414c06356bSdh pmcs_unlock_phy(pptr); 12424c06356bSdh pptr = pnext; 12434c06356bSdh } 12444c06356bSdh 12454c06356bSdh return (0); 12464c06356bSdh } 12474c06356bSdh 12484c06356bSdh /* 12494c06356bSdh * Register a device (get a device handle for it). 12504c06356bSdh * Called with PHY lock held. 12514c06356bSdh */ 12524c06356bSdh int 12534c06356bSdh pmcs_register_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 12544c06356bSdh { 12554c06356bSdh struct pmcwork *pwrk; 12564c06356bSdh int result = 0; 12574c06356bSdh uint32_t *msg; 12584c06356bSdh uint32_t tmp, status; 12594c06356bSdh uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2]; 12604c06356bSdh 12614c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 12624c06356bSdh msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 12634c06356bSdh 12644c06356bSdh if (msg == NULL || 12654c06356bSdh (pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr)) == NULL) { 12664c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 12674c06356bSdh result = ENOMEM; 12684c06356bSdh goto out; 12694c06356bSdh } 12704c06356bSdh 12714c06356bSdh pwrk->arg = iomb; 12724c06356bSdh pwrk->dtype = pptr->dtype; 12734c06356bSdh 12744c06356bSdh msg[1] = LE_32(pwrk->htag); 12754c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_REGISTER_DEVICE)); 12764c06356bSdh tmp = PMCS_DEVREG_TLR | 12774c06356bSdh (pptr->link_rate << PMCS_DEVREG_LINK_RATE_SHIFT); 12784c06356bSdh if (IS_ROOT_PHY(pptr)) { 12794c06356bSdh msg[2] = LE_32(pptr->portid | 12804c06356bSdh (pptr->phynum << PMCS_PHYID_SHIFT)); 12814c06356bSdh } else { 12824c06356bSdh msg[2] = LE_32(pptr->portid); 12834c06356bSdh } 12844c06356bSdh if (pptr->dtype == SATA) { 12854c06356bSdh if (IS_ROOT_PHY(pptr)) { 12864c06356bSdh tmp |= PMCS_DEVREG_TYPE_SATA_DIRECT; 12874c06356bSdh } else { 12884c06356bSdh tmp |= PMCS_DEVREG_TYPE_SATA; 12894c06356bSdh } 12904c06356bSdh } else { 12914c06356bSdh tmp |= PMCS_DEVREG_TYPE_SAS; 12924c06356bSdh } 12934c06356bSdh msg[3] = LE_32(tmp); 12944c06356bSdh msg[4] = LE_32(PMCS_DEVREG_IT_NEXUS_TIMEOUT); 12954c06356bSdh (void) memcpy(&msg[5], pptr->sas_address, 8); 12964c06356bSdh 12974c06356bSdh CLEAN_MESSAGE(msg, 7); 12984c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 12994c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 13004c06356bSdh 13014c06356bSdh pmcs_unlock_phy(pptr); 13024c06356bSdh WAIT_FOR(pwrk, 250, result); 13034c06356bSdh pmcs_pwork(pwp, pwrk); 1304*3be32c0fSJesse Butler pmcs_lock_phy(pptr); 13054c06356bSdh 13064c06356bSdh if (result) { 13076745c559SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__); 13084c06356bSdh result = ETIMEDOUT; 13094c06356bSdh goto out; 13104c06356bSdh } 13114c06356bSdh status = LE_32(iomb[2]); 13124c06356bSdh tmp = LE_32(iomb[3]); 13134c06356bSdh switch (status) { 13144c06356bSdh case PMCS_DEVREG_OK: 13154c06356bSdh case PMCS_DEVREG_DEVICE_ALREADY_REGISTERED: 13164c06356bSdh case PMCS_DEVREG_PHY_ALREADY_REGISTERED: 13174c06356bSdh if (pmcs_validate_devid(pwp->root_phys, pptr, tmp) == B_FALSE) { 13184c06356bSdh result = EEXIST; 13194c06356bSdh goto out; 13204c06356bSdh } else if (status != PMCS_DEVREG_OK) { 13214c06356bSdh if (tmp == 0xffffffff) { /* F/W bug */ 1322c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, pptr, NULL, 13234c06356bSdh "%s: phy %s already has bogus devid 0x%x", 13244c06356bSdh __func__, pptr->path, tmp); 13254c06356bSdh result = EIO; 13264c06356bSdh goto out; 13274c06356bSdh } else { 1328c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, pptr, NULL, 13294c06356bSdh "%s: phy %s already has a device id 0x%x", 13304c06356bSdh __func__, pptr->path, tmp); 13314c06356bSdh } 13324c06356bSdh } 13334c06356bSdh break; 13344c06356bSdh default: 1335c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 1336c3bc407cSdh "%s: status 0x%x when trying to register device %s", 1337c3bc407cSdh __func__, status, pptr->path); 13384c06356bSdh result = EIO; 13394c06356bSdh goto out; 13404c06356bSdh } 13414c06356bSdh pptr->device_id = tmp; 13424c06356bSdh pptr->valid_device_id = 1; 1343c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "Phy %s/" SAS_ADDR_FMT 13444c06356bSdh " registered with device_id 0x%x (portid %d)", pptr->path, 13454c06356bSdh SAS_ADDR_PRT(pptr->sas_address), tmp, pptr->portid); 13464c06356bSdh out: 13474c06356bSdh return (result); 13484c06356bSdh } 13494c06356bSdh 13504c06356bSdh /* 13514c06356bSdh * Deregister a device (remove a device handle). 13524c06356bSdh * Called with PHY locked. 13534c06356bSdh */ 13544c06356bSdh void 13554c06356bSdh pmcs_deregister_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 13564c06356bSdh { 13574c06356bSdh struct pmcwork *pwrk; 13584c06356bSdh uint32_t msg[PMCS_MSG_SIZE], *ptr, status; 13594c06356bSdh uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2]; 13604c06356bSdh int result; 13614c06356bSdh 13624c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 13634c06356bSdh if (pwrk == NULL) { 13644c06356bSdh return; 13654c06356bSdh } 13664c06356bSdh 13674c06356bSdh pwrk->arg = iomb; 13684c06356bSdh pwrk->dtype = pptr->dtype; 13694c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 13704c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 13714c06356bSdh if (ptr == NULL) { 13724c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 13734c06356bSdh pmcs_pwork(pwp, pwrk); 13744c06356bSdh return; 13754c06356bSdh } 13764c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 13774c06356bSdh PMCIN_DEREGISTER_DEVICE_HANDLE)); 13784c06356bSdh msg[1] = LE_32(pwrk->htag); 13794c06356bSdh msg[2] = LE_32(pptr->device_id); 13804c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 13814c06356bSdh COPY_MESSAGE(ptr, msg, 3); 13824c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 13834c06356bSdh 13844c06356bSdh pmcs_unlock_phy(pptr); 13854c06356bSdh WAIT_FOR(pwrk, 250, result); 13864c06356bSdh pmcs_pwork(pwp, pwrk); 13874c06356bSdh pmcs_lock_phy(pptr); 13884c06356bSdh 13894c06356bSdh if (result) { 13906745c559SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__); 13914c06356bSdh return; 13924c06356bSdh } 13934c06356bSdh status = LE_32(iomb[2]); 13944c06356bSdh if (status != PMCOUT_STATUS_OK) { 1395c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 1396c3bc407cSdh "%s: status 0x%x when trying to deregister device %s", 1397c3bc407cSdh __func__, status, pptr->path); 13984c06356bSdh } else { 1399c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 1400c3bc407cSdh "%s: device %s deregistered", __func__, pptr->path); 14014c06356bSdh } 1402*3be32c0fSJesse Butler 1403*3be32c0fSJesse Butler pptr->device_id = PMCS_INVALID_DEVICE_ID; 1404*3be32c0fSJesse Butler pptr->configured = 0; 1405*3be32c0fSJesse Butler pptr->deregister_wait = 0; 1406*3be32c0fSJesse Butler pptr->valid_device_id = 0; 14074c06356bSdh } 14084c06356bSdh 14094c06356bSdh /* 14104c06356bSdh * Deregister all registered devices. 14114c06356bSdh */ 14124c06356bSdh void 14134c06356bSdh pmcs_deregister_devices(pmcs_hw_t *pwp, pmcs_phy_t *phyp) 14144c06356bSdh { 14154c06356bSdh /* 14164c06356bSdh * Start at the maximum level and walk back to level 0. This only 14174c06356bSdh * gets done during detach after all threads and timers have been 14181b94a41bSChris Horne * destroyed. 14194c06356bSdh */ 14204c06356bSdh while (phyp) { 14214c06356bSdh if (phyp->children) { 14224c06356bSdh pmcs_deregister_devices(pwp, phyp->children); 14234c06356bSdh } 14241b94a41bSChris Horne pmcs_lock_phy(phyp); 14254c06356bSdh if (phyp->valid_device_id) { 14264c06356bSdh pmcs_deregister_device(pwp, phyp); 14274c06356bSdh } 14281b94a41bSChris Horne pmcs_unlock_phy(phyp); 14294c06356bSdh phyp = phyp->sibling; 14304c06356bSdh } 14314c06356bSdh } 14324c06356bSdh 14334c06356bSdh /* 14344c06356bSdh * Perform a 'soft' reset on the PMC chip 14354c06356bSdh */ 14364c06356bSdh int 14374c06356bSdh pmcs_soft_reset(pmcs_hw_t *pwp, boolean_t no_restart) 14384c06356bSdh { 14394c06356bSdh uint32_t s2, sfrbits, gsm, rapchk, wapchk, wdpchk, spc, tsmode; 14404c06356bSdh pmcs_phy_t *pptr; 14414c06356bSdh char *msg = NULL; 14424c06356bSdh int i; 14434c06356bSdh 14444c06356bSdh /* 14454c06356bSdh * Disable interrupts 14464c06356bSdh */ 14474c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff); 14484c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff); 14494c06356bSdh 1450c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "%s", __func__); 14514c06356bSdh 14524c06356bSdh if (pwp->locks_initted) { 14534c06356bSdh mutex_enter(&pwp->lock); 14544c06356bSdh } 14554c06356bSdh pwp->blocked = 1; 14564c06356bSdh 14575c45adf0SJesse Butler /* 14585c45adf0SJesse Butler * Clear our softstate copies of the MSGU and IOP heartbeats. 14595c45adf0SJesse Butler */ 14605c45adf0SJesse Butler pwp->last_msgu_tick = pwp->last_iop_tick = 0; 14615c45adf0SJesse Butler 14624c06356bSdh /* 14634c06356bSdh * Step 1 14644c06356bSdh */ 14654c06356bSdh s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2); 14664c06356bSdh if ((s2 & PMCS_MSGU_HOST_SOFT_RESET_READY) == 0) { 14674c06356bSdh pmcs_wr_gsm_reg(pwp, RB6_ACCESS, RB6_NMI_SIGNATURE); 14684c06356bSdh pmcs_wr_gsm_reg(pwp, RB6_ACCESS, RB6_NMI_SIGNATURE); 14694c06356bSdh for (i = 0; i < 100; i++) { 14704c06356bSdh s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) & 14714c06356bSdh PMCS_MSGU_HOST_SOFT_RESET_READY; 14724c06356bSdh if (s2) { 14734c06356bSdh break; 14744c06356bSdh } 14754c06356bSdh drv_usecwait(10000); 14764c06356bSdh } 14774c06356bSdh s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) & 14784c06356bSdh PMCS_MSGU_HOST_SOFT_RESET_READY; 14794c06356bSdh if (s2 == 0) { 1480c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1481c3bc407cSdh "%s: PMCS_MSGU_HOST_SOFT_RESET_READY never came " 1482c3bc407cSdh "ready", __func__); 14834c06356bSdh pmcs_register_dump(pwp); 14844c06356bSdh if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) & 14854c06356bSdh PMCS_MSGU_CPU_SOFT_RESET_READY) == 0 || 14864c06356bSdh (pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) & 14874c06356bSdh PMCS_MSGU_CPU_SOFT_RESET_READY) == 0) { 14884c06356bSdh pwp->state = STATE_DEAD; 14894c06356bSdh pwp->blocked = 0; 14904c06356bSdh if (pwp->locks_initted) { 14914c06356bSdh mutex_exit(&pwp->lock); 14924c06356bSdh } 14934c06356bSdh return (-1); 14944c06356bSdh } 14954c06356bSdh } 14964c06356bSdh } 14974c06356bSdh 14984c06356bSdh /* 14994c06356bSdh * Step 2 15004c06356bSdh */ 15014c06356bSdh pmcs_wr_gsm_reg(pwp, NMI_EN_VPE0_IOP, 0); 15024c06356bSdh drv_usecwait(10); 15034c06356bSdh pmcs_wr_gsm_reg(pwp, NMI_EN_VPE0_AAP1, 0); 15044c06356bSdh drv_usecwait(10); 15054c06356bSdh pmcs_wr_topunit(pwp, PMCS_EVENT_INT_ENABLE, 0); 15064c06356bSdh drv_usecwait(10); 15074c06356bSdh pmcs_wr_topunit(pwp, PMCS_EVENT_INT_STAT, 15084c06356bSdh pmcs_rd_topunit(pwp, PMCS_EVENT_INT_STAT)); 15094c06356bSdh drv_usecwait(10); 15104c06356bSdh pmcs_wr_topunit(pwp, PMCS_ERROR_INT_ENABLE, 0); 15114c06356bSdh drv_usecwait(10); 15124c06356bSdh pmcs_wr_topunit(pwp, PMCS_ERROR_INT_STAT, 15134c06356bSdh pmcs_rd_topunit(pwp, PMCS_ERROR_INT_STAT)); 15144c06356bSdh drv_usecwait(10); 15154c06356bSdh 15164c06356bSdh sfrbits = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) & 15174c06356bSdh PMCS_MSGU_AAP_SFR_PROGRESS; 15184c06356bSdh sfrbits ^= PMCS_MSGU_AAP_SFR_PROGRESS; 1519c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "PMCS_MSGU_HOST_SCRATCH0 " 1520c3bc407cSdh "%08x -> %08x", pmcs_rd_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH0), 1521c3bc407cSdh HST_SFT_RESET_SIG); 15224c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH0, HST_SFT_RESET_SIG); 15234c06356bSdh 15244c06356bSdh /* 15254c06356bSdh * Step 3 15264c06356bSdh */ 15272ac4abe8SDavid Hollister gsm = pmcs_rd_gsm_reg(pwp, 0, GSM_CFG_AND_RESET); 1528c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "GSM %08x -> %08x", gsm, 15294c06356bSdh gsm & ~PMCS_SOFT_RESET_BITS); 15304c06356bSdh pmcs_wr_gsm_reg(pwp, GSM_CFG_AND_RESET, gsm & ~PMCS_SOFT_RESET_BITS); 15314c06356bSdh 15324c06356bSdh /* 15334c06356bSdh * Step 4 15344c06356bSdh */ 15352ac4abe8SDavid Hollister rapchk = pmcs_rd_gsm_reg(pwp, 0, READ_ADR_PARITY_CHK_EN); 1536c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "READ_ADR_PARITY_CHK_EN " 1537c3bc407cSdh "%08x -> %08x", rapchk, 0); 15384c06356bSdh pmcs_wr_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN, 0); 15392ac4abe8SDavid Hollister wapchk = pmcs_rd_gsm_reg(pwp, 0, WRITE_ADR_PARITY_CHK_EN); 1540c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_ADR_PARITY_CHK_EN " 1541c3bc407cSdh "%08x -> %08x", wapchk, 0); 15424c06356bSdh pmcs_wr_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN, 0); 15432ac4abe8SDavid Hollister wdpchk = pmcs_rd_gsm_reg(pwp, 0, WRITE_DATA_PARITY_CHK_EN); 1544c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_DATA_PARITY_CHK_EN " 1545c3bc407cSdh "%08x -> %08x", wdpchk, 0); 15464c06356bSdh pmcs_wr_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN, 0); 15474c06356bSdh 15484c06356bSdh /* 15494c06356bSdh * Step 5 15504c06356bSdh */ 15514c06356bSdh drv_usecwait(100); 15524c06356bSdh 15534c06356bSdh /* 15544c06356bSdh * Step 5.5 (Temporary workaround for 1.07.xx Beta) 15554c06356bSdh */ 15562ac4abe8SDavid Hollister tsmode = pmcs_rd_gsm_reg(pwp, 0, PMCS_GPIO_TRISTATE_MODE_ADDR); 1557c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "GPIO TSMODE %08x -> %08x", 1558c3bc407cSdh tsmode, tsmode & ~(PMCS_GPIO_TSMODE_BIT0|PMCS_GPIO_TSMODE_BIT1)); 15594c06356bSdh pmcs_wr_gsm_reg(pwp, PMCS_GPIO_TRISTATE_MODE_ADDR, 15604c06356bSdh tsmode & ~(PMCS_GPIO_TSMODE_BIT0|PMCS_GPIO_TSMODE_BIT1)); 15614c06356bSdh drv_usecwait(10); 15624c06356bSdh 15634c06356bSdh /* 15644c06356bSdh * Step 6 15654c06356bSdh */ 15664c06356bSdh spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET); 1567c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x", 1568c3bc407cSdh spc, spc & ~(PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB)); 15694c06356bSdh pmcs_wr_topunit(pwp, PMCS_SPC_RESET, 15704c06356bSdh spc & ~(PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB)); 15714c06356bSdh drv_usecwait(10); 15724c06356bSdh 15734c06356bSdh /* 15744c06356bSdh * Step 7 15754c06356bSdh */ 15764c06356bSdh spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET); 1577c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x", 1578c3bc407cSdh spc, spc & ~(BDMA_CORE_RSTB|OSSP_RSTB)); 15794c06356bSdh pmcs_wr_topunit(pwp, PMCS_SPC_RESET, spc & ~(BDMA_CORE_RSTB|OSSP_RSTB)); 15804c06356bSdh 15814c06356bSdh /* 15824c06356bSdh * Step 8 15834c06356bSdh */ 15844c06356bSdh drv_usecwait(100); 15854c06356bSdh 15864c06356bSdh /* 15874c06356bSdh * Step 9 15884c06356bSdh */ 15894c06356bSdh spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET); 1590c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x", 1591c3bc407cSdh spc, spc | (BDMA_CORE_RSTB|OSSP_RSTB)); 15924c06356bSdh pmcs_wr_topunit(pwp, PMCS_SPC_RESET, spc | (BDMA_CORE_RSTB|OSSP_RSTB)); 15934c06356bSdh 15944c06356bSdh /* 15954c06356bSdh * Step 10 15964c06356bSdh */ 15974c06356bSdh drv_usecwait(100); 15984c06356bSdh 15994c06356bSdh /* 16004c06356bSdh * Step 11 16014c06356bSdh */ 16022ac4abe8SDavid Hollister gsm = pmcs_rd_gsm_reg(pwp, 0, GSM_CFG_AND_RESET); 1603c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "GSM %08x -> %08x", gsm, 16044c06356bSdh gsm | PMCS_SOFT_RESET_BITS); 16054c06356bSdh pmcs_wr_gsm_reg(pwp, GSM_CFG_AND_RESET, gsm | PMCS_SOFT_RESET_BITS); 16064c06356bSdh drv_usecwait(10); 16074c06356bSdh 16084c06356bSdh /* 16094c06356bSdh * Step 12 16104c06356bSdh */ 1611c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "READ_ADR_PARITY_CHK_EN " 16122ac4abe8SDavid Hollister "%08x -> %08x", pmcs_rd_gsm_reg(pwp, 0, READ_ADR_PARITY_CHK_EN), 1613c3bc407cSdh rapchk); 16144c06356bSdh pmcs_wr_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN, rapchk); 16154c06356bSdh drv_usecwait(10); 1616c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_ADR_PARITY_CHK_EN " 16172ac4abe8SDavid Hollister "%08x -> %08x", pmcs_rd_gsm_reg(pwp, 0, WRITE_ADR_PARITY_CHK_EN), 1618c3bc407cSdh wapchk); 16194c06356bSdh pmcs_wr_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN, wapchk); 16204c06356bSdh drv_usecwait(10); 1621c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_DATA_PARITY_CHK_EN " 16222ac4abe8SDavid Hollister "%08x -> %08x", pmcs_rd_gsm_reg(pwp, 0, WRITE_DATA_PARITY_CHK_EN), 1623c3bc407cSdh wapchk); 16244c06356bSdh pmcs_wr_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN, wdpchk); 16254c06356bSdh drv_usecwait(10); 16264c06356bSdh 16274c06356bSdh /* 16284c06356bSdh * Step 13 16294c06356bSdh */ 16304c06356bSdh spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET); 1631c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x", 1632c3bc407cSdh spc, spc | (PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB)); 16334c06356bSdh pmcs_wr_topunit(pwp, PMCS_SPC_RESET, 16344c06356bSdh spc | (PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB)); 16354c06356bSdh 16364c06356bSdh /* 16374c06356bSdh * Step 14 16384c06356bSdh */ 16394c06356bSdh drv_usecwait(100); 16404c06356bSdh 16414c06356bSdh /* 16424c06356bSdh * Step 15 16434c06356bSdh */ 16444c06356bSdh for (spc = 0, i = 0; i < 1000; i++) { 16454c06356bSdh drv_usecwait(1000); 16464c06356bSdh spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1); 16474c06356bSdh if ((spc & PMCS_MSGU_AAP_SFR_PROGRESS) == sfrbits) { 16484c06356bSdh break; 16494c06356bSdh } 16504c06356bSdh } 16514c06356bSdh 16524c06356bSdh if ((spc & PMCS_MSGU_AAP_SFR_PROGRESS) != sfrbits) { 1653c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 16544c06356bSdh "SFR didn't toggle (sfr 0x%x)", spc); 16554c06356bSdh pwp->state = STATE_DEAD; 16564c06356bSdh pwp->blocked = 0; 16574c06356bSdh if (pwp->locks_initted) { 16584c06356bSdh mutex_exit(&pwp->lock); 16594c06356bSdh } 16604c06356bSdh return (-1); 16614c06356bSdh } 16624c06356bSdh 16634c06356bSdh /* 16644c06356bSdh * Step 16 16654c06356bSdh */ 16664c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff); 16674c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff); 16684c06356bSdh 16694c06356bSdh /* 16704c06356bSdh * Wait for up to 5 seconds for AAP state to come either ready or error. 16714c06356bSdh */ 16724c06356bSdh for (i = 0; i < 50; i++) { 16734c06356bSdh spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) & 16744c06356bSdh PMCS_MSGU_AAP_STATE_MASK; 16754c06356bSdh if (spc == PMCS_MSGU_AAP_STATE_ERROR || 16764c06356bSdh spc == PMCS_MSGU_AAP_STATE_READY) { 16774c06356bSdh break; 16784c06356bSdh } 16794c06356bSdh drv_usecwait(100000); 16804c06356bSdh } 16814c06356bSdh spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1); 16824c06356bSdh if ((spc & PMCS_MSGU_AAP_STATE_MASK) != PMCS_MSGU_AAP_STATE_READY) { 1683c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 16844c06356bSdh "soft reset failed (state 0x%x)", spc); 16854c06356bSdh pwp->state = STATE_DEAD; 16864c06356bSdh pwp->blocked = 0; 16874c06356bSdh if (pwp->locks_initted) { 16884c06356bSdh mutex_exit(&pwp->lock); 16894c06356bSdh } 16904c06356bSdh return (-1); 16914c06356bSdh } 16924c06356bSdh 16935c45adf0SJesse Butler /* Clear the firmware log */ 16945c45adf0SJesse Butler if (pwp->fwlogp) { 16955c45adf0SJesse Butler bzero(pwp->fwlogp, PMCS_FWLOG_SIZE); 16965c45adf0SJesse Butler } 16975c45adf0SJesse Butler 16985c45adf0SJesse Butler /* Reset our queue indices and entries */ 16995c45adf0SJesse Butler bzero(pwp->shadow_iqpi, sizeof (pwp->shadow_iqpi)); 17005c45adf0SJesse Butler bzero(pwp->last_iqci, sizeof (pwp->last_iqci)); 1701d78a6b7eSJesse Butler bzero(pwp->last_htag, sizeof (pwp->last_htag)); 17025c45adf0SJesse Butler for (i = 0; i < PMCS_NIQ; i++) { 17035c45adf0SJesse Butler if (pwp->iqp[i]) { 17045c45adf0SJesse Butler bzero(pwp->iqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth); 17055c45adf0SJesse Butler pmcs_wr_iqpi(pwp, i, 0); 17065c45adf0SJesse Butler pmcs_wr_iqci(pwp, i, 0); 17075c45adf0SJesse Butler } 17085c45adf0SJesse Butler } 17095c45adf0SJesse Butler for (i = 0; i < PMCS_NOQ; i++) { 17105c45adf0SJesse Butler if (pwp->oqp[i]) { 17115c45adf0SJesse Butler bzero(pwp->oqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth); 17125c45adf0SJesse Butler pmcs_wr_oqpi(pwp, i, 0); 17135c45adf0SJesse Butler pmcs_wr_oqci(pwp, i, 0); 17145c45adf0SJesse Butler } 17155c45adf0SJesse Butler 17165c45adf0SJesse Butler } 17174c06356bSdh 17184c06356bSdh if (pwp->state == STATE_DEAD || pwp->state == STATE_UNPROBING || 17194c06356bSdh pwp->state == STATE_PROBING || pwp->locks_initted == 0) { 17204c06356bSdh pwp->blocked = 0; 17214c06356bSdh if (pwp->locks_initted) { 17224c06356bSdh mutex_exit(&pwp->lock); 17234c06356bSdh } 17244c06356bSdh return (0); 17254c06356bSdh } 17264c06356bSdh 17274c06356bSdh /* 17284c06356bSdh * Return at this point if we dont need to startup. 17294c06356bSdh */ 17304c06356bSdh if (no_restart) { 17314c06356bSdh return (0); 17324c06356bSdh } 17334c06356bSdh 17344c06356bSdh ASSERT(pwp->locks_initted != 0); 17354c06356bSdh 17364c06356bSdh /* 17375c45adf0SJesse Butler * Flush the target queues and clear each target's PHY 17384c06356bSdh */ 17394c06356bSdh if (pwp->targets) { 17404c06356bSdh for (i = 0; i < pwp->max_dev; i++) { 17414c06356bSdh pmcs_xscsi_t *xp = pwp->targets[i]; 17424c06356bSdh 17434c06356bSdh if (xp == NULL) { 17444c06356bSdh continue; 17454c06356bSdh } 17465c45adf0SJesse Butler 17474c06356bSdh mutex_enter(&xp->statlock); 17485c45adf0SJesse Butler pmcs_flush_target_queues(pwp, xp, PMCS_TGT_ALL_QUEUES); 17495c45adf0SJesse Butler xp->phy = NULL; 17504c06356bSdh mutex_exit(&xp->statlock); 17514c06356bSdh } 17524c06356bSdh } 17534c06356bSdh 17544c06356bSdh /* 17555c45adf0SJesse Butler * Zero out the ports list, free non root phys, clear root phys 17564c06356bSdh */ 17575c45adf0SJesse Butler bzero(pwp->ports, sizeof (pwp->ports)); 17585c45adf0SJesse Butler pmcs_free_all_phys(pwp, pwp->root_phys); 17595c45adf0SJesse Butler for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) { 17605c45adf0SJesse Butler pmcs_lock_phy(pptr); 17615c45adf0SJesse Butler pmcs_clear_phy(pwp, pptr); 17625c45adf0SJesse Butler pptr->target = NULL; 17635c45adf0SJesse Butler pmcs_unlock_phy(pptr); 17644c06356bSdh } 17654c06356bSdh 17664c06356bSdh /* 17674c06356bSdh * Restore Interrupt Mask 17684c06356bSdh */ 17694c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, pwp->intr_mask); 17704c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff); 17714c06356bSdh 17724c06356bSdh pwp->mpi_table_setup = 0; 17734c06356bSdh mutex_exit(&pwp->lock); 17744c06356bSdh 17754c06356bSdh /* 17764c06356bSdh * Set up MPI again. 17774c06356bSdh */ 17784c06356bSdh if (pmcs_setup(pwp)) { 17794c06356bSdh msg = "unable to setup MPI tables again"; 17804c06356bSdh goto fail_restart; 17814c06356bSdh } 17824c06356bSdh pmcs_report_fwversion(pwp); 17834c06356bSdh 17844c06356bSdh /* 17854c06356bSdh * Restart MPI 17864c06356bSdh */ 17874c06356bSdh if (pmcs_start_mpi(pwp)) { 17884c06356bSdh msg = "unable to restart MPI again"; 17894c06356bSdh goto fail_restart; 17904c06356bSdh } 17914c06356bSdh 17924c06356bSdh mutex_enter(&pwp->lock); 17934c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 17944c06356bSdh mutex_exit(&pwp->lock); 17954c06356bSdh 17964c06356bSdh /* 17974c06356bSdh * Run any completions 17984c06356bSdh */ 17994c06356bSdh PMCS_CQ_RUN(pwp); 18004c06356bSdh 18014c06356bSdh /* 18024c06356bSdh * Delay 18034c06356bSdh */ 18044c06356bSdh drv_usecwait(1000000); 18054c06356bSdh return (0); 18064c06356bSdh 18074c06356bSdh fail_restart: 18084c06356bSdh mutex_enter(&pwp->lock); 18094c06356bSdh pwp->state = STATE_DEAD; 18104c06356bSdh mutex_exit(&pwp->lock); 1811c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 1812c3bc407cSdh "%s: Failed: %s", __func__, msg); 18134c06356bSdh return (-1); 18144c06356bSdh } 18154c06356bSdh 18165c45adf0SJesse Butler 18175c45adf0SJesse Butler /* 18185c45adf0SJesse Butler * Perform a 'hot' reset, which will soft reset the chip and 18195c45adf0SJesse Butler * restore the state back to pre-reset context. Called with pwp 18205c45adf0SJesse Butler * lock held. 18215c45adf0SJesse Butler */ 18225c45adf0SJesse Butler int 18235c45adf0SJesse Butler pmcs_hot_reset(pmcs_hw_t *pwp) 18245c45adf0SJesse Butler { 18255c45adf0SJesse Butler pmcs_iport_t *iport; 18265c45adf0SJesse Butler 18275c45adf0SJesse Butler ASSERT(mutex_owned(&pwp->lock)); 18285c45adf0SJesse Butler pwp->state = STATE_IN_RESET; 18295c45adf0SJesse Butler 18305c45adf0SJesse Butler /* 18315c45adf0SJesse Butler * For any iports on this HBA, report empty target sets and 18325c45adf0SJesse Butler * then tear them down. 18335c45adf0SJesse Butler */ 18345c45adf0SJesse Butler rw_enter(&pwp->iports_lock, RW_READER); 18355c45adf0SJesse Butler for (iport = list_head(&pwp->iports); iport != NULL; 18365c45adf0SJesse Butler iport = list_next(&pwp->iports, iport)) { 18375c45adf0SJesse Butler mutex_enter(&iport->lock); 18385c45adf0SJesse Butler (void) scsi_hba_tgtmap_set_begin(iport->iss_tgtmap); 18395c45adf0SJesse Butler (void) scsi_hba_tgtmap_set_end(iport->iss_tgtmap, 0); 18405c45adf0SJesse Butler pmcs_iport_teardown_phys(iport); 18415c45adf0SJesse Butler mutex_exit(&iport->lock); 18425c45adf0SJesse Butler } 18435c45adf0SJesse Butler rw_exit(&pwp->iports_lock); 18445c45adf0SJesse Butler 18455c45adf0SJesse Butler /* Grab a register dump, in the event that reset fails */ 18465c45adf0SJesse Butler pmcs_register_dump_int(pwp); 18475c45adf0SJesse Butler mutex_exit(&pwp->lock); 18485c45adf0SJesse Butler 184932b54db7SJesse Butler /* Ensure discovery is not running before we proceed */ 185032b54db7SJesse Butler mutex_enter(&pwp->config_lock); 185132b54db7SJesse Butler while (pwp->configuring) { 185232b54db7SJesse Butler cv_wait(&pwp->config_cv, &pwp->config_lock); 185332b54db7SJesse Butler } 185432b54db7SJesse Butler mutex_exit(&pwp->config_lock); 185532b54db7SJesse Butler 18565c45adf0SJesse Butler /* Issue soft reset and clean up related softstate */ 18575c45adf0SJesse Butler if (pmcs_soft_reset(pwp, B_FALSE)) { 18585c45adf0SJesse Butler /* 18595c45adf0SJesse Butler * Disable interrupts, in case we got far enough along to 18605c45adf0SJesse Butler * enable them, then fire off ereport and service impact. 18615c45adf0SJesse Butler */ 18625c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 18635c45adf0SJesse Butler "%s: failed soft reset", __func__); 18645c45adf0SJesse Butler pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff); 18655c45adf0SJesse Butler pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff); 18665c45adf0SJesse Butler pmcs_fm_ereport(pwp, DDI_FM_DEVICE_NO_RESPONSE); 18675c45adf0SJesse Butler ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST); 18685c45adf0SJesse Butler mutex_enter(&pwp->lock); 18695c45adf0SJesse Butler pwp->state = STATE_DEAD; 18705c45adf0SJesse Butler return (DDI_FAILURE); 18715c45adf0SJesse Butler } 18725c45adf0SJesse Butler 18735c45adf0SJesse Butler mutex_enter(&pwp->lock); 18745c45adf0SJesse Butler pwp->state = STATE_RUNNING; 18755c45adf0SJesse Butler mutex_exit(&pwp->lock); 18765c45adf0SJesse Butler 18775c45adf0SJesse Butler /* 18785c45adf0SJesse Butler * Finally, restart the phys, which will bring the iports back 18795c45adf0SJesse Butler * up and eventually result in discovery running. 18805c45adf0SJesse Butler */ 18815c45adf0SJesse Butler if (pmcs_start_phys(pwp)) { 18825c45adf0SJesse Butler /* We should be up and running now, so retry */ 18835c45adf0SJesse Butler if (pmcs_start_phys(pwp)) { 18845c45adf0SJesse Butler /* Apparently unable to restart PHYs, fail */ 18855c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 18865c45adf0SJesse Butler "%s: failed to restart PHYs after soft reset", 18875c45adf0SJesse Butler __func__); 18885c45adf0SJesse Butler mutex_enter(&pwp->lock); 18895c45adf0SJesse Butler return (DDI_FAILURE); 18905c45adf0SJesse Butler } 18915c45adf0SJesse Butler } 18925c45adf0SJesse Butler 18935c45adf0SJesse Butler mutex_enter(&pwp->lock); 18945c45adf0SJesse Butler return (DDI_SUCCESS); 18955c45adf0SJesse Butler } 18965c45adf0SJesse Butler 18974c06356bSdh /* 18984c06356bSdh * Reset a device or a logical unit. 18994c06356bSdh */ 19004c06356bSdh int 19014c06356bSdh pmcs_reset_dev(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint64_t lun) 19024c06356bSdh { 19034c06356bSdh int rval = 0; 19044c06356bSdh 19054c06356bSdh if (pptr == NULL) { 19064c06356bSdh return (ENXIO); 19074c06356bSdh } 19084c06356bSdh 19094c06356bSdh pmcs_lock_phy(pptr); 19104c06356bSdh if (pptr->dtype == SAS) { 19114c06356bSdh /* 19124c06356bSdh * Some devices do not support SAS_I_T_NEXUS_RESET as 19134c06356bSdh * it is not a mandatory (in SAM4) task management 19144c06356bSdh * function, while LOGIC_UNIT_RESET is mandatory. 19154c06356bSdh * 19164c06356bSdh * The problem here is that we need to iterate over 19174c06356bSdh * all known LUNs to emulate the semantics of 19184c06356bSdh * "RESET_TARGET". 19194c06356bSdh * 19204c06356bSdh * XXX: FIX ME 19214c06356bSdh */ 19224c06356bSdh if (lun == (uint64_t)-1) { 19234c06356bSdh lun = 0; 19244c06356bSdh } 19254c06356bSdh rval = pmcs_ssp_tmf(pwp, pptr, SAS_LOGICAL_UNIT_RESET, 0, lun, 19264c06356bSdh NULL); 19274c06356bSdh } else if (pptr->dtype == SATA) { 19284c06356bSdh if (lun != 0ull) { 19294c06356bSdh pmcs_unlock_phy(pptr); 19304c06356bSdh return (EINVAL); 19314c06356bSdh } 19324c06356bSdh rval = pmcs_reset_phy(pwp, pptr, PMCS_PHYOP_LINK_RESET); 19334c06356bSdh } else { 19344c06356bSdh pmcs_unlock_phy(pptr); 1935c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 19364c06356bSdh "%s: cannot reset a SMP device yet (%s)", 19374c06356bSdh __func__, pptr->path); 19384c06356bSdh return (EINVAL); 19394c06356bSdh } 19404c06356bSdh 19414c06356bSdh /* 19424c06356bSdh * Now harvest any commands killed by this action 19434c06356bSdh * by issuing an ABORT for all commands on this device. 19444c06356bSdh * 19454c06356bSdh * We do this even if the the tmf or reset fails (in case there 19464c06356bSdh * are any dead commands around to be harvested *anyway*). 19474c06356bSdh * We don't have to await for the abort to complete. 19484c06356bSdh */ 19494c06356bSdh if (pmcs_abort(pwp, pptr, 0, 1, 0)) { 19504c06356bSdh pptr->abort_pending = 1; 19514c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE); 19524c06356bSdh } 19534c06356bSdh 19544c06356bSdh pmcs_unlock_phy(pptr); 19554c06356bSdh return (rval); 19564c06356bSdh } 19574c06356bSdh 19584c06356bSdh /* 19594c06356bSdh * Called with PHY locked. 19604c06356bSdh */ 19614c06356bSdh static int 19624c06356bSdh pmcs_get_device_handle(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 19634c06356bSdh { 19644c06356bSdh if (pptr->valid_device_id == 0) { 19654c06356bSdh int result = pmcs_register_device(pwp, pptr); 19664c06356bSdh 19674c06356bSdh /* 19684c06356bSdh * If we changed while registering, punt 19694c06356bSdh */ 19704c06356bSdh if (pptr->changed) { 19714c06356bSdh RESTART_DISCOVERY(pwp); 19724c06356bSdh return (-1); 19734c06356bSdh } 19744c06356bSdh 19754c06356bSdh /* 19764c06356bSdh * If we had a failure to register, check against errors. 19774c06356bSdh * An ENOMEM error means we just retry (temp resource shortage). 19784c06356bSdh */ 19794c06356bSdh if (result == ENOMEM) { 19804c06356bSdh PHY_CHANGED(pwp, pptr); 19814c06356bSdh RESTART_DISCOVERY(pwp); 19824c06356bSdh return (-1); 19834c06356bSdh } 19844c06356bSdh 19854c06356bSdh /* 19864c06356bSdh * An ETIMEDOUT error means we retry (if our counter isn't 19874c06356bSdh * exhausted) 19884c06356bSdh */ 19894c06356bSdh if (result == ETIMEDOUT) { 19904c06356bSdh if (ddi_get_lbolt() < pptr->config_stop) { 19914c06356bSdh PHY_CHANGED(pwp, pptr); 19924c06356bSdh RESTART_DISCOVERY(pwp); 19934c06356bSdh } else { 1994c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 19954c06356bSdh "%s: Retries exhausted for %s, killing", 19964c06356bSdh __func__, pptr->path); 19974c06356bSdh pptr->config_stop = 0; 19984c06356bSdh pmcs_kill_changed(pwp, pptr, 0); 19994c06356bSdh } 20004c06356bSdh return (-1); 20014c06356bSdh } 20024c06356bSdh /* 20034c06356bSdh * Other errors or no valid device id is fatal, but don't 20044c06356bSdh * preclude a future action. 20054c06356bSdh */ 20064c06356bSdh if (result || pptr->valid_device_id == 0) { 2007c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 2008c3bc407cSdh "%s: %s could not be registered", __func__, 2009c3bc407cSdh pptr->path); 20104c06356bSdh return (-1); 20114c06356bSdh } 20124c06356bSdh } 20134c06356bSdh return (0); 20144c06356bSdh } 20154c06356bSdh 20164c06356bSdh int 20174c06356bSdh pmcs_iport_tgtmap_create(pmcs_iport_t *iport) 20184c06356bSdh { 20194c06356bSdh ASSERT(iport); 20204c06356bSdh if (iport == NULL) 20214c06356bSdh return (B_FALSE); 20224c06356bSdh 2023c3bc407cSdh pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s", __func__); 20244c06356bSdh 20254c06356bSdh /* create target map */ 202660aabb4cSChris Horne if (scsi_hba_tgtmap_create(iport->dip, SCSI_TM_FULLSET, 202760aabb4cSChris Horne tgtmap_csync_usec, tgtmap_stable_usec, (void *)iport, 202860aabb4cSChris Horne 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); 2170*3be32c0fSJesse Butler pmcs_hold_iport(iport); 21714c06356bSdh break; 21724c06356bSdh } 21734c06356bSdh mutex_exit(&iport->lock); 21744c06356bSdh } 21754c06356bSdh rw_exit(&pwp->iports_lock); 21764c06356bSdh 21774c06356bSdh return (iport); 21784c06356bSdh } 21794c06356bSdh 21804c06356bSdh /* 21814c06356bSdh * Return the iport that pptr is associated with, or NULL. 21824c06356bSdh * If an iport is returned, there is a hold that the caller must release. 21834c06356bSdh */ 21844c06356bSdh pmcs_iport_t * 21859aed1621SDavid Hollister pmcs_get_iport_by_wwn(pmcs_hw_t *pwp, uint64_t wwn) 21864c06356bSdh { 21874c06356bSdh pmcs_iport_t *iport = NULL; 21884c06356bSdh char *ua; 21894c06356bSdh 21909aed1621SDavid Hollister ua = sas_phymap_lookup_ua(pwp->hss_phymap, pwp->sas_wwns[0], wwn); 21914c06356bSdh if (ua) { 21924c06356bSdh iport = pmcs_get_iport_by_ua(pwp, ua); 21934c06356bSdh if (iport) { 21944c06356bSdh mutex_enter(&iport->lock); 21959aed1621SDavid Hollister pmcs_iport_active(iport); 21969aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: " 21979aed1621SDavid Hollister "found iport [0x%p] on ua (%s), refcnt (%d)", 21989aed1621SDavid Hollister __func__, (void *)iport, ua, iport->refcnt); 21994c06356bSdh mutex_exit(&iport->lock); 22004c06356bSdh } 22014c06356bSdh } 22024c06356bSdh 22034c06356bSdh return (iport); 22044c06356bSdh } 22054c06356bSdh 2206f73ae3dbSJesse Butler /* 22076745c559SJesse Butler * Promote the next phy on this port to primary, and return it. 2208f73ae3dbSJesse Butler * Called when the primary PHY on a port is going down, but the port 2209f73ae3dbSJesse Butler * remains up (see pmcs_intr.c). 2210f73ae3dbSJesse Butler */ 2211f73ae3dbSJesse Butler pmcs_phy_t * 2212f73ae3dbSJesse Butler pmcs_promote_next_phy(pmcs_phy_t *prev_primary) 2213f73ae3dbSJesse Butler { 22146745c559SJesse Butler pmcs_hw_t *pwp; 22156745c559SJesse Butler pmcs_iport_t *iport; 22166745c559SJesse Butler pmcs_phy_t *pptr, *child; 22176745c559SJesse Butler int portid; 2218f73ae3dbSJesse Butler 22196745c559SJesse Butler pmcs_lock_phy(prev_primary); 22206745c559SJesse Butler portid = prev_primary->portid; 22216745c559SJesse Butler iport = prev_primary->iport; 22226745c559SJesse Butler pwp = prev_primary->pwp; 2223f73ae3dbSJesse Butler 22246745c559SJesse Butler /* Use the first available phy in this port */ 22256745c559SJesse Butler for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) { 22266745c559SJesse Butler if ((pptr->portid == portid) && (pptr != prev_primary)) { 22276745c559SJesse Butler mutex_enter(&pptr->phy_lock); 2228f73ae3dbSJesse Butler break; 2229f73ae3dbSJesse Butler } 2230f73ae3dbSJesse Butler } 2231f73ae3dbSJesse Butler 2232f73ae3dbSJesse Butler if (pptr == NULL) { 22336745c559SJesse Butler pmcs_unlock_phy(prev_primary); 2234f73ae3dbSJesse Butler return (NULL); 2235f73ae3dbSJesse Butler } 2236f73ae3dbSJesse Butler 22376745c559SJesse Butler if (iport) { 22386745c559SJesse Butler mutex_enter(&iport->lock); 22396745c559SJesse Butler iport->pptr = pptr; 22406745c559SJesse Butler mutex_exit(&iport->lock); 22416745c559SJesse Butler } 2242f73ae3dbSJesse Butler 22436745c559SJesse Butler /* Update the phy handle with the data from the previous primary */ 2244f73ae3dbSJesse Butler pptr->children = prev_primary->children; 2245f73ae3dbSJesse Butler child = pptr->children; 2246f73ae3dbSJesse Butler while (child) { 2247f73ae3dbSJesse Butler child->parent = pptr; 2248f73ae3dbSJesse Butler child = child->sibling; 2249f73ae3dbSJesse Butler } 2250f73ae3dbSJesse Butler pptr->ncphy = prev_primary->ncphy; 2251f73ae3dbSJesse Butler pptr->width = prev_primary->width; 2252f73ae3dbSJesse Butler pptr->dtype = prev_primary->dtype; 2253f73ae3dbSJesse Butler pptr->pend_dtype = prev_primary->pend_dtype; 2254f73ae3dbSJesse Butler pptr->tolerates_sas2 = prev_primary->tolerates_sas2; 2255f73ae3dbSJesse Butler pptr->atdt = prev_primary->atdt; 2256f73ae3dbSJesse Butler pptr->portid = prev_primary->portid; 2257f73ae3dbSJesse Butler pptr->link_rate = prev_primary->link_rate; 2258f73ae3dbSJesse Butler pptr->configured = prev_primary->configured; 2259f73ae3dbSJesse Butler pptr->iport = prev_primary->iport; 2260f73ae3dbSJesse Butler pptr->target = prev_primary->target; 22616745c559SJesse Butler if (pptr->target) { 22626745c559SJesse Butler pptr->target->phy = pptr; 22636745c559SJesse Butler } 2264499cfd15SDavid Hollister 2265499cfd15SDavid Hollister /* Update the phy mask properties for the affected PHYs */ 2266499cfd15SDavid Hollister /* Clear the current values... */ 2267499cfd15SDavid Hollister pmcs_update_phy_pm_props(pptr, pptr->att_port_pm_tmp, 2268499cfd15SDavid Hollister pptr->tgt_port_pm_tmp, B_FALSE); 2269499cfd15SDavid Hollister /* ...replace with the values from prev_primary... */ 2270499cfd15SDavid Hollister pmcs_update_phy_pm_props(pptr, prev_primary->att_port_pm_tmp, 2271499cfd15SDavid Hollister prev_primary->tgt_port_pm_tmp, B_TRUE); 2272499cfd15SDavid Hollister /* ...then clear prev_primary's PHY values from the new primary */ 2273499cfd15SDavid Hollister pmcs_update_phy_pm_props(pptr, prev_primary->att_port_pm, 2274499cfd15SDavid Hollister prev_primary->tgt_port_pm, B_FALSE); 2275499cfd15SDavid Hollister /* Clear the prev_primary's values */ 2276499cfd15SDavid Hollister pmcs_update_phy_pm_props(prev_primary, prev_primary->att_port_pm_tmp, 2277499cfd15SDavid Hollister prev_primary->tgt_port_pm_tmp, B_FALSE); 2278499cfd15SDavid Hollister 2279f73ae3dbSJesse Butler pptr->subsidiary = 0; 2280f73ae3dbSJesse Butler 2281f73ae3dbSJesse Butler prev_primary->subsidiary = 1; 2282f73ae3dbSJesse Butler prev_primary->children = NULL; 22836745c559SJesse Butler prev_primary->target = NULL; 2284601c90f1SSrikanth, Ramana pptr->device_id = prev_primary->device_id; 2285*3be32c0fSJesse Butler pptr->valid_device_id = prev_primary->valid_device_id; 2286f73ae3dbSJesse Butler pmcs_unlock_phy(prev_primary); 2287f73ae3dbSJesse Butler 2288f73ae3dbSJesse Butler /* 2289f73ae3dbSJesse Butler * We call pmcs_unlock_phy() on pptr because it now contains the 2290f73ae3dbSJesse Butler * list of children. 2291f73ae3dbSJesse Butler */ 2292f73ae3dbSJesse Butler pmcs_unlock_phy(pptr); 2293f73ae3dbSJesse Butler 2294f73ae3dbSJesse Butler return (pptr); 2295f73ae3dbSJesse Butler } 2296f73ae3dbSJesse Butler 2297*3be32c0fSJesse Butler void 2298*3be32c0fSJesse Butler pmcs_hold_iport(pmcs_iport_t *iport) 2299*3be32c0fSJesse Butler { 2300*3be32c0fSJesse Butler /* 2301*3be32c0fSJesse Butler * Grab a reference to this iport. 2302*3be32c0fSJesse Butler */ 2303*3be32c0fSJesse Butler ASSERT(iport); 2304*3be32c0fSJesse Butler mutex_enter(&iport->refcnt_lock); 2305*3be32c0fSJesse Butler iport->refcnt++; 2306*3be32c0fSJesse Butler mutex_exit(&iport->refcnt_lock); 2307*3be32c0fSJesse Butler 2308*3be32c0fSJesse Butler pmcs_prt(iport->pwp, PMCS_PRT_DEBUG2, NULL, NULL, "%s: iport " 2309*3be32c0fSJesse Butler "[0x%p] refcnt (%d)", __func__, (void *)iport, iport->refcnt); 2310*3be32c0fSJesse Butler } 2311*3be32c0fSJesse Butler 23124c06356bSdh void 23134c06356bSdh pmcs_rele_iport(pmcs_iport_t *iport) 23144c06356bSdh { 23154c06356bSdh /* 23164c06356bSdh * Release a refcnt on this iport. If this is the last reference, 23174c06356bSdh * signal the potential waiter in pmcs_iport_unattach(). 23184c06356bSdh */ 23194c06356bSdh ASSERT(iport->refcnt > 0); 23204c06356bSdh mutex_enter(&iport->refcnt_lock); 23214c06356bSdh iport->refcnt--; 23224c06356bSdh mutex_exit(&iport->refcnt_lock); 23234c06356bSdh if (iport->refcnt == 0) { 23244c06356bSdh cv_signal(&iport->refcnt_cv); 23254c06356bSdh } 2326*3be32c0fSJesse Butler pmcs_prt(iport->pwp, PMCS_PRT_DEBUG2, NULL, NULL, "%s: iport " 2327c3bc407cSdh "[0x%p] refcnt (%d)", __func__, (void *)iport, iport->refcnt); 23284c06356bSdh } 23294c06356bSdh 23304c06356bSdh void 23314c06356bSdh pmcs_phymap_activate(void *arg, char *ua, void **privp) 23324c06356bSdh { 23334c06356bSdh _NOTE(ARGUNUSED(privp)); 23344c06356bSdh pmcs_hw_t *pwp = arg; 23354c06356bSdh pmcs_iport_t *iport = NULL; 23364c06356bSdh 23374c06356bSdh mutex_enter(&pwp->lock); 2338978d7443SSrikanth Suravajhala if ((pwp->state == STATE_UNPROBING) || (pwp->state == STATE_DEAD) || 2339978d7443SSrikanth Suravajhala (pwp->state == STATE_IN_RESET)) { 23404c06356bSdh mutex_exit(&pwp->lock); 23414c06356bSdh return; 23424c06356bSdh } 23434c06356bSdh pwp->phymap_active++; 23444c06356bSdh mutex_exit(&pwp->lock); 23454c06356bSdh 23464c06356bSdh if (scsi_hba_iportmap_iport_add(pwp->hss_iportmap, ua, NULL) != 23474c06356bSdh DDI_SUCCESS) { 2348c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: failed to " 2349c3bc407cSdh "add iport handle on unit address [%s]", __func__, ua); 23504c06356bSdh } else { 2351c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: " 2352c3bc407cSdh "phymap_active count (%d), added iport handle on unit " 2353c3bc407cSdh "address [%s]", __func__, pwp->phymap_active, ua); 23544c06356bSdh } 23554c06356bSdh 23564c06356bSdh /* Set the HBA softstate as our private data for this unit address */ 23574c06356bSdh *privp = (void *)pwp; 23584c06356bSdh 23594c06356bSdh /* 23604c06356bSdh * We are waiting on attach for this iport node, unless it is still 23614c06356bSdh * attached. This can happen if a consumer has an outstanding open 23624c06356bSdh * on our iport node, but the port is down. If this is the case, we 23634c06356bSdh * need to configure our iport here for reuse. 23644c06356bSdh */ 23654c06356bSdh iport = pmcs_get_iport_by_ua(pwp, ua); 23664c06356bSdh if (iport) { 23674c06356bSdh mutex_enter(&iport->lock); 23684c06356bSdh if (pmcs_iport_configure_phys(iport) != DDI_SUCCESS) { 2369c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: " 23704c06356bSdh "failed to configure phys on iport [0x%p] at " 23714c06356bSdh "unit address (%s)", __func__, (void *)iport, ua); 23724c06356bSdh } 23739aed1621SDavid Hollister pmcs_iport_active(iport); 23744c06356bSdh pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS, 23754c06356bSdh &iport->nphy); 23764c06356bSdh mutex_exit(&iport->lock); 23774c06356bSdh pmcs_rele_iport(iport); 23784c06356bSdh } 23794c06356bSdh 23804c06356bSdh } 23814c06356bSdh 23824c06356bSdh void 23834c06356bSdh pmcs_phymap_deactivate(void *arg, char *ua, void *privp) 23844c06356bSdh { 23854c06356bSdh _NOTE(ARGUNUSED(privp)); 23864c06356bSdh pmcs_hw_t *pwp = arg; 23874c06356bSdh pmcs_iport_t *iport; 23884c06356bSdh 23894c06356bSdh mutex_enter(&pwp->lock); 23904c06356bSdh pwp->phymap_active--; 23914c06356bSdh mutex_exit(&pwp->lock); 23924c06356bSdh 23934c06356bSdh if (scsi_hba_iportmap_iport_remove(pwp->hss_iportmap, ua) != 23944c06356bSdh DDI_SUCCESS) { 2395c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: failed to " 2396c3bc407cSdh "remove iport handle on unit address [%s]", __func__, ua); 23974c06356bSdh } else { 2398c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: " 2399c3bc407cSdh "phymap_active count (%d), removed iport handle on unit " 2400c3bc407cSdh "address [%s]", __func__, pwp->phymap_active, ua); 24014c06356bSdh } 24024c06356bSdh 24034c06356bSdh iport = pmcs_get_iport_by_ua(pwp, ua); 24044c06356bSdh 24054c06356bSdh if (iport == NULL) { 2406c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: failed " 2407c3bc407cSdh "lookup of iport handle on unit addr (%s)", __func__, ua); 24084c06356bSdh return; 24094c06356bSdh } 24104c06356bSdh 24114c06356bSdh mutex_enter(&iport->lock); 24124c06356bSdh iport->ua_state = UA_INACTIVE; 24134c06356bSdh iport->portid = PMCS_IPORT_INVALID_PORT_ID; 24144c06356bSdh pmcs_remove_phy_from_iport(iport, NULL); 24154c06356bSdh mutex_exit(&iport->lock); 24164c06356bSdh pmcs_rele_iport(iport); 24174c06356bSdh } 24184c06356bSdh 24194c06356bSdh /* 24204c06356bSdh * Top-level discovery function 24214c06356bSdh */ 24224c06356bSdh void 24234c06356bSdh pmcs_discover(pmcs_hw_t *pwp) 24244c06356bSdh { 24254c06356bSdh pmcs_phy_t *pptr; 24264c06356bSdh pmcs_phy_t *root_phy; 24274c06356bSdh 24284c06356bSdh DTRACE_PROBE2(pmcs__discover__entry, ulong_t, pwp->work_flags, 24294c06356bSdh boolean_t, pwp->config_changed); 24304c06356bSdh 24314c06356bSdh mutex_enter(&pwp->lock); 24324c06356bSdh 24334c06356bSdh if (pwp->state != STATE_RUNNING) { 24344c06356bSdh mutex_exit(&pwp->lock); 24354c06356bSdh return; 24364c06356bSdh } 24374c06356bSdh 24384c06356bSdh /* Ensure we have at least one phymap active */ 24394c06356bSdh if (pwp->phymap_active == 0) { 24404c06356bSdh mutex_exit(&pwp->lock); 2441c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 24424c06356bSdh "%s: phymap inactive, exiting", __func__); 24434c06356bSdh return; 24444c06356bSdh } 24454c06356bSdh 24464c06356bSdh mutex_exit(&pwp->lock); 24474c06356bSdh 24484c06356bSdh /* 24494c06356bSdh * If no iports have attached, but we have PHYs that are up, we 24504c06356bSdh * are waiting for iport attach to complete. Restart discovery. 24514c06356bSdh */ 24524c06356bSdh rw_enter(&pwp->iports_lock, RW_READER); 24534c06356bSdh if (!pwp->iports_attached) { 24544c06356bSdh rw_exit(&pwp->iports_lock); 2455c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 24564c06356bSdh "%s: no iports attached, retry discovery", __func__); 24574c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER); 24584c06356bSdh return; 24594c06356bSdh } 24604c06356bSdh rw_exit(&pwp->iports_lock); 24614c06356bSdh 24624c06356bSdh mutex_enter(&pwp->config_lock); 24634c06356bSdh if (pwp->configuring) { 24644c06356bSdh mutex_exit(&pwp->config_lock); 2465c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 24664c06356bSdh "%s: configuration already in progress", __func__); 24674c06356bSdh return; 24684c06356bSdh } 24694c06356bSdh 24704c06356bSdh if (pmcs_acquire_scratch(pwp, B_FALSE)) { 24714c06356bSdh mutex_exit(&pwp->config_lock); 2472c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 24734c06356bSdh "%s: cannot allocate scratch", __func__); 24744c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER); 24754c06356bSdh return; 24764c06356bSdh } 24774c06356bSdh 24784c06356bSdh pwp->configuring = 1; 24794c06356bSdh pwp->config_changed = B_FALSE; 24804c06356bSdh mutex_exit(&pwp->config_lock); 24814c06356bSdh 2482c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "Discovery begin"); 24834c06356bSdh 2484601c90f1SSrikanth, Ramana /* 2485601c90f1SSrikanth, Ramana * First, tell SCSA that we're beginning set operations. 2486601c90f1SSrikanth, Ramana */ 2487601c90f1SSrikanth, Ramana pmcs_begin_observations(pwp); 2488601c90f1SSrikanth, Ramana 24894c06356bSdh /* 24904c06356bSdh * The order of the following traversals is important. 24914c06356bSdh * 24924c06356bSdh * The first one checks for changed expanders. 24934c06356bSdh * 24944c06356bSdh * The second one aborts commands for dead devices and deregisters them. 24954c06356bSdh * 24964c06356bSdh * The third one clears the contents of dead expanders from the tree 24974c06356bSdh * 24984c06356bSdh * The fourth one clears now dead devices in expanders that remain. 24994c06356bSdh */ 25004c06356bSdh 25014c06356bSdh /* 25024c06356bSdh * 1. Check expanders marked changed (but not dead) to see if they still 25034c06356bSdh * have the same number of phys and the same SAS address. Mark them, 25044c06356bSdh * their subsidiary phys (if wide) and their descendents dead if 25054c06356bSdh * anything has changed. Check the devices they contain to see if 25064c06356bSdh * *they* have changed. If they've changed from type NOTHING we leave 25074c06356bSdh * them marked changed to be configured later (picking up a new SAS 25084c06356bSdh * address and link rate if possible). Otherwise, any change in type, 25094c06356bSdh * SAS address or removal of target role will cause us to mark them 25104c06356bSdh * (and their descendents) as dead (and cause any pending commands 25114c06356bSdh * and associated devices to be removed). 2512ec2c44b8Sdh * 2513ec2c44b8Sdh * NOTE: We don't want to bail on discovery if the config has 2514ec2c44b8Sdh * changed until *after* we run pmcs_kill_devices. 25154c06356bSdh */ 25164c06356bSdh root_phy = pwp->root_phys; 251773a3eccdSDavid Hollister pmcs_check_expanders(pwp, root_phy); 25184c06356bSdh 25194c06356bSdh /* 25204c06356bSdh * 2. Descend the tree looking for dead devices and kill them 25214c06356bSdh * by aborting all active commands and then deregistering them. 25224c06356bSdh */ 252373a3eccdSDavid Hollister if (pmcs_kill_devices(pwp, root_phy)) { 252473a3eccdSDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 252573a3eccdSDavid Hollister "%s: pmcs_kill_devices failed!", __func__); 25264c06356bSdh } 25274c06356bSdh 25284c06356bSdh /* 25294c06356bSdh * 3. Check for dead expanders and remove their children from the tree. 25304c06356bSdh * By the time we get here, the devices and commands for them have 25314c06356bSdh * already been terminated and removed. 25324c06356bSdh * 25334c06356bSdh * We do this independent of the configuration count changing so we can 25344c06356bSdh * free any dead device PHYs that were discovered while checking 25354c06356bSdh * expanders. We ignore any subsidiary phys as pmcs_clear_expander 25364c06356bSdh * will take care of those. 25374c06356bSdh * 25384c06356bSdh * NOTE: pmcs_clear_expander requires softstate lock 25394c06356bSdh */ 25404c06356bSdh mutex_enter(&pwp->lock); 25414c06356bSdh for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) { 25424c06356bSdh /* 25434c06356bSdh * Call pmcs_clear_expander for every root PHY. It will 25444c06356bSdh * recurse and determine which (if any) expanders actually 25454c06356bSdh * need to be cleared. 25464c06356bSdh */ 25474c06356bSdh pmcs_lock_phy(pptr); 25484c06356bSdh pmcs_clear_expander(pwp, pptr, 0); 25494c06356bSdh pmcs_unlock_phy(pptr); 25504c06356bSdh } 25514c06356bSdh mutex_exit(&pwp->lock); 25524c06356bSdh 25534c06356bSdh /* 25544c06356bSdh * 4. Check for dead devices and nullify them. By the time we get here, 25554c06356bSdh * the devices and commands for them have already been terminated 25564c06356bSdh * and removed. This is different from step 2 in that this just nulls 25574c06356bSdh * phys that are part of expanders that are still here but used to 25584c06356bSdh * be something but are no longer something (e.g., after a pulled 25594c06356bSdh * disk drive). Note that dead expanders had their contained phys 25604c06356bSdh * removed from the tree- here, the expanders themselves are 25614c06356bSdh * nullified (unless they were removed by being contained in another 25624c06356bSdh * expander phy). 25634c06356bSdh */ 25644c06356bSdh pmcs_clear_phys(pwp, root_phy); 25654c06356bSdh 25664c06356bSdh /* 25674c06356bSdh * 5. Now check for and configure new devices. 25684c06356bSdh */ 25694c06356bSdh if (pmcs_configure_new_devices(pwp, root_phy)) { 25704c06356bSdh goto restart; 25714c06356bSdh } 25724c06356bSdh 25734c06356bSdh out: 25744c06356bSdh DTRACE_PROBE2(pmcs__discover__exit, ulong_t, pwp->work_flags, 25754c06356bSdh boolean_t, pwp->config_changed); 2576c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "Discovery end"); 25774c06356bSdh 25784c06356bSdh mutex_enter(&pwp->config_lock); 25794c06356bSdh 25804c06356bSdh if (pwp->config_changed == B_FALSE) { 25814c06356bSdh /* 25824c06356bSdh * Observation is stable, report what we currently see to 25834c06356bSdh * the tgtmaps for delta processing. Start by setting 25844c06356bSdh * BEGIN on all tgtmaps. 25854c06356bSdh */ 25864c06356bSdh mutex_exit(&pwp->config_lock); 25874c06356bSdh if (pmcs_report_observations(pwp) == B_FALSE) { 25884c06356bSdh goto restart; 25894c06356bSdh } 25904c06356bSdh mutex_enter(&pwp->config_lock); 25914c06356bSdh } else { 25924c06356bSdh /* 25934c06356bSdh * If config_changed is TRUE, we need to reschedule 25944c06356bSdh * discovery now. 25954c06356bSdh */ 2596c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 25974c06356bSdh "%s: Config has changed, will re-run discovery", __func__); 25984c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER); 25994c06356bSdh } 26004c06356bSdh 26014c06356bSdh pmcs_release_scratch(pwp); 26025c45adf0SJesse Butler if (!pwp->quiesced) { 26035c45adf0SJesse Butler pwp->blocked = 0; 26045c45adf0SJesse Butler } 26054c06356bSdh pwp->configuring = 0; 260632b54db7SJesse Butler cv_signal(&pwp->config_cv); 26074c06356bSdh mutex_exit(&pwp->config_lock); 26084c06356bSdh 26094c06356bSdh #ifdef DEBUG 26104c06356bSdh pptr = pmcs_find_phy_needing_work(pwp, pwp->root_phys); 26114c06356bSdh if (pptr != NULL) { 26124c06356bSdh if (!WORK_IS_SCHEDULED(pwp, PMCS_WORK_DISCOVER)) { 2613c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 26144c06356bSdh "PHY %s dead=%d changed=%d configured=%d " 26154c06356bSdh "but no work scheduled", pptr->path, pptr->dead, 26164c06356bSdh pptr->changed, pptr->configured); 26174c06356bSdh } 26184c06356bSdh pmcs_unlock_phy(pptr); 26194c06356bSdh } 26204c06356bSdh #endif 26214c06356bSdh 26224c06356bSdh return; 26234c06356bSdh 26244c06356bSdh restart: 26254c06356bSdh /* Clean up and restart discovery */ 26264c06356bSdh pmcs_release_scratch(pwp); 26270b53804eSReed pmcs_flush_observations(pwp); 26284c06356bSdh mutex_enter(&pwp->config_lock); 26294c06356bSdh pwp->configuring = 0; 263032b54db7SJesse Butler cv_signal(&pwp->config_cv); 26314c06356bSdh RESTART_DISCOVERY_LOCKED(pwp); 26324c06356bSdh mutex_exit(&pwp->config_lock); 26334c06356bSdh } 26344c06356bSdh 26354c06356bSdh /* 26364c06356bSdh * Return any PHY that needs to have scheduled work done. The PHY is returned 26374c06356bSdh * locked. 26384c06356bSdh */ 26394c06356bSdh static pmcs_phy_t * 26404c06356bSdh pmcs_find_phy_needing_work(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 26414c06356bSdh { 26424c06356bSdh pmcs_phy_t *cphyp, *pnext; 26434c06356bSdh 26444c06356bSdh while (pptr) { 26454c06356bSdh pmcs_lock_phy(pptr); 26464c06356bSdh 26474c06356bSdh if (pptr->changed || (pptr->dead && pptr->valid_device_id)) { 26484c06356bSdh return (pptr); 26494c06356bSdh } 26504c06356bSdh 26514c06356bSdh pnext = pptr->sibling; 26524c06356bSdh 26534c06356bSdh if (pptr->children) { 26544c06356bSdh cphyp = pptr->children; 26554c06356bSdh pmcs_unlock_phy(pptr); 26564c06356bSdh cphyp = pmcs_find_phy_needing_work(pwp, cphyp); 26574c06356bSdh if (cphyp) { 26584c06356bSdh return (cphyp); 26594c06356bSdh } 26604c06356bSdh } else { 26614c06356bSdh pmcs_unlock_phy(pptr); 26624c06356bSdh } 26634c06356bSdh 26644c06356bSdh pptr = pnext; 26654c06356bSdh } 26664c06356bSdh 26674c06356bSdh return (NULL); 26684c06356bSdh } 26694c06356bSdh 26704c06356bSdh /* 2671601c90f1SSrikanth, Ramana * We may (or may not) report observations to SCSA. This is prefaced by 2672601c90f1SSrikanth, Ramana * issuing a set_begin for each iport target map. 26734c06356bSdh */ 2674601c90f1SSrikanth, Ramana static void 2675601c90f1SSrikanth, Ramana pmcs_begin_observations(pmcs_hw_t *pwp) 26764c06356bSdh { 26774c06356bSdh pmcs_iport_t *iport; 26784c06356bSdh scsi_hba_tgtmap_t *tgtmap; 26794c06356bSdh 26804c06356bSdh rw_enter(&pwp->iports_lock, RW_READER); 26814c06356bSdh for (iport = list_head(&pwp->iports); iport != NULL; 26824c06356bSdh iport = list_next(&pwp->iports, iport)) { 26834c06356bSdh /* 26844c06356bSdh * Unless we have at least one phy up, skip this iport. 26854c06356bSdh * Note we don't need to lock the iport for report_skip 26864c06356bSdh * since it is only used here. We are doing the skip so that 26874c06356bSdh * the phymap and iportmap stabilization times are honored - 26884c06356bSdh * giving us the ability to recover port operation within the 26894c06356bSdh * stabilization time without unconfiguring targets using the 26904c06356bSdh * port. 26914c06356bSdh */ 26924c06356bSdh if (!sas_phymap_uahasphys(pwp->hss_phymap, iport->ua)) { 26934c06356bSdh iport->report_skip = 1; 26944c06356bSdh continue; /* skip set_begin */ 26954c06356bSdh } 26964c06356bSdh iport->report_skip = 0; 26974c06356bSdh 26984c06356bSdh tgtmap = iport->iss_tgtmap; 26994c06356bSdh ASSERT(tgtmap); 27004c06356bSdh if (scsi_hba_tgtmap_set_begin(tgtmap) != DDI_SUCCESS) { 2701c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, 27024c06356bSdh "%s: cannot set_begin tgtmap ", __func__); 27034c06356bSdh rw_exit(&pwp->iports_lock); 2704601c90f1SSrikanth, Ramana return; 27054c06356bSdh } 2706c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, 2707c3bc407cSdh "%s: set begin on tgtmap [0x%p]", __func__, (void *)tgtmap); 27084c06356bSdh } 27094c06356bSdh rw_exit(&pwp->iports_lock); 2710601c90f1SSrikanth, Ramana } 2711601c90f1SSrikanth, Ramana 27120b53804eSReed /* 27130b53804eSReed * Tell SCSA to flush the observations we've already sent (if any), as they 27140b53804eSReed * are no longer valid. 27150b53804eSReed */ 27160b53804eSReed static void 27170b53804eSReed pmcs_flush_observations(pmcs_hw_t *pwp) 27180b53804eSReed { 27190b53804eSReed pmcs_iport_t *iport; 27200b53804eSReed scsi_hba_tgtmap_t *tgtmap; 27210b53804eSReed 27220b53804eSReed rw_enter(&pwp->iports_lock, RW_READER); 27230b53804eSReed for (iport = list_head(&pwp->iports); iport != NULL; 27240b53804eSReed iport = list_next(&pwp->iports, iport)) { 27250b53804eSReed /* 27260b53804eSReed * Skip this iport if it has no PHYs up. 27270b53804eSReed */ 27280b53804eSReed if (!sas_phymap_uahasphys(pwp->hss_phymap, iport->ua)) { 27290b53804eSReed continue; 27300b53804eSReed } 27310b53804eSReed 27320b53804eSReed tgtmap = iport->iss_tgtmap; 27330b53804eSReed ASSERT(tgtmap); 27340b53804eSReed if (scsi_hba_tgtmap_set_flush(tgtmap) != DDI_SUCCESS) { 27350b53804eSReed pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, 27360b53804eSReed "%s: Failed set_flush on tgtmap 0x%p", __func__, 27370b53804eSReed (void *)tgtmap); 27380b53804eSReed } else { 27390b53804eSReed pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, 27400b53804eSReed "%s: set flush on tgtmap 0x%p", __func__, 27410b53804eSReed (void *)tgtmap); 27420b53804eSReed } 27430b53804eSReed } 27440b53804eSReed rw_exit(&pwp->iports_lock); 27450b53804eSReed } 27460b53804eSReed 2747601c90f1SSrikanth, Ramana /* 2748601c90f1SSrikanth, Ramana * Report current observations to SCSA. 2749601c90f1SSrikanth, Ramana */ 2750601c90f1SSrikanth, Ramana static boolean_t 2751601c90f1SSrikanth, Ramana pmcs_report_observations(pmcs_hw_t *pwp) 2752601c90f1SSrikanth, Ramana { 2753601c90f1SSrikanth, Ramana pmcs_iport_t *iport; 2754601c90f1SSrikanth, Ramana scsi_hba_tgtmap_t *tgtmap; 2755601c90f1SSrikanth, Ramana char *ap; 2756601c90f1SSrikanth, Ramana pmcs_phy_t *pptr; 2757601c90f1SSrikanth, Ramana uint64_t wwn; 27584c06356bSdh 27594c06356bSdh /* 2760601c90f1SSrikanth, Ramana * Observation is stable, report what we currently see to the tgtmaps 2761601c90f1SSrikanth, Ramana * for delta processing. 27624c06356bSdh */ 27634c06356bSdh pptr = pwp->root_phys; 27644c06356bSdh 27654c06356bSdh while (pptr) { 27664c06356bSdh pmcs_lock_phy(pptr); 27674c06356bSdh 27684c06356bSdh /* 27694c06356bSdh * Skip PHYs that have nothing attached or are dead. 27704c06356bSdh */ 27714c06356bSdh if ((pptr->dtype == NOTHING) || pptr->dead) { 27724c06356bSdh pmcs_unlock_phy(pptr); 27734c06356bSdh pptr = pptr->sibling; 27744c06356bSdh continue; 27754c06356bSdh } 27764c06356bSdh 27774c06356bSdh if (pptr->changed) { 2778c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 27794c06356bSdh "%s: oops, PHY %s changed; restart discovery", 27804c06356bSdh __func__, pptr->path); 27814c06356bSdh pmcs_unlock_phy(pptr); 27824c06356bSdh return (B_FALSE); 27834c06356bSdh } 27844c06356bSdh 27854c06356bSdh /* 27864c06356bSdh * Get the iport for this root PHY, then call the helper 27874c06356bSdh * to report observations for this iport's targets 27884c06356bSdh */ 27899aed1621SDavid Hollister wwn = pmcs_barray2wwn(pptr->sas_address); 27909aed1621SDavid Hollister pmcs_unlock_phy(pptr); 27919aed1621SDavid Hollister iport = pmcs_get_iport_by_wwn(pwp, wwn); 27924c06356bSdh if (iport == NULL) { 27934c06356bSdh /* No iport for this tgt */ 2794c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 2795c3bc407cSdh "%s: no iport for this target", __func__); 27964c06356bSdh pptr = pptr->sibling; 27974c06356bSdh continue; 27984c06356bSdh } 27994c06356bSdh 28009aed1621SDavid Hollister pmcs_lock_phy(pptr); 28014c06356bSdh if (!iport->report_skip) { 28024c06356bSdh if (pmcs_report_iport_observations( 28034c06356bSdh pwp, iport, pptr) == B_FALSE) { 28044c06356bSdh pmcs_rele_iport(iport); 28054c06356bSdh pmcs_unlock_phy(pptr); 28064c06356bSdh return (B_FALSE); 28074c06356bSdh } 28084c06356bSdh } 28094c06356bSdh pmcs_rele_iport(iport); 28104c06356bSdh pmcs_unlock_phy(pptr); 28114c06356bSdh pptr = pptr->sibling; 28124c06356bSdh } 28134c06356bSdh 28144c06356bSdh /* 28154c06356bSdh * The observation is complete, end sets. Note we will skip any 28164c06356bSdh * iports that are active, but have no PHYs in them (i.e. awaiting 28174c06356bSdh * unconfigure). Set to restart discovery if we find this. 28184c06356bSdh */ 28194c06356bSdh rw_enter(&pwp->iports_lock, RW_READER); 28204c06356bSdh for (iport = list_head(&pwp->iports); 28214c06356bSdh iport != NULL; 28224c06356bSdh iport = list_next(&pwp->iports, iport)) { 28234c06356bSdh 28244c06356bSdh if (iport->report_skip) 28254c06356bSdh continue; /* skip set_end */ 28264c06356bSdh 28274c06356bSdh tgtmap = iport->iss_tgtmap; 28284c06356bSdh ASSERT(tgtmap); 28294c06356bSdh if (scsi_hba_tgtmap_set_end(tgtmap, 0) != DDI_SUCCESS) { 2830c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, 28314c06356bSdh "%s: cannot set_end tgtmap ", __func__); 28324c06356bSdh rw_exit(&pwp->iports_lock); 28334c06356bSdh return (B_FALSE); 28344c06356bSdh } 2835c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, 2836c3bc407cSdh "%s: set end on tgtmap [0x%p]", __func__, (void *)tgtmap); 28374c06356bSdh } 28384c06356bSdh 28394c06356bSdh /* 28404c06356bSdh * Now that discovery is complete, set up the necessary 28414c06356bSdh * DDI properties on each iport node. 28424c06356bSdh */ 28434c06356bSdh for (iport = list_head(&pwp->iports); iport != NULL; 28444c06356bSdh iport = list_next(&pwp->iports, iport)) { 28454c06356bSdh /* Set up the 'attached-port' property on the iport */ 28464c06356bSdh ap = kmem_zalloc(PMCS_MAX_UA_SIZE, KM_SLEEP); 28474c06356bSdh mutex_enter(&iport->lock); 28484c06356bSdh pptr = iport->pptr; 28494c06356bSdh mutex_exit(&iport->lock); 28504c06356bSdh if (pptr == NULL) { 28514c06356bSdh /* 28524c06356bSdh * This iport is down, but has not been 28534c06356bSdh * removed from our list (unconfigured). 28544c06356bSdh * Set our value to '0'. 28554c06356bSdh */ 28564c06356bSdh (void) snprintf(ap, 1, "%s", "0"); 28574c06356bSdh } else { 28584c06356bSdh /* Otherwise, set it to remote phy's wwn */ 28594c06356bSdh pmcs_lock_phy(pptr); 28604c06356bSdh wwn = pmcs_barray2wwn(pptr->sas_address); 28614c06356bSdh (void) scsi_wwn_to_wwnstr(wwn, 1, ap); 28624c06356bSdh pmcs_unlock_phy(pptr); 28634c06356bSdh } 28644c06356bSdh if (ndi_prop_update_string(DDI_DEV_T_NONE, iport->dip, 2865499cfd15SDavid Hollister SCSI_ADDR_PROP_ATTACHED_PORT, ap) != DDI_SUCCESS) { 2866c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed " 2867c3bc407cSdh "to set prop ("SCSI_ADDR_PROP_ATTACHED_PORT")", 28684c06356bSdh __func__); 28694c06356bSdh } 28704c06356bSdh kmem_free(ap, PMCS_MAX_UA_SIZE); 28714c06356bSdh } 28724c06356bSdh rw_exit(&pwp->iports_lock); 28734c06356bSdh 28744c06356bSdh return (B_TRUE); 28754c06356bSdh } 28764c06356bSdh 28774c06356bSdh /* 28784c06356bSdh * Report observations into a particular iport's target map 28794c06356bSdh * 28804c06356bSdh * Called with phyp (and all descendents) locked 28814c06356bSdh */ 28824c06356bSdh static boolean_t 28834c06356bSdh pmcs_report_iport_observations(pmcs_hw_t *pwp, pmcs_iport_t *iport, 28844c06356bSdh pmcs_phy_t *phyp) 28854c06356bSdh { 28864c06356bSdh pmcs_phy_t *lphyp; 28874c06356bSdh scsi_hba_tgtmap_t *tgtmap; 28884c06356bSdh scsi_tgtmap_tgt_type_t tgt_type; 28894c06356bSdh char *ua; 28904c06356bSdh uint64_t wwn; 28914c06356bSdh 28924c06356bSdh tgtmap = iport->iss_tgtmap; 28934c06356bSdh ASSERT(tgtmap); 28944c06356bSdh 28954c06356bSdh lphyp = phyp; 28964c06356bSdh while (lphyp) { 28974c06356bSdh switch (lphyp->dtype) { 28984c06356bSdh default: /* Skip unknown PHYs. */ 28994c06356bSdh /* for non-root phys, skip to sibling */ 29004c06356bSdh goto next_phy; 29014c06356bSdh 29024c06356bSdh case SATA: 29034c06356bSdh case SAS: 29044c06356bSdh tgt_type = SCSI_TGT_SCSI_DEVICE; 29054c06356bSdh break; 29064c06356bSdh 29074c06356bSdh case EXPANDER: 29084c06356bSdh tgt_type = SCSI_TGT_SMP_DEVICE; 29094c06356bSdh break; 29104c06356bSdh } 29114c06356bSdh 29129aed1621SDavid Hollister if (lphyp->dead || !lphyp->configured) { 29134c06356bSdh goto next_phy; 29144c06356bSdh } 29154c06356bSdh 2916a25672a1SDavid Hollister /* 2917a25672a1SDavid Hollister * Validate the PHY's SAS address 2918a25672a1SDavid Hollister */ 2919a25672a1SDavid Hollister if (((lphyp->sas_address[0] & 0xf0) >> 4) != NAA_IEEE_REG) { 2920a25672a1SDavid Hollister pmcs_prt(pwp, PMCS_PRT_ERR, lphyp, NULL, 2921a25672a1SDavid Hollister "PHY 0x%p (%s) has invalid SAS address; " 2922a25672a1SDavid Hollister "will not enumerate", (void *)lphyp, lphyp->path); 2923a25672a1SDavid Hollister goto next_phy; 2924a25672a1SDavid Hollister } 2925a25672a1SDavid Hollister 29264c06356bSdh wwn = pmcs_barray2wwn(lphyp->sas_address); 29274c06356bSdh ua = scsi_wwn_to_wwnstr(wwn, 1, NULL); 29284c06356bSdh 2929c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, lphyp, NULL, 29304c06356bSdh "iport_observation: adding %s on tgtmap [0x%p] phy [0x%p]", 29314c06356bSdh ua, (void *)tgtmap, (void*)lphyp); 29324c06356bSdh 29334c06356bSdh if (scsi_hba_tgtmap_set_add(tgtmap, tgt_type, ua, NULL) != 29344c06356bSdh DDI_SUCCESS) { 2935c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, 29364c06356bSdh "%s: failed to add address %s", __func__, ua); 29374c06356bSdh scsi_free_wwnstr(ua); 29384c06356bSdh return (B_FALSE); 29394c06356bSdh } 29404c06356bSdh scsi_free_wwnstr(ua); 29414c06356bSdh 29424c06356bSdh if (lphyp->children) { 29434c06356bSdh if (pmcs_report_iport_observations(pwp, iport, 29444c06356bSdh lphyp->children) == B_FALSE) { 29454c06356bSdh return (B_FALSE); 29464c06356bSdh } 29474c06356bSdh } 29484c06356bSdh 29494c06356bSdh /* for non-root phys, report siblings too */ 29504c06356bSdh next_phy: 29514c06356bSdh if (IS_ROOT_PHY(lphyp)) { 29524c06356bSdh lphyp = NULL; 29534c06356bSdh } else { 29544c06356bSdh lphyp = lphyp->sibling; 29554c06356bSdh } 29564c06356bSdh } 29574c06356bSdh 29584c06356bSdh return (B_TRUE); 29594c06356bSdh } 29604c06356bSdh 29614c06356bSdh /* 29624c06356bSdh * Check for and configure new devices. 29634c06356bSdh * 29644c06356bSdh * If the changed device is a SATA device, add a SATA device. 29654c06356bSdh * 29664c06356bSdh * If the changed device is a SAS device, add a SAS device. 29674c06356bSdh * 29684c06356bSdh * If the changed device is an EXPANDER device, do a REPORT 29694c06356bSdh * GENERAL SMP command to find out the number of contained phys. 29704c06356bSdh * 29714c06356bSdh * For each number of contained phys, allocate a phy, do a 29724c06356bSdh * DISCOVERY SMP command to find out what kind of device it 29734c06356bSdh * is and add it to the linked list of phys on the *next* level. 29744c06356bSdh * 29754c06356bSdh * NOTE: pptr passed in by the caller will be a root PHY 29764c06356bSdh */ 29774c06356bSdh static int 29784c06356bSdh pmcs_configure_new_devices(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 29794c06356bSdh { 29804c06356bSdh int rval = 0; 29814c06356bSdh pmcs_iport_t *iport; 29824c06356bSdh pmcs_phy_t *pnext, *orig_pptr = pptr, *root_phy, *pchild; 29839aed1621SDavid Hollister uint64_t wwn; 29844c06356bSdh 29854c06356bSdh /* 29864c06356bSdh * First, walk through each PHY at this level 29874c06356bSdh */ 29884c06356bSdh while (pptr) { 29894c06356bSdh pmcs_lock_phy(pptr); 29904c06356bSdh pnext = pptr->sibling; 29914c06356bSdh 29924c06356bSdh /* 29934c06356bSdh * Set the new dtype if it has changed 29944c06356bSdh */ 29954c06356bSdh if ((pptr->pend_dtype != NEW) && 29964c06356bSdh (pptr->pend_dtype != pptr->dtype)) { 29974c06356bSdh pptr->dtype = pptr->pend_dtype; 29984c06356bSdh } 29994c06356bSdh 30004c06356bSdh if (pptr->changed == 0 || pptr->dead || pptr->configured) { 30014c06356bSdh goto next_phy; 30024c06356bSdh } 30034c06356bSdh 3004*3be32c0fSJesse Butler /* Confirm that this iport is configured */ 30054c06356bSdh root_phy = pmcs_get_root_phy(pptr); 30069aed1621SDavid Hollister wwn = pmcs_barray2wwn(root_phy->sas_address); 30079aed1621SDavid Hollister pmcs_unlock_phy(pptr); 30089aed1621SDavid Hollister iport = pmcs_get_iport_by_wwn(pwp, wwn); 30094c06356bSdh if (iport == NULL) { 3010c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 30114c06356bSdh "%s: iport not yet configured, " 30124c06356bSdh "retry discovery", __func__); 30134c06356bSdh pnext = NULL; 30144c06356bSdh rval = -1; 30159aed1621SDavid Hollister pmcs_lock_phy(pptr); 30164c06356bSdh goto next_phy; 30174c06356bSdh } 30184c06356bSdh 30199aed1621SDavid Hollister pmcs_lock_phy(pptr); 30204c06356bSdh switch (pptr->dtype) { 30214c06356bSdh case NOTHING: 30224c06356bSdh pptr->changed = 0; 30234c06356bSdh break; 30244c06356bSdh case SATA: 30254c06356bSdh case SAS: 30264c06356bSdh pptr->iport = iport; 30274c06356bSdh pmcs_new_tport(pwp, pptr); 30284c06356bSdh break; 30294c06356bSdh case EXPANDER: 30304c06356bSdh pmcs_configure_expander(pwp, pptr, iport); 30314c06356bSdh break; 30324c06356bSdh } 30334c06356bSdh pmcs_rele_iport(iport); 30344c06356bSdh 30354c06356bSdh mutex_enter(&pwp->config_lock); 30364c06356bSdh if (pwp->config_changed) { 30374c06356bSdh mutex_exit(&pwp->config_lock); 30384c06356bSdh pnext = NULL; 30394c06356bSdh goto next_phy; 30404c06356bSdh } 30414c06356bSdh mutex_exit(&pwp->config_lock); 30424c06356bSdh 30434c06356bSdh next_phy: 30444c06356bSdh pmcs_unlock_phy(pptr); 30454c06356bSdh pptr = pnext; 30464c06356bSdh } 30474c06356bSdh 30484c06356bSdh if (rval != 0) { 30494c06356bSdh return (rval); 30504c06356bSdh } 30514c06356bSdh 30524c06356bSdh /* 30534c06356bSdh * Now walk through each PHY again, recalling ourselves if they 30544c06356bSdh * have children 30554c06356bSdh */ 30564c06356bSdh pptr = orig_pptr; 30574c06356bSdh while (pptr) { 30584c06356bSdh pmcs_lock_phy(pptr); 30594c06356bSdh pnext = pptr->sibling; 30604c06356bSdh pchild = pptr->children; 30614c06356bSdh pmcs_unlock_phy(pptr); 30624c06356bSdh 30634c06356bSdh if (pchild) { 30644c06356bSdh rval = pmcs_configure_new_devices(pwp, pchild); 30654c06356bSdh if (rval != 0) { 30664c06356bSdh break; 30674c06356bSdh } 30684c06356bSdh } 30694c06356bSdh 30704c06356bSdh pptr = pnext; 30714c06356bSdh } 30724c06356bSdh 30734c06356bSdh return (rval); 30744c06356bSdh } 30754c06356bSdh 30764c06356bSdh /* 30774c06356bSdh * Set all phys and descendent phys as changed if changed == B_TRUE, otherwise 30784c06356bSdh * mark them all as not changed. 30794c06356bSdh * 30804c06356bSdh * Called with parent PHY locked. 30814c06356bSdh */ 30824c06356bSdh void 30834c06356bSdh pmcs_set_changed(pmcs_hw_t *pwp, pmcs_phy_t *parent, boolean_t changed, 30844c06356bSdh int level) 30854c06356bSdh { 30864c06356bSdh pmcs_phy_t *pptr; 30874c06356bSdh 30884c06356bSdh if (level == 0) { 30894c06356bSdh if (changed) { 30904c06356bSdh PHY_CHANGED(pwp, parent); 30914c06356bSdh } else { 30924c06356bSdh parent->changed = 0; 30934c06356bSdh } 30944c06356bSdh if (parent->dtype == EXPANDER && parent->level) { 30954c06356bSdh parent->width = 1; 30964c06356bSdh } 30974c06356bSdh if (parent->children) { 30984c06356bSdh pmcs_set_changed(pwp, parent->children, changed, 30994c06356bSdh level + 1); 31004c06356bSdh } 31014c06356bSdh } else { 31024c06356bSdh pptr = parent; 31034c06356bSdh while (pptr) { 31044c06356bSdh if (changed) { 31054c06356bSdh PHY_CHANGED(pwp, pptr); 31064c06356bSdh } else { 31074c06356bSdh pptr->changed = 0; 31084c06356bSdh } 31094c06356bSdh if (pptr->dtype == EXPANDER && pptr->level) { 31104c06356bSdh pptr->width = 1; 31114c06356bSdh } 31124c06356bSdh if (pptr->children) { 31134c06356bSdh pmcs_set_changed(pwp, pptr->children, changed, 31144c06356bSdh level + 1); 31154c06356bSdh } 31164c06356bSdh pptr = pptr->sibling; 31174c06356bSdh } 31184c06356bSdh } 31194c06356bSdh } 31204c06356bSdh 31214c06356bSdh /* 31224c06356bSdh * Take the passed phy mark it and its descendants as dead. 31234c06356bSdh * Fire up reconfiguration to abort commands and bury it. 31244c06356bSdh * 31254c06356bSdh * Called with the parent PHY locked. 31264c06356bSdh */ 31274c06356bSdh void 31284c06356bSdh pmcs_kill_changed(pmcs_hw_t *pwp, pmcs_phy_t *parent, int level) 31294c06356bSdh { 31304c06356bSdh pmcs_phy_t *pptr = parent; 31314c06356bSdh 31324c06356bSdh while (pptr) { 31334c06356bSdh pptr->link_rate = 0; 31344c06356bSdh pptr->abort_sent = 0; 31354c06356bSdh pptr->abort_pending = 1; 31364c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE); 31374c06356bSdh pptr->need_rl_ext = 0; 31384c06356bSdh 31394c06356bSdh if (pptr->dead == 0) { 31404c06356bSdh PHY_CHANGED(pwp, pptr); 31414c06356bSdh RESTART_DISCOVERY(pwp); 31424c06356bSdh } 31434c06356bSdh 31444c06356bSdh pptr->dead = 1; 31454c06356bSdh 31464c06356bSdh if (pptr->children) { 31474c06356bSdh pmcs_kill_changed(pwp, pptr->children, level + 1); 31484c06356bSdh } 31494c06356bSdh 31504c06356bSdh /* 31514c06356bSdh * Only kill siblings at level > 0 31524c06356bSdh */ 31534c06356bSdh if (level == 0) { 31544c06356bSdh return; 31554c06356bSdh } 31564c06356bSdh 31574c06356bSdh pptr = pptr->sibling; 31584c06356bSdh } 31594c06356bSdh } 31604c06356bSdh 31614c06356bSdh /* 31624c06356bSdh * Go through every PHY and clear any that are dead (unless they're expanders) 31634c06356bSdh */ 31644c06356bSdh static void 31654c06356bSdh pmcs_clear_phys(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 31664c06356bSdh { 31674c06356bSdh pmcs_phy_t *pnext, *phyp; 31684c06356bSdh 31694c06356bSdh phyp = pptr; 31704c06356bSdh while (phyp) { 31714c06356bSdh if (IS_ROOT_PHY(phyp)) { 31724c06356bSdh pmcs_lock_phy(phyp); 31734c06356bSdh } 31744c06356bSdh 31754c06356bSdh if ((phyp->dtype != EXPANDER) && phyp->dead) { 31764c06356bSdh pmcs_clear_phy(pwp, phyp); 31774c06356bSdh } 31784c06356bSdh 31794c06356bSdh if (phyp->children) { 31804c06356bSdh pmcs_clear_phys(pwp, phyp->children); 31814c06356bSdh } 31824c06356bSdh 31834c06356bSdh pnext = phyp->sibling; 31844c06356bSdh 31854c06356bSdh if (IS_ROOT_PHY(phyp)) { 31864c06356bSdh pmcs_unlock_phy(phyp); 31874c06356bSdh } 31884c06356bSdh 31894c06356bSdh phyp = pnext; 31904c06356bSdh } 31914c06356bSdh } 31924c06356bSdh 31934c06356bSdh /* 31944c06356bSdh * Clear volatile parts of a phy. Called with PHY locked. 31954c06356bSdh */ 31964c06356bSdh void 31974c06356bSdh pmcs_clear_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 31984c06356bSdh { 3199c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "%s: %s", 3200c3bc407cSdh __func__, pptr->path); 32014c06356bSdh ASSERT(mutex_owned(&pptr->phy_lock)); 32024c06356bSdh /* keep sibling */ 32034c06356bSdh /* keep children */ 32044c06356bSdh /* keep parent */ 32054c06356bSdh pptr->device_id = PMCS_INVALID_DEVICE_ID; 32064c06356bSdh /* keep hw_event_ack */ 32074c06356bSdh pptr->ncphy = 0; 32084c06356bSdh /* keep phynum */ 32094c06356bSdh pptr->width = 0; 32104c06356bSdh pptr->ds_recovery_retries = 0; 3211af685682SSrikanth, Ramana pptr->ds_prev_good_recoveries = 0; 3212af685682SSrikanth, Ramana pptr->last_good_recovery = 0; 3213af685682SSrikanth, Ramana pptr->prev_recovery = 0; 3214145e0143Sdh 32154c06356bSdh /* keep dtype */ 32164c06356bSdh pptr->config_stop = 0; 32174c06356bSdh pptr->spinup_hold = 0; 32184c06356bSdh pptr->atdt = 0; 32194c06356bSdh /* keep portid */ 32204c06356bSdh pptr->link_rate = 0; 32214c06356bSdh pptr->valid_device_id = 0; 32224c06356bSdh pptr->abort_sent = 0; 32234c06356bSdh pptr->abort_pending = 0; 32244c06356bSdh pptr->need_rl_ext = 0; 32254c06356bSdh pptr->subsidiary = 0; 32264c06356bSdh pptr->configured = 0; 3227601c90f1SSrikanth, Ramana pptr->deregister_wait = 0; 32289aed1621SDavid Hollister pptr->reenumerate = 0; 32294c06356bSdh /* Only mark dead if it's not a root PHY and its dtype isn't NOTHING */ 32304c06356bSdh /* XXX: What about directly attached disks? */ 32314c06356bSdh if (!IS_ROOT_PHY(pptr) && (pptr->dtype != NOTHING)) 32324c06356bSdh pptr->dead = 1; 32334c06356bSdh pptr->changed = 0; 32344c06356bSdh /* keep SAS address */ 32354c06356bSdh /* keep path */ 32364c06356bSdh /* keep ref_count */ 32374c06356bSdh /* Don't clear iport on root PHYs - they are handled in pmcs_intr.c */ 32384c06356bSdh if (!IS_ROOT_PHY(pptr)) { 323973a3eccdSDavid Hollister pptr->last_iport = pptr->iport; 32404c06356bSdh pptr->iport = NULL; 32414c06356bSdh } 3242b18a19c2SJesse Butler /* keep target */ 32434c06356bSdh } 32444c06356bSdh 32454c06356bSdh /* 32464c06356bSdh * Allocate softstate for this target if there isn't already one. If there 32474c06356bSdh * is, just redo our internal configuration. If it is actually "new", we'll 32484c06356bSdh * soon get a tran_tgt_init for it. 32494c06356bSdh * 32504c06356bSdh * Called with PHY locked. 32514c06356bSdh */ 32524c06356bSdh static void 32534c06356bSdh pmcs_new_tport(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 32544c06356bSdh { 3255c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "%s: phy 0x%p @ %s", 3256c3bc407cSdh __func__, (void *)pptr, pptr->path); 32574c06356bSdh 32584c06356bSdh if (pmcs_configure_phy(pwp, pptr) == B_FALSE) { 32594c06356bSdh /* 32604c06356bSdh * If the config failed, mark the PHY as changed. 32614c06356bSdh */ 32624c06356bSdh PHY_CHANGED(pwp, pptr); 3263c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 32644c06356bSdh "%s: pmcs_configure_phy failed for phy 0x%p", __func__, 32654c06356bSdh (void *)pptr); 32664c06356bSdh return; 32674c06356bSdh } 32684c06356bSdh 32694c06356bSdh /* Mark PHY as no longer changed */ 32704c06356bSdh pptr->changed = 0; 32714c06356bSdh 32724c06356bSdh /* 327373a3eccdSDavid Hollister * If the PHY has no target pointer: 327473a3eccdSDavid Hollister * 327573a3eccdSDavid Hollister * If it's a root PHY, see if another PHY in the iport holds the 327673a3eccdSDavid Hollister * target pointer (primary PHY changed). If so, move it over. 327773a3eccdSDavid Hollister * 327873a3eccdSDavid Hollister * If it's not a root PHY, see if there's a PHY on the dead_phys 327973a3eccdSDavid Hollister * list that matches. 32804c06356bSdh */ 32814c06356bSdh if (pptr->target == NULL) { 328273a3eccdSDavid Hollister if (IS_ROOT_PHY(pptr)) { 328373a3eccdSDavid Hollister pmcs_phy_t *rphy = pwp->root_phys; 328473a3eccdSDavid Hollister 328573a3eccdSDavid Hollister while (rphy) { 328673a3eccdSDavid Hollister if (rphy == pptr) { 328773a3eccdSDavid Hollister rphy = rphy->sibling; 328873a3eccdSDavid Hollister continue; 328973a3eccdSDavid Hollister } 329073a3eccdSDavid Hollister 329173a3eccdSDavid Hollister mutex_enter(&rphy->phy_lock); 329273a3eccdSDavid Hollister if ((rphy->iport == pptr->iport) && 329373a3eccdSDavid Hollister (rphy->target != NULL)) { 329473a3eccdSDavid Hollister mutex_enter(&rphy->target->statlock); 329573a3eccdSDavid Hollister pptr->target = rphy->target; 329673a3eccdSDavid Hollister rphy->target = NULL; 329773a3eccdSDavid Hollister pptr->target->phy = pptr; 329873a3eccdSDavid Hollister /* The target is now on pptr */ 329973a3eccdSDavid Hollister mutex_exit(&pptr->target->statlock); 330073a3eccdSDavid Hollister mutex_exit(&rphy->phy_lock); 330173a3eccdSDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 330273a3eccdSDavid Hollister pptr, pptr->target, 330373a3eccdSDavid Hollister "%s: Moved target from %s to %s", 330473a3eccdSDavid Hollister __func__, rphy->path, pptr->path); 330573a3eccdSDavid Hollister break; 330673a3eccdSDavid Hollister } 330773a3eccdSDavid Hollister mutex_exit(&rphy->phy_lock); 330873a3eccdSDavid Hollister 330973a3eccdSDavid Hollister rphy = rphy->sibling; 331073a3eccdSDavid Hollister } 331173a3eccdSDavid Hollister } else { 331273a3eccdSDavid Hollister pmcs_reap_dead_phy(pptr); 331373a3eccdSDavid Hollister } 33144c06356bSdh } 33154c06356bSdh 33164c06356bSdh /* 33174c06356bSdh * Only assign the device if there is a target for this PHY with a 33184c06356bSdh * matching SAS address. If an iport is disconnected from one piece 33194c06356bSdh * of storage and connected to another within the iport stabilization 33204c06356bSdh * time, we can get the PHY/target mismatch situation. 33214c06356bSdh * 33224c06356bSdh * Otherwise, it'll get done in tran_tgt_init. 33234c06356bSdh */ 33244c06356bSdh if (pptr->target) { 33254c06356bSdh mutex_enter(&pptr->target->statlock); 33264c06356bSdh if (pmcs_phy_target_match(pptr) == B_FALSE) { 33274c06356bSdh mutex_exit(&pptr->target->statlock); 33284c06356bSdh if (!IS_ROOT_PHY(pptr)) { 33294c06356bSdh pmcs_dec_phy_ref_count(pptr); 33304c06356bSdh } 3331c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 33324c06356bSdh "%s: Not assigning existing tgt %p for PHY %p " 33334c06356bSdh "(WWN mismatch)", __func__, (void *)pptr->target, 33344c06356bSdh (void *)pptr); 33354c06356bSdh pptr->target = NULL; 33364c06356bSdh return; 33374c06356bSdh } 33384c06356bSdh 33394c06356bSdh if (!pmcs_assign_device(pwp, pptr->target)) { 3340c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target, 33414c06356bSdh "%s: pmcs_assign_device failed for target 0x%p", 33424c06356bSdh __func__, (void *)pptr->target); 33434c06356bSdh } 33444c06356bSdh mutex_exit(&pptr->target->statlock); 33454c06356bSdh } 33464c06356bSdh } 33474c06356bSdh 33484c06356bSdh /* 33494c06356bSdh * Called with PHY lock held. 33504c06356bSdh */ 33514c06356bSdh static boolean_t 33524c06356bSdh pmcs_configure_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 33534c06356bSdh { 33544c06356bSdh char *dtype; 33554c06356bSdh 33564c06356bSdh ASSERT(mutex_owned(&pptr->phy_lock)); 33574c06356bSdh 33584c06356bSdh /* 33594c06356bSdh * Mark this device as no longer changed. 33604c06356bSdh */ 33614c06356bSdh pptr->changed = 0; 33624c06356bSdh 33634c06356bSdh /* 33644c06356bSdh * If we don't have a device handle, get one. 33654c06356bSdh */ 33664c06356bSdh if (pmcs_get_device_handle(pwp, pptr)) { 33674c06356bSdh return (B_FALSE); 33684c06356bSdh } 33694c06356bSdh 33704c06356bSdh pptr->configured = 1; 33714c06356bSdh 33724c06356bSdh switch (pptr->dtype) { 33734c06356bSdh case SAS: 33744c06356bSdh dtype = "SAS"; 33754c06356bSdh break; 33764c06356bSdh case SATA: 33774c06356bSdh dtype = "SATA"; 33784c06356bSdh break; 33794c06356bSdh case EXPANDER: 33804c06356bSdh dtype = "SMP"; 33814c06356bSdh break; 33824c06356bSdh default: 33834c06356bSdh dtype = "???"; 33844c06356bSdh } 33854c06356bSdh 3386c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "config_dev: %s " 3387c3bc407cSdh "dev %s " SAS_ADDR_FMT " dev id 0x%x lr 0x%x", dtype, pptr->path, 33884c06356bSdh SAS_ADDR_PRT(pptr->sas_address), pptr->device_id, pptr->link_rate); 33894c06356bSdh 33904c06356bSdh return (B_TRUE); 33914c06356bSdh } 33924c06356bSdh 33934c06356bSdh /* 33944c06356bSdh * Called with PHY locked 33954c06356bSdh */ 33964c06356bSdh static void 33974c06356bSdh pmcs_configure_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr, pmcs_iport_t *iport) 33984c06356bSdh { 33994c06356bSdh pmcs_phy_t *ctmp, *clist = NULL, *cnext; 34004c06356bSdh int result, i, nphy = 0; 34014c06356bSdh boolean_t root_phy = B_FALSE; 34024c06356bSdh 34034c06356bSdh ASSERT(iport); 34044c06356bSdh 34054c06356bSdh /* 34064c06356bSdh * Step 1- clear our "changed" bit. If we need to retry/restart due 34074c06356bSdh * to resource shortages, we'll set it again. While we're doing 34084c06356bSdh * configuration, other events may set it again as well. If the PHY 34094c06356bSdh * is a root PHY and is currently marked as having changed, reset the 34104c06356bSdh * config_stop timer as well. 34114c06356bSdh */ 34124c06356bSdh if (IS_ROOT_PHY(pptr) && pptr->changed) { 34134c06356bSdh pptr->config_stop = ddi_get_lbolt() + 34144c06356bSdh drv_usectohz(PMCS_MAX_CONFIG_TIME); 34154c06356bSdh } 34164c06356bSdh pptr->changed = 0; 34174c06356bSdh 34184c06356bSdh /* 34194c06356bSdh * Step 2- make sure we don't overflow 34204c06356bSdh */ 34214c06356bSdh if (pptr->level == PMCS_MAX_XPND-1) { 3422c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_WARN, pptr, NULL, 34234c06356bSdh "%s: SAS expansion tree too deep", __func__); 34244c06356bSdh return; 34254c06356bSdh } 34264c06356bSdh 34274c06356bSdh /* 34284c06356bSdh * Step 3- Check if this expander is part of a wide phy that has 34294c06356bSdh * already been configured. 34304c06356bSdh * 34314c06356bSdh * This is known by checking this level for another EXPANDER device 34324c06356bSdh * with the same SAS address and isn't already marked as a subsidiary 34334c06356bSdh * phy and a parent whose SAS address is the same as our SAS address 34344c06356bSdh * (if there are parents). 34354c06356bSdh */ 34364c06356bSdh if (!IS_ROOT_PHY(pptr)) { 34374c06356bSdh /* 34384c06356bSdh * No need to lock the parent here because we're in discovery 34394c06356bSdh * and the only time a PHY's children pointer can change is 34404c06356bSdh * in discovery; either in pmcs_clear_expander (which has 34414c06356bSdh * already been called) or here, down below. Plus, trying to 34424c06356bSdh * grab the parent's lock here can cause deadlock. 34434c06356bSdh */ 34444c06356bSdh ctmp = pptr->parent->children; 34454c06356bSdh } else { 34464c06356bSdh ctmp = pwp->root_phys; 34474c06356bSdh root_phy = B_TRUE; 34484c06356bSdh } 34494c06356bSdh 34504c06356bSdh while (ctmp) { 34514c06356bSdh /* 34524c06356bSdh * If we've checked all PHYs up to pptr, we stop. Otherwise, 34534c06356bSdh * we'll be checking for a primary PHY with a higher PHY 34544c06356bSdh * number than pptr, which will never happen. The primary 34554c06356bSdh * PHY on non-root expanders will ALWAYS be the lowest 34564c06356bSdh * numbered PHY. 34574c06356bSdh */ 34584c06356bSdh if (ctmp == pptr) { 34594c06356bSdh break; 34604c06356bSdh } 34614c06356bSdh 34624c06356bSdh /* 34634c06356bSdh * If pptr and ctmp are root PHYs, just grab the mutex on 34644c06356bSdh * ctmp. No need to lock the entire tree. If they are not 34654c06356bSdh * root PHYs, there is no need to lock since a non-root PHY's 34664c06356bSdh * SAS address and other characteristics can only change in 34674c06356bSdh * discovery anyway. 34684c06356bSdh */ 34694c06356bSdh if (root_phy) { 34704c06356bSdh mutex_enter(&ctmp->phy_lock); 34714c06356bSdh } 34724c06356bSdh 34734c06356bSdh if (ctmp->dtype == EXPANDER && ctmp->width && 34744c06356bSdh memcmp(ctmp->sas_address, pptr->sas_address, 8) == 0) { 34754c06356bSdh int widephy = 0; 34764c06356bSdh /* 34774c06356bSdh * If these phys are not root PHYs, compare their SAS 34784c06356bSdh * addresses too. 34794c06356bSdh */ 34804c06356bSdh if (!root_phy) { 34814c06356bSdh if (memcmp(ctmp->parent->sas_address, 34824c06356bSdh pptr->parent->sas_address, 8) == 0) { 34834c06356bSdh widephy = 1; 34844c06356bSdh } 34854c06356bSdh } else { 34864c06356bSdh widephy = 1; 34874c06356bSdh } 34884c06356bSdh if (widephy) { 34894c06356bSdh ctmp->width++; 34904c06356bSdh pptr->subsidiary = 1; 3491499cfd15SDavid Hollister 3492499cfd15SDavid Hollister /* 3493499cfd15SDavid Hollister * Update the primary PHY's attached-port-pm 3494499cfd15SDavid Hollister * and target-port-pm information with the info 3495499cfd15SDavid Hollister * from this subsidiary 3496499cfd15SDavid Hollister */ 3497499cfd15SDavid Hollister pmcs_update_phy_pm_props(ctmp, 3498499cfd15SDavid Hollister pptr->att_port_pm_tmp, 3499499cfd15SDavid Hollister pptr->tgt_port_pm_tmp, B_TRUE); 3500499cfd15SDavid Hollister 3501c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 3502c3bc407cSdh "%s: PHY %s part of wide PHY %s " 3503c3bc407cSdh "(now %d wide)", __func__, pptr->path, 3504c3bc407cSdh ctmp->path, ctmp->width); 35054c06356bSdh if (root_phy) { 35064c06356bSdh mutex_exit(&ctmp->phy_lock); 35074c06356bSdh } 35084c06356bSdh return; 35094c06356bSdh } 35104c06356bSdh } 35114c06356bSdh 35124c06356bSdh cnext = ctmp->sibling; 35134c06356bSdh if (root_phy) { 35144c06356bSdh mutex_exit(&ctmp->phy_lock); 35154c06356bSdh } 35164c06356bSdh ctmp = cnext; 35174c06356bSdh } 35184c06356bSdh 35194c06356bSdh /* 35204c06356bSdh * Step 4- If we don't have a device handle, get one. Since this 35214c06356bSdh * is the primary PHY, make sure subsidiary is cleared. 35224c06356bSdh */ 35234c06356bSdh pptr->subsidiary = 0; 35246745c559SJesse Butler pptr->iport = iport; 35254c06356bSdh if (pmcs_get_device_handle(pwp, pptr)) { 35264c06356bSdh goto out; 35274c06356bSdh } 3528c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "Config expander %s " 35294c06356bSdh SAS_ADDR_FMT " dev id 0x%x lr 0x%x", pptr->path, 35304c06356bSdh SAS_ADDR_PRT(pptr->sas_address), pptr->device_id, pptr->link_rate); 35314c06356bSdh 35324c06356bSdh /* 35334c06356bSdh * Step 5- figure out how many phys are in this expander. 35344c06356bSdh */ 35354c06356bSdh nphy = pmcs_expander_get_nphy(pwp, pptr); 35364c06356bSdh if (nphy <= 0) { 35374c06356bSdh if (nphy == 0 && ddi_get_lbolt() < pptr->config_stop) { 35384c06356bSdh PHY_CHANGED(pwp, pptr); 35394c06356bSdh RESTART_DISCOVERY(pwp); 35404c06356bSdh } else { 3541c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 35424c06356bSdh "%s: Retries exhausted for %s, killing", __func__, 35434c06356bSdh pptr->path); 35444c06356bSdh pptr->config_stop = 0; 35454c06356bSdh pmcs_kill_changed(pwp, pptr, 0); 35464c06356bSdh } 35474c06356bSdh goto out; 35484c06356bSdh } 35494c06356bSdh 35504c06356bSdh /* 35514c06356bSdh * Step 6- Allocate a list of phys for this expander and figure out 35524c06356bSdh * what each one is. 35534c06356bSdh */ 35544c06356bSdh for (i = 0; i < nphy; i++) { 35554c06356bSdh ctmp = kmem_cache_alloc(pwp->phy_cache, KM_SLEEP); 35564c06356bSdh bzero(ctmp, sizeof (pmcs_phy_t)); 35574c06356bSdh ctmp->device_id = PMCS_INVALID_DEVICE_ID; 35584c06356bSdh ctmp->sibling = clist; 35594c06356bSdh ctmp->pend_dtype = NEW; /* Init pending dtype */ 35604c06356bSdh ctmp->config_stop = ddi_get_lbolt() + 35614c06356bSdh drv_usectohz(PMCS_MAX_CONFIG_TIME); 35624c06356bSdh clist = ctmp; 35634c06356bSdh } 35644c06356bSdh 35654c06356bSdh mutex_enter(&pwp->config_lock); 35664c06356bSdh if (pwp->config_changed) { 35674c06356bSdh RESTART_DISCOVERY_LOCKED(pwp); 35684c06356bSdh mutex_exit(&pwp->config_lock); 35694c06356bSdh /* 35704c06356bSdh * Clean up the newly allocated PHYs and return 35714c06356bSdh */ 35724c06356bSdh while (clist) { 35734c06356bSdh ctmp = clist->sibling; 35744c06356bSdh kmem_cache_free(pwp->phy_cache, clist); 35754c06356bSdh clist = ctmp; 35764c06356bSdh } 35774c06356bSdh return; 35784c06356bSdh } 35794c06356bSdh mutex_exit(&pwp->config_lock); 35804c06356bSdh 35814c06356bSdh /* 35824c06356bSdh * Step 7- Now fill in the rest of the static portions of the phy. 35834c06356bSdh */ 35844c06356bSdh for (i = 0, ctmp = clist; ctmp; ctmp = ctmp->sibling, i++) { 35854c06356bSdh ctmp->parent = pptr; 35864c06356bSdh ctmp->pwp = pwp; 35874c06356bSdh ctmp->level = pptr->level+1; 35884c06356bSdh ctmp->portid = pptr->portid; 35894c06356bSdh if (ctmp->tolerates_sas2) { 35904c06356bSdh ASSERT(i < SAS2_PHYNUM_MAX); 35914c06356bSdh ctmp->phynum = i & SAS2_PHYNUM_MASK; 35924c06356bSdh } else { 35934c06356bSdh ASSERT(i < SAS_PHYNUM_MAX); 35944c06356bSdh ctmp->phynum = i & SAS_PHYNUM_MASK; 35954c06356bSdh } 35964c06356bSdh pmcs_phy_name(pwp, ctmp, ctmp->path, sizeof (ctmp->path)); 35974c06356bSdh pmcs_lock_phy(ctmp); 35984c06356bSdh } 35994c06356bSdh 36004c06356bSdh /* 36014c06356bSdh * Step 8- Discover things about each phy in the expander. 36024c06356bSdh */ 36034c06356bSdh for (i = 0, ctmp = clist; ctmp; ctmp = ctmp->sibling, i++) { 36044c06356bSdh result = pmcs_expander_content_discover(pwp, pptr, ctmp); 36054c06356bSdh if (result <= 0) { 36064c06356bSdh if (ddi_get_lbolt() < pptr->config_stop) { 36074c06356bSdh PHY_CHANGED(pwp, pptr); 36084c06356bSdh RESTART_DISCOVERY(pwp); 36094c06356bSdh } else { 36104c06356bSdh pptr->config_stop = 0; 3611c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 36124c06356bSdh "%s: Retries exhausted for %s, killing", 36134c06356bSdh __func__, pptr->path); 36144c06356bSdh pmcs_kill_changed(pwp, pptr, 0); 36154c06356bSdh } 36164c06356bSdh goto out; 36174c06356bSdh } 36184c06356bSdh 36194c06356bSdh /* Set pend_dtype to dtype for 1st time initialization */ 36204c06356bSdh ctmp->pend_dtype = ctmp->dtype; 36214c06356bSdh } 36224c06356bSdh 36234c06356bSdh /* 36249aed1621SDavid Hollister * Step 9: Install the new list on the next level. There should 36259aed1621SDavid Hollister * typically be no children pointer on this PHY. There is one known 36269aed1621SDavid Hollister * case where this can happen, though. If a root PHY goes down and 36279aed1621SDavid Hollister * comes back up before discovery can run, we will fail to remove the 36289aed1621SDavid Hollister * children from that PHY since it will no longer be marked dead. 36299aed1621SDavid Hollister * However, in this case, all children should also be marked dead. If 36309aed1621SDavid Hollister * we see that, take those children and put them on the dead_phys list. 36314c06356bSdh */ 36324c06356bSdh if (pptr->children != NULL) { 36339aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 36349aed1621SDavid Hollister "%s: Expander @ %s still has children: Clean up", 3635c3bc407cSdh __func__, pptr->path); 36369aed1621SDavid Hollister pmcs_add_dead_phys(pwp, pptr->children); 36374c06356bSdh } 36384c06356bSdh 36399aed1621SDavid Hollister /* 36409aed1621SDavid Hollister * Set the new children pointer for this expander 36419aed1621SDavid Hollister */ 36429aed1621SDavid Hollister pptr->children = clist; 36434c06356bSdh clist = NULL; 36444c06356bSdh pptr->ncphy = nphy; 36454c06356bSdh pptr->configured = 1; 36464c06356bSdh 36474c06356bSdh /* 36484c06356bSdh * We only set width if we're greater than level 0. 36494c06356bSdh */ 36504c06356bSdh if (pptr->level) { 36514c06356bSdh pptr->width = 1; 36524c06356bSdh } 36534c06356bSdh 36544c06356bSdh /* 36554c06356bSdh * Now tell the rest of the world about us, as an SMP node. 36564c06356bSdh */ 36574c06356bSdh pptr->iport = iport; 36584c06356bSdh pmcs_new_tport(pwp, pptr); 36594c06356bSdh 36604c06356bSdh out: 36614c06356bSdh while (clist) { 36624c06356bSdh ctmp = clist->sibling; 36634c06356bSdh pmcs_unlock_phy(clist); 36644c06356bSdh kmem_cache_free(pwp->phy_cache, clist); 36654c06356bSdh clist = ctmp; 36664c06356bSdh } 36674c06356bSdh } 36684c06356bSdh 36694c06356bSdh /* 36704c06356bSdh * 2. Check expanders marked changed (but not dead) to see if they still have 36714c06356bSdh * the same number of phys and the same SAS address. Mark them, their subsidiary 36724c06356bSdh * phys (if wide) and their descendents dead if anything has changed. Check the 36734c06356bSdh * the devices they contain to see if *they* have changed. If they've changed 36744c06356bSdh * from type NOTHING we leave them marked changed to be configured later 36754c06356bSdh * (picking up a new SAS address and link rate if possible). Otherwise, any 36764c06356bSdh * change in type, SAS address or removal of target role will cause us to 36774c06356bSdh * mark them (and their descendents) as dead and cause any pending commands 36784c06356bSdh * and associated devices to be removed. 36794c06356bSdh * 36804c06356bSdh * Called with PHY (pptr) locked. 36814c06356bSdh */ 36824c06356bSdh 36834c06356bSdh static void 36844c06356bSdh pmcs_check_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 36854c06356bSdh { 36864c06356bSdh int nphy, result; 36874c06356bSdh pmcs_phy_t *ctmp, *local, *local_list = NULL, *local_tail = NULL; 36884c06356bSdh boolean_t kill_changed, changed; 36894c06356bSdh 3690c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 36914c06356bSdh "%s: check %s", __func__, pptr->path); 36924c06356bSdh 36934c06356bSdh /* 36944c06356bSdh * Step 1: Mark phy as not changed. We will mark it changed if we need 36954c06356bSdh * to retry. 36964c06356bSdh */ 36974c06356bSdh pptr->changed = 0; 36984c06356bSdh 36994c06356bSdh /* 37004c06356bSdh * Reset the config_stop time. Although we're not actually configuring 37014c06356bSdh * anything here, we do want some indication of when to give up trying 37024c06356bSdh * if we can't communicate with the expander. 37034c06356bSdh */ 37044c06356bSdh pptr->config_stop = ddi_get_lbolt() + 37054c06356bSdh drv_usectohz(PMCS_MAX_CONFIG_TIME); 37064c06356bSdh 37074c06356bSdh /* 37084c06356bSdh * Step 2: Figure out how many phys are in this expander. If 37094c06356bSdh * pmcs_expander_get_nphy returns 0 we ran out of resources, 37104c06356bSdh * so reschedule and try later. If it returns another error, 37114c06356bSdh * just return. 37124c06356bSdh */ 37134c06356bSdh nphy = pmcs_expander_get_nphy(pwp, pptr); 37144c06356bSdh if (nphy <= 0) { 37154c06356bSdh if ((nphy == 0) && (ddi_get_lbolt() < pptr->config_stop)) { 37164c06356bSdh PHY_CHANGED(pwp, pptr); 37174c06356bSdh RESTART_DISCOVERY(pwp); 37184c06356bSdh } else { 37194c06356bSdh pptr->config_stop = 0; 3720c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 37214c06356bSdh "%s: Retries exhausted for %s, killing", __func__, 37224c06356bSdh pptr->path); 37234c06356bSdh pmcs_kill_changed(pwp, pptr, 0); 37244c06356bSdh } 37254c06356bSdh return; 37264c06356bSdh } 37274c06356bSdh 37284c06356bSdh /* 37294c06356bSdh * Step 3: If the number of phys don't agree, kill the old sub-tree. 37304c06356bSdh */ 37314c06356bSdh if (nphy != pptr->ncphy) { 3732c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 37334c06356bSdh "%s: number of contained phys for %s changed from %d to %d", 37344c06356bSdh __func__, pptr->path, pptr->ncphy, nphy); 37354c06356bSdh /* 37364c06356bSdh * Force a rescan of this expander after dead contents 37374c06356bSdh * are cleared and removed. 37384c06356bSdh */ 37394c06356bSdh pmcs_kill_changed(pwp, pptr, 0); 37404c06356bSdh return; 37414c06356bSdh } 37424c06356bSdh 37434c06356bSdh /* 37444c06356bSdh * Step 4: if we're at the bottom of the stack, we're done 37454c06356bSdh * (we can't have any levels below us) 37464c06356bSdh */ 37474c06356bSdh if (pptr->level == PMCS_MAX_XPND-1) { 37484c06356bSdh return; 37494c06356bSdh } 37504c06356bSdh 37514c06356bSdh /* 37524c06356bSdh * Step 5: Discover things about each phy in this expander. We do 37534c06356bSdh * this by walking the current list of contained phys and doing a 37544c06356bSdh * content discovery for it to a local phy. 37554c06356bSdh */ 37564c06356bSdh ctmp = pptr->children; 37574c06356bSdh ASSERT(ctmp); 37584c06356bSdh if (ctmp == NULL) { 3759c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 37604c06356bSdh "%s: No children attached to expander @ %s?", __func__, 37614c06356bSdh pptr->path); 37624c06356bSdh return; 37634c06356bSdh } 37644c06356bSdh 37654c06356bSdh while (ctmp) { 37664c06356bSdh /* 37674c06356bSdh * Allocate a local PHY to contain the proposed new contents 37684c06356bSdh * and link it to the rest of the local PHYs so that they 37694c06356bSdh * can all be freed later. 37704c06356bSdh */ 37714c06356bSdh local = pmcs_clone_phy(ctmp); 37724c06356bSdh 37734c06356bSdh if (local_list == NULL) { 37744c06356bSdh local_list = local; 37754c06356bSdh local_tail = local; 37764c06356bSdh } else { 37774c06356bSdh local_tail->sibling = local; 37784c06356bSdh local_tail = local; 37794c06356bSdh } 37804c06356bSdh 37814c06356bSdh /* 37824c06356bSdh * Need to lock the local PHY since pmcs_expander_content_ 37834c06356bSdh * discovery may call pmcs_clear_phy on it, which expects 37844c06356bSdh * the PHY to be locked. 37854c06356bSdh */ 37864c06356bSdh pmcs_lock_phy(local); 37874c06356bSdh result = pmcs_expander_content_discover(pwp, pptr, local); 37884c06356bSdh pmcs_unlock_phy(local); 37894c06356bSdh if (result <= 0) { 37904c06356bSdh if (ddi_get_lbolt() < pptr->config_stop) { 37914c06356bSdh PHY_CHANGED(pwp, pptr); 37924c06356bSdh RESTART_DISCOVERY(pwp); 37934c06356bSdh } else { 37944c06356bSdh pptr->config_stop = 0; 3795c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 37964c06356bSdh "%s: Retries exhausted for %s, killing", 37974c06356bSdh __func__, pptr->path); 37984c06356bSdh pmcs_kill_changed(pwp, pptr, 0); 37994c06356bSdh } 38004c06356bSdh 38014c06356bSdh /* 38024c06356bSdh * Release all the local PHYs that we allocated. 38034c06356bSdh */ 38044c06356bSdh pmcs_free_phys(pwp, local_list); 38054c06356bSdh return; 38064c06356bSdh } 38074c06356bSdh 38084c06356bSdh ctmp = ctmp->sibling; 38094c06356bSdh } 38104c06356bSdh 38114c06356bSdh /* 38124c06356bSdh * Step 6: Compare the local PHY's contents to our current PHY. If 38134c06356bSdh * there are changes, take the appropriate action. 38144c06356bSdh * This is done in two steps (step 5 above, and 6 here) so that if we 38154c06356bSdh * have to bail during this process (e.g. pmcs_expander_content_discover 38164c06356bSdh * fails), we haven't actually changed the state of any of the real 38174c06356bSdh * PHYs. Next time we come through here, we'll be starting over from 38184c06356bSdh * scratch. This keeps us from marking a changed PHY as no longer 38194c06356bSdh * changed, but then having to bail only to come back next time and 38204c06356bSdh * think that the PHY hadn't changed. If this were to happen, we 38214c06356bSdh * would fail to properly configure the device behind this PHY. 38224c06356bSdh */ 38234c06356bSdh local = local_list; 38244c06356bSdh ctmp = pptr->children; 38254c06356bSdh 38264c06356bSdh while (ctmp) { 38274c06356bSdh changed = B_FALSE; 38284c06356bSdh kill_changed = B_FALSE; 38294c06356bSdh 38304c06356bSdh /* 38314c06356bSdh * We set local to local_list prior to this loop so that we 38324c06356bSdh * can simply walk the local_list while we walk this list. The 38334c06356bSdh * two lists should be completely in sync. 38344c06356bSdh * 38354c06356bSdh * Clear the changed flag here. 38364c06356bSdh */ 38374c06356bSdh ctmp->changed = 0; 38384c06356bSdh 38394c06356bSdh if (ctmp->dtype != local->dtype) { 38404c06356bSdh if (ctmp->dtype != NOTHING) { 3841c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL, 3842c3bc407cSdh "%s: %s type changed from %s to %s " 3843c3bc407cSdh "(killing)", __func__, ctmp->path, 3844c3bc407cSdh PHY_TYPE(ctmp), PHY_TYPE(local)); 38454c06356bSdh /* 38464c06356bSdh * Force a rescan of this expander after dead 38474c06356bSdh * contents are cleared and removed. 38484c06356bSdh */ 38494c06356bSdh changed = B_TRUE; 38504c06356bSdh kill_changed = B_TRUE; 38514c06356bSdh } else { 38524c06356bSdh changed = B_TRUE; 3853c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL, 38544c06356bSdh "%s: %s type changed from NOTHING to %s", 38554c06356bSdh __func__, ctmp->path, PHY_TYPE(local)); 385673a3eccdSDavid Hollister /* 385773a3eccdSDavid Hollister * Since this PHY was nothing and is now 385873a3eccdSDavid Hollister * something, reset the config_stop timer. 385973a3eccdSDavid Hollister */ 386073a3eccdSDavid Hollister ctmp->config_stop = ddi_get_lbolt() + 386173a3eccdSDavid Hollister drv_usectohz(PMCS_MAX_CONFIG_TIME); 38624c06356bSdh } 38634c06356bSdh 38644c06356bSdh } else if (ctmp->atdt != local->atdt) { 3865c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL, "%s: " 3866c3bc407cSdh "%s attached device type changed from %d to %d " 3867c3bc407cSdh "(killing)", __func__, ctmp->path, ctmp->atdt, 3868c3bc407cSdh local->atdt); 38694c06356bSdh /* 38704c06356bSdh * Force a rescan of this expander after dead 38714c06356bSdh * contents are cleared and removed. 38724c06356bSdh */ 38734c06356bSdh changed = B_TRUE; 38744c06356bSdh 38754c06356bSdh if (local->atdt == 0) { 38764c06356bSdh kill_changed = B_TRUE; 38774c06356bSdh } 38784c06356bSdh } else if (ctmp->link_rate != local->link_rate) { 3879c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, ctmp, NULL, "%s: %s " 3880c3bc407cSdh "changed speed from %s to %s", __func__, ctmp->path, 38814c06356bSdh pmcs_get_rate(ctmp->link_rate), 38824c06356bSdh pmcs_get_rate(local->link_rate)); 38834c06356bSdh /* If the speed changed from invalid, force rescan */ 38844c06356bSdh if (!PMCS_VALID_LINK_RATE(ctmp->link_rate)) { 38854c06356bSdh changed = B_TRUE; 38864c06356bSdh RESTART_DISCOVERY(pwp); 38874c06356bSdh } else { 38884c06356bSdh /* Just update to the new link rate */ 38894c06356bSdh ctmp->link_rate = local->link_rate; 38904c06356bSdh } 38914c06356bSdh 38924c06356bSdh if (!PMCS_VALID_LINK_RATE(local->link_rate)) { 38934c06356bSdh kill_changed = B_TRUE; 38944c06356bSdh } 38954c06356bSdh } else if (memcmp(ctmp->sas_address, local->sas_address, 38964c06356bSdh sizeof (ctmp->sas_address)) != 0) { 3897c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL, 3898c3bc407cSdh "%s: SAS Addr for %s changed from " SAS_ADDR_FMT 3899c3bc407cSdh "to " SAS_ADDR_FMT " (kill old tree)", __func__, 39004c06356bSdh ctmp->path, SAS_ADDR_PRT(ctmp->sas_address), 39014c06356bSdh SAS_ADDR_PRT(local->sas_address)); 39024c06356bSdh /* 39034c06356bSdh * Force a rescan of this expander after dead 39044c06356bSdh * contents are cleared and removed. 39054c06356bSdh */ 39064c06356bSdh changed = B_TRUE; 39074c06356bSdh } else { 3908c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL, 39094c06356bSdh "%s: %s looks the same (type %s)", 39104c06356bSdh __func__, ctmp->path, PHY_TYPE(ctmp)); 39114c06356bSdh /* 39124c06356bSdh * If EXPANDER, still mark it changed so we 39134c06356bSdh * re-evaluate its contents. If it's not an expander, 39144c06356bSdh * but it hasn't been configured, also mark it as 39154c06356bSdh * changed so that it will undergo configuration. 39164c06356bSdh */ 39174c06356bSdh if (ctmp->dtype == EXPANDER) { 39184c06356bSdh changed = B_TRUE; 39194c06356bSdh } else if ((ctmp->dtype != NOTHING) && 39204c06356bSdh !ctmp->configured) { 39214c06356bSdh ctmp->changed = 1; 39224c06356bSdh } else { 39234c06356bSdh /* It simply hasn't changed */ 39244c06356bSdh ctmp->changed = 0; 39254c06356bSdh } 39264c06356bSdh } 39274c06356bSdh 39284c06356bSdh /* 39294c06356bSdh * If the PHY changed, call pmcs_kill_changed if indicated, 39304c06356bSdh * update its contents to reflect its current state and mark it 39314c06356bSdh * as changed. 39324c06356bSdh */ 39334c06356bSdh if (changed) { 39344c06356bSdh /* 39354c06356bSdh * pmcs_kill_changed will mark the PHY as changed, so 39364c06356bSdh * only do PHY_CHANGED if we did not do kill_changed. 39374c06356bSdh */ 39384c06356bSdh if (kill_changed) { 39394c06356bSdh pmcs_kill_changed(pwp, ctmp, 0); 39404c06356bSdh } else { 39414c06356bSdh /* 39424c06356bSdh * If we're not killing the device, it's not 39434c06356bSdh * dead. Mark the PHY as changed. 39444c06356bSdh */ 39454c06356bSdh PHY_CHANGED(pwp, ctmp); 39464c06356bSdh 39474c06356bSdh if (ctmp->dead) { 39484c06356bSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, 3949c3bc407cSdh ctmp, NULL, "%s: Unmarking PHY %s " 3950c3bc407cSdh "dead, restarting discovery", 39514c06356bSdh __func__, ctmp->path); 39524c06356bSdh ctmp->dead = 0; 39534c06356bSdh RESTART_DISCOVERY(pwp); 39544c06356bSdh } 39554c06356bSdh } 39564c06356bSdh 39574c06356bSdh /* 39584c06356bSdh * If the dtype of this PHY is now NOTHING, mark it as 39594c06356bSdh * unconfigured. Set pend_dtype to what the new dtype 39604c06356bSdh * is. It'll get updated at the end of the discovery 39614c06356bSdh * process. 39624c06356bSdh */ 39634c06356bSdh if (local->dtype == NOTHING) { 39644c06356bSdh bzero(ctmp->sas_address, 39654c06356bSdh sizeof (local->sas_address)); 39664c06356bSdh ctmp->atdt = 0; 39674c06356bSdh ctmp->link_rate = 0; 39684c06356bSdh ctmp->pend_dtype = NOTHING; 39694c06356bSdh ctmp->configured = 0; 39704c06356bSdh } else { 39714c06356bSdh (void) memcpy(ctmp->sas_address, 39724c06356bSdh local->sas_address, 39734c06356bSdh sizeof (local->sas_address)); 39744c06356bSdh ctmp->atdt = local->atdt; 39754c06356bSdh ctmp->link_rate = local->link_rate; 39764c06356bSdh ctmp->pend_dtype = local->dtype; 39774c06356bSdh } 39784c06356bSdh } 39794c06356bSdh 39804c06356bSdh local = local->sibling; 39814c06356bSdh ctmp = ctmp->sibling; 39824c06356bSdh } 39834c06356bSdh 39844c06356bSdh /* 39854c06356bSdh * If we got to here, that means we were able to see all the PHYs 39864c06356bSdh * and we can now update all of the real PHYs with the information 39874c06356bSdh * we got on the local PHYs. Once that's done, free all the local 39884c06356bSdh * PHYs. 39894c06356bSdh */ 39904c06356bSdh 39914c06356bSdh pmcs_free_phys(pwp, local_list); 39924c06356bSdh } 39934c06356bSdh 39944c06356bSdh /* 39954c06356bSdh * Top level routine to check expanders. We call pmcs_check_expander for 39964c06356bSdh * each expander. Since we're not doing any configuration right now, it 39974c06356bSdh * doesn't matter if this is breadth-first. 39984c06356bSdh */ 399973a3eccdSDavid Hollister static void 40004c06356bSdh pmcs_check_expanders(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 40014c06356bSdh { 40024c06356bSdh pmcs_phy_t *phyp, *pnext, *pchild; 40034c06356bSdh 4004c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 4005c3bc407cSdh "%s: %s", __func__, pptr->path); 40064c06356bSdh 40074c06356bSdh /* 40084c06356bSdh * Check each expander at this level 40094c06356bSdh */ 40104c06356bSdh phyp = pptr; 401173a3eccdSDavid Hollister while (phyp) { 40124c06356bSdh pmcs_lock_phy(phyp); 40134c06356bSdh 40144c06356bSdh if ((phyp->dtype == EXPANDER) && phyp->changed && 40154c06356bSdh !phyp->dead && !phyp->subsidiary && 40164c06356bSdh phyp->configured) { 40174c06356bSdh pmcs_check_expander(pwp, phyp); 40184c06356bSdh } 40194c06356bSdh 40204c06356bSdh pnext = phyp->sibling; 40214c06356bSdh pmcs_unlock_phy(phyp); 40224c06356bSdh phyp = pnext; 40234c06356bSdh } 40244c06356bSdh 40254c06356bSdh /* 40264c06356bSdh * Now check the children 40274c06356bSdh */ 40284c06356bSdh phyp = pptr; 402973a3eccdSDavid Hollister while (phyp) { 40304c06356bSdh pmcs_lock_phy(phyp); 40314c06356bSdh pnext = phyp->sibling; 40324c06356bSdh pchild = phyp->children; 40334c06356bSdh pmcs_unlock_phy(phyp); 40344c06356bSdh 40354c06356bSdh if (pchild) { 403673a3eccdSDavid Hollister pmcs_check_expanders(pwp, pchild); 40374c06356bSdh } 40384c06356bSdh 40394c06356bSdh phyp = pnext; 40404c06356bSdh } 40414c06356bSdh } 40424c06356bSdh 40434c06356bSdh /* 40444c06356bSdh * Called with softstate and PHY locked 40454c06356bSdh */ 40464c06356bSdh static void 40474c06356bSdh pmcs_clear_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr, int level) 40484c06356bSdh { 40494c06356bSdh pmcs_phy_t *ctmp; 40504c06356bSdh 40514c06356bSdh ASSERT(mutex_owned(&pwp->lock)); 40524c06356bSdh ASSERT(mutex_owned(&pptr->phy_lock)); 40534c06356bSdh ASSERT(pptr->level < PMCS_MAX_XPND - 1); 40544c06356bSdh 4055c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 4056c3bc407cSdh "%s: checking %s", __func__, pptr->path); 40574c06356bSdh 40584c06356bSdh ctmp = pptr->children; 40594c06356bSdh while (ctmp) { 40604c06356bSdh /* 40614c06356bSdh * If the expander is dead, mark its children dead 40624c06356bSdh */ 40634c06356bSdh if (pptr->dead) { 40644c06356bSdh ctmp->dead = 1; 40654c06356bSdh } 40664c06356bSdh if (ctmp->dtype == EXPANDER) { 40674c06356bSdh pmcs_clear_expander(pwp, ctmp, level + 1); 40684c06356bSdh } 40694c06356bSdh ctmp = ctmp->sibling; 40704c06356bSdh } 40714c06356bSdh 40724c06356bSdh /* 40734c06356bSdh * If this expander is not dead, we're done here. 40744c06356bSdh */ 40754c06356bSdh if (!pptr->dead) { 40764c06356bSdh return; 40774c06356bSdh } 40784c06356bSdh 40794c06356bSdh /* 40804c06356bSdh * Now snip out the list of children below us and release them 40814c06356bSdh */ 40829aed1621SDavid Hollister if (pptr->children) { 40839aed1621SDavid Hollister pmcs_add_dead_phys(pwp, pptr->children); 40844c06356bSdh } 40854c06356bSdh 40864c06356bSdh pptr->children = NULL; 40874c06356bSdh 40884c06356bSdh /* 40894c06356bSdh * Clear subsidiary phys as well. Getting the parent's PHY lock 40904c06356bSdh * is only necessary if level == 0 since otherwise the parent is 40914c06356bSdh * already locked. 40924c06356bSdh */ 40934c06356bSdh if (!IS_ROOT_PHY(pptr)) { 40944c06356bSdh if (level == 0) { 40954c06356bSdh mutex_enter(&pptr->parent->phy_lock); 40964c06356bSdh } 40974c06356bSdh ctmp = pptr->parent->children; 40984c06356bSdh if (level == 0) { 40994c06356bSdh mutex_exit(&pptr->parent->phy_lock); 41004c06356bSdh } 41014c06356bSdh } else { 41024c06356bSdh ctmp = pwp->root_phys; 41034c06356bSdh } 41044c06356bSdh 41054c06356bSdh while (ctmp) { 41064c06356bSdh if (ctmp == pptr) { 41074c06356bSdh ctmp = ctmp->sibling; 41084c06356bSdh continue; 41094c06356bSdh } 41104c06356bSdh /* 41114c06356bSdh * We only need to lock subsidiary PHYs on the level 0 41124c06356bSdh * expander. Any children of that expander, subsidiaries or 41134c06356bSdh * not, will already be locked. 41144c06356bSdh */ 41154c06356bSdh if (level == 0) { 41164c06356bSdh pmcs_lock_phy(ctmp); 41174c06356bSdh } 41184c06356bSdh if (ctmp->dtype != EXPANDER || ctmp->subsidiary == 0 || 41194c06356bSdh memcmp(ctmp->sas_address, pptr->sas_address, 41204c06356bSdh sizeof (ctmp->sas_address)) != 0) { 41214c06356bSdh if (level == 0) { 41224c06356bSdh pmcs_unlock_phy(ctmp); 41234c06356bSdh } 41244c06356bSdh ctmp = ctmp->sibling; 41254c06356bSdh continue; 41264c06356bSdh } 4127c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL, 4128c3bc407cSdh "%s: subsidiary %s", __func__, ctmp->path); 41294c06356bSdh pmcs_clear_phy(pwp, ctmp); 41304c06356bSdh if (level == 0) { 41314c06356bSdh pmcs_unlock_phy(ctmp); 41324c06356bSdh } 41334c06356bSdh ctmp = ctmp->sibling; 41344c06356bSdh } 41354c06356bSdh 41364c06356bSdh pmcs_clear_phy(pwp, pptr); 41374c06356bSdh } 41384c06356bSdh 41394c06356bSdh /* 41404c06356bSdh * Called with PHY locked and with scratch acquired. We return 0 if 41414c06356bSdh * we fail to allocate resources or notice that the configuration 41424c06356bSdh * count changed while we were running the command. We return 41434c06356bSdh * less than zero if we had an I/O error or received an unsupported 41444c06356bSdh * configuration. Otherwise we return the number of phys in the 41454c06356bSdh * expander. 41464c06356bSdh */ 41474c06356bSdh #define DFM(m, y) if (m == NULL) m = y 41484c06356bSdh static int 41494c06356bSdh pmcs_expander_get_nphy(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 41504c06356bSdh { 41514c06356bSdh struct pmcwork *pwrk; 4152*3be32c0fSJesse Butler pmcs_iport_t *iport; 41534c06356bSdh char buf[64]; 41544c06356bSdh const uint_t rdoff = 0x100; /* returned data offset */ 41554c06356bSdh smp_response_frame_t *srf; 41564c06356bSdh smp_report_general_resp_t *srgr; 41574c06356bSdh uint32_t msg[PMCS_MSG_SIZE], *ptr, htag, status, ival; 41589aed1621SDavid Hollister int result = 0; 41594c06356bSdh 41604c06356bSdh ival = 0x40001100; 41619aed1621SDavid Hollister 41624c06356bSdh again: 41639aed1621SDavid Hollister if (!pptr->iport || !pptr->valid_device_id) { 41649aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target, 41659aed1621SDavid Hollister "%s: Can't reach PHY %s", __func__, pptr->path); 41669aed1621SDavid Hollister goto out; 41679aed1621SDavid Hollister } 41689aed1621SDavid Hollister 41694c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 41704c06356bSdh if (pwrk == NULL) { 41714c06356bSdh goto out; 41724c06356bSdh } 41734c06356bSdh (void) memset(pwp->scratch, 0x77, PMCS_SCRATCH_SIZE); 41744c06356bSdh pwrk->arg = pwp->scratch; 41754c06356bSdh pwrk->dtype = pptr->dtype; 4176*3be32c0fSJesse Butler pwrk->xp = pptr->target; 4177978d7443SSrikanth Suravajhala pwrk->htag |= PMCS_TAG_NONIO_CMD; 41784c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 41794c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 41804c06356bSdh if (ptr == NULL) { 41814c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 4182c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, NULL, 4183c3bc407cSdh "%s: GET_IQ_ENTRY failed", __func__); 41844c06356bSdh pmcs_pwork(pwp, pwrk); 41854c06356bSdh goto out; 41864c06356bSdh } 41874c06356bSdh 41884c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST)); 41894c06356bSdh msg[1] = LE_32(pwrk->htag); 41904c06356bSdh msg[2] = LE_32(pptr->device_id); 41914c06356bSdh msg[3] = LE_32((4 << SMP_REQUEST_LENGTH_SHIFT) | SMP_INDIRECT_RESPONSE); 41924c06356bSdh /* 41934c06356bSdh * Send SMP REPORT GENERAL (of either SAS1.1 or SAS2 flavors). 41944c06356bSdh */ 41954c06356bSdh msg[4] = BE_32(ival); 41964c06356bSdh msg[5] = 0; 41974c06356bSdh msg[6] = 0; 41984c06356bSdh msg[7] = 0; 41994c06356bSdh msg[8] = 0; 42004c06356bSdh msg[9] = 0; 42014c06356bSdh msg[10] = 0; 42024c06356bSdh msg[11] = 0; 42034c06356bSdh msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff)); 42044c06356bSdh msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff)); 42054c06356bSdh msg[14] = LE_32(PMCS_SCRATCH_SIZE - rdoff); 42064c06356bSdh msg[15] = 0; 42074c06356bSdh 42084c06356bSdh COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE); 42096745c559SJesse Butler 4210*3be32c0fSJesse Butler pmcs_hold_iport(pptr->iport); 4211*3be32c0fSJesse Butler iport = pptr->iport; 4212*3be32c0fSJesse Butler pmcs_smp_acquire(iport); 42134c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 42144c06356bSdh htag = pwrk->htag; 42154c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 42164c06356bSdh pmcs_unlock_phy(pptr); 42174c06356bSdh WAIT_FOR(pwrk, 1000, result); 4218*3be32c0fSJesse Butler pmcs_pwork(pwp, pwrk); 4219*3be32c0fSJesse Butler pmcs_smp_release(iport); 4220*3be32c0fSJesse Butler pmcs_rele_iport(iport); 4221601c90f1SSrikanth, Ramana pmcs_lock_phy(pptr); 42226745c559SJesse Butler 42234c06356bSdh 42244c06356bSdh mutex_enter(&pwp->config_lock); 42254c06356bSdh if (pwp->config_changed) { 42264c06356bSdh RESTART_DISCOVERY_LOCKED(pwp); 42274c06356bSdh mutex_exit(&pwp->config_lock); 42284c06356bSdh result = 0; 42294c06356bSdh goto out; 42304c06356bSdh } 42314c06356bSdh mutex_exit(&pwp->config_lock); 42324c06356bSdh 42334c06356bSdh if (result) { 42344c06356bSdh pmcs_timed_out(pwp, htag, __func__); 4235c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 42364c06356bSdh "%s: Issuing SMP ABORT for htag 0x%08x", __func__, htag); 42374c06356bSdh if (pmcs_abort(pwp, pptr, htag, 0, 0)) { 4238c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 42394c06356bSdh "%s: Unable to issue SMP ABORT for htag 0x%08x", 42404c06356bSdh __func__, htag); 42414c06356bSdh } else { 4242c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 42434c06356bSdh "%s: Issuing SMP ABORT for htag 0x%08x", 42444c06356bSdh __func__, htag); 42454c06356bSdh } 42464c06356bSdh result = 0; 42474c06356bSdh goto out; 42484c06356bSdh } 42494c06356bSdh ptr = (void *)pwp->scratch; 42504c06356bSdh status = LE_32(ptr[2]); 42514c06356bSdh if (status == PMCOUT_STATUS_UNDERFLOW || 42524c06356bSdh status == PMCOUT_STATUS_OVERFLOW) { 4253c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, pptr, NULL, 42544c06356bSdh "%s: over/underflow", __func__); 42554c06356bSdh status = PMCOUT_STATUS_OK; 42564c06356bSdh } 42574c06356bSdh srf = (smp_response_frame_t *)&((uint32_t *)pwp->scratch)[rdoff >> 2]; 42584c06356bSdh srgr = (smp_report_general_resp_t *) 42594c06356bSdh &((uint32_t *)pwp->scratch)[(rdoff >> 2)+1]; 42604c06356bSdh 42614c06356bSdh if (status != PMCOUT_STATUS_OK) { 42624c06356bSdh char *nag = NULL; 42634c06356bSdh (void) snprintf(buf, sizeof (buf), 42644c06356bSdh "%s: SMP op failed (0x%x)", __func__, status); 42654c06356bSdh switch (status) { 42664c06356bSdh case PMCOUT_STATUS_IO_PORT_IN_RESET: 42674c06356bSdh DFM(nag, "I/O Port In Reset"); 42684c06356bSdh /* FALLTHROUGH */ 42694c06356bSdh case PMCOUT_STATUS_ERROR_HW_TIMEOUT: 42704c06356bSdh DFM(nag, "Hardware Timeout"); 42714c06356bSdh /* FALLTHROUGH */ 42724c06356bSdh case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE: 42734c06356bSdh DFM(nag, "Internal SMP Resource Failure"); 42744c06356bSdh /* FALLTHROUGH */ 42754c06356bSdh case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY: 42764c06356bSdh DFM(nag, "PHY Not Ready"); 42774c06356bSdh /* FALLTHROUGH */ 42784c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: 42794c06356bSdh DFM(nag, "Connection Rate Not Supported"); 42804c06356bSdh /* FALLTHROUGH */ 42814c06356bSdh case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT: 42824c06356bSdh DFM(nag, "Open Retry Timeout"); 42834c06356bSdh /* FALLTHROUGH */ 4284a25672a1SDavid Hollister case PMCOUT_STATUS_IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY: 4285a25672a1SDavid Hollister DFM(nag, "HW Resource Busy"); 4286a25672a1SDavid Hollister /* FALLTHROUGH */ 42874c06356bSdh case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR: 42884c06356bSdh DFM(nag, "Response Connection Error"); 4289c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 42904c06356bSdh "%s: expander %s SMP operation failed (%s)", 42914c06356bSdh __func__, pptr->path, nag); 42924c06356bSdh break; 42934c06356bSdh 42944c06356bSdh /* 42954c06356bSdh * For the IO_DS_NON_OPERATIONAL case, we need to kick off 42964c06356bSdh * device state recovery and return 0 so that the caller 42974c06356bSdh * doesn't assume this expander is dead for good. 42984c06356bSdh */ 42994c06356bSdh case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL: { 43004c06356bSdh pmcs_xscsi_t *xp = pptr->target; 43014c06356bSdh 4302c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, pptr, xp, 43034c06356bSdh "%s: expander %s device state non-operational", 43044c06356bSdh __func__, pptr->path); 43054c06356bSdh 43064c06356bSdh if (xp == NULL) { 43076745c559SJesse Butler /* 43086745c559SJesse Butler * Kick off recovery right now. 43096745c559SJesse Butler */ 43106745c559SJesse Butler SCHEDULE_WORK(pwp, PMCS_WORK_DS_ERR_RECOVERY); 43116745c559SJesse Butler (void) ddi_taskq_dispatch(pwp->tq, pmcs_worker, 43126745c559SJesse Butler pwp, DDI_NOSLEEP); 43136745c559SJesse Butler } else { 43146745c559SJesse Butler mutex_enter(&xp->statlock); 43156745c559SJesse Butler pmcs_start_dev_state_recovery(xp, pptr); 43166745c559SJesse Butler mutex_exit(&xp->statlock); 43174c06356bSdh } 43184c06356bSdh 43194c06356bSdh break; 43204c06356bSdh } 43214c06356bSdh 43224c06356bSdh default: 43234c06356bSdh pmcs_print_entry(pwp, PMCS_PRT_DEBUG, buf, ptr); 43244c06356bSdh result = -EIO; 43254c06356bSdh break; 43264c06356bSdh } 43274c06356bSdh } else if (srf->srf_frame_type != SMP_FRAME_TYPE_RESPONSE) { 4328c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 43294c06356bSdh "%s: bad response frame type 0x%x", 43304c06356bSdh __func__, srf->srf_frame_type); 43314c06356bSdh result = -EINVAL; 43324c06356bSdh } else if (srf->srf_function != SMP_FUNC_REPORT_GENERAL) { 4333c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 4334c3bc407cSdh "%s: bad response function 0x%x", 43354c06356bSdh __func__, srf->srf_function); 43364c06356bSdh result = -EINVAL; 43374c06356bSdh } else if (srf->srf_result != 0) { 43384c06356bSdh /* 43394c06356bSdh * Check to see if we have a value of 3 for failure and 43404c06356bSdh * whether we were using a SAS2.0 allocation length value 43414c06356bSdh * and retry without it. 43424c06356bSdh */ 43434c06356bSdh if (srf->srf_result == 3 && (ival & 0xff00)) { 43444c06356bSdh ival &= ~0xff00; 4345c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 43464c06356bSdh "%s: err 0x%x with SAS2 request- retry with SAS1", 43474c06356bSdh __func__, srf->srf_result); 43484c06356bSdh goto again; 43494c06356bSdh } 4350c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 4351c3bc407cSdh "%s: bad response 0x%x", __func__, srf->srf_result); 43524c06356bSdh result = -EINVAL; 43534c06356bSdh } else if (srgr->srgr_configuring) { 4354c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 43554c06356bSdh "%s: expander at phy %s is still configuring", 43564c06356bSdh __func__, pptr->path); 43574c06356bSdh result = 0; 43584c06356bSdh } else { 43594c06356bSdh result = srgr->srgr_number_of_phys; 43604c06356bSdh if (ival & 0xff00) { 43614c06356bSdh pptr->tolerates_sas2 = 1; 43624c06356bSdh } 43639aed1621SDavid Hollister /* 43649aed1621SDavid Hollister * Save off the REPORT_GENERAL response 43659aed1621SDavid Hollister */ 43669aed1621SDavid Hollister bcopy(srgr, &pptr->rg_resp, sizeof (smp_report_general_resp_t)); 4367c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 43684c06356bSdh "%s has %d phys and %s SAS2", pptr->path, result, 43694c06356bSdh pptr->tolerates_sas2? "tolerates" : "does not tolerate"); 43704c06356bSdh } 43714c06356bSdh out: 43724c06356bSdh return (result); 43734c06356bSdh } 43744c06356bSdh 43754c06356bSdh /* 43764c06356bSdh * Called with expander locked (and thus, pptr) as well as all PHYs up to 43774c06356bSdh * the root, and scratch acquired. Return 0 if we fail to allocate resources 43784c06356bSdh * or notice that the configuration changed while we were running the command. 43794c06356bSdh * 43804c06356bSdh * We return less than zero if we had an I/O error or received an 43814c06356bSdh * unsupported configuration. 43824c06356bSdh */ 43834c06356bSdh static int 43844c06356bSdh pmcs_expander_content_discover(pmcs_hw_t *pwp, pmcs_phy_t *expander, 43854c06356bSdh pmcs_phy_t *pptr) 43864c06356bSdh { 43874c06356bSdh struct pmcwork *pwrk; 4388*3be32c0fSJesse Butler pmcs_iport_t *iport; 43894c06356bSdh char buf[64]; 43904c06356bSdh uint8_t sas_address[8]; 43914c06356bSdh uint8_t att_sas_address[8]; 43924c06356bSdh smp_response_frame_t *srf; 43934c06356bSdh smp_discover_resp_t *sdr; 43944c06356bSdh const uint_t rdoff = 0x100; /* returned data offset */ 43954c06356bSdh uint8_t *roff; 43964c06356bSdh uint32_t status, *ptr, msg[PMCS_MSG_SIZE], htag; 43979aed1621SDavid Hollister int result = 0; 43984c06356bSdh uint8_t ini_support; 43994c06356bSdh uint8_t tgt_support; 44004c06356bSdh 44019aed1621SDavid Hollister if (!expander->iport || !expander->valid_device_id) { 44029aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, expander, expander->target, 44039aed1621SDavid Hollister "%s: Can't reach PHY %s", __func__, expander->path); 44049aed1621SDavid Hollister goto out; 44059aed1621SDavid Hollister } 44069aed1621SDavid Hollister 44074c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, expander); 44084c06356bSdh if (pwrk == NULL) { 44094c06356bSdh goto out; 44104c06356bSdh } 44114c06356bSdh (void) memset(pwp->scratch, 0x77, PMCS_SCRATCH_SIZE); 44124c06356bSdh pwrk->arg = pwp->scratch; 44134c06356bSdh pwrk->dtype = expander->dtype; 4414*3be32c0fSJesse Butler pwrk->xp = expander->target; 4415978d7443SSrikanth Suravajhala pwrk->htag |= PMCS_TAG_NONIO_CMD; 44164c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST)); 44174c06356bSdh msg[1] = LE_32(pwrk->htag); 44184c06356bSdh msg[2] = LE_32(expander->device_id); 44194c06356bSdh msg[3] = LE_32((12 << SMP_REQUEST_LENGTH_SHIFT) | 44204c06356bSdh SMP_INDIRECT_RESPONSE); 44214c06356bSdh /* 44224c06356bSdh * Send SMP DISCOVER (of either SAS1.1 or SAS2 flavors). 44234c06356bSdh */ 44244c06356bSdh if (expander->tolerates_sas2) { 44254c06356bSdh msg[4] = BE_32(0x40101B00); 44264c06356bSdh } else { 44274c06356bSdh msg[4] = BE_32(0x40100000); 44284c06356bSdh } 44294c06356bSdh msg[5] = 0; 44304c06356bSdh msg[6] = BE_32((pptr->phynum << 16)); 44314c06356bSdh msg[7] = 0; 44324c06356bSdh msg[8] = 0; 44334c06356bSdh msg[9] = 0; 44344c06356bSdh msg[10] = 0; 44354c06356bSdh msg[11] = 0; 44364c06356bSdh msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff)); 44374c06356bSdh msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff)); 44384c06356bSdh msg[14] = LE_32(PMCS_SCRATCH_SIZE - rdoff); 44394c06356bSdh msg[15] = 0; 44404c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 44414c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 44424c06356bSdh if (ptr == NULL) { 44434c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 44444c06356bSdh goto out; 44454c06356bSdh } 44464c06356bSdh 44474c06356bSdh COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE); 44486745c559SJesse Butler 4449*3be32c0fSJesse Butler pmcs_hold_iport(expander->iport); 4450*3be32c0fSJesse Butler iport = expander->iport; 4451*3be32c0fSJesse Butler pmcs_smp_acquire(iport); 44524c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 44534c06356bSdh htag = pwrk->htag; 44544c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 44554c06356bSdh pmcs_unlock_phy(expander); 44564c06356bSdh WAIT_FOR(pwrk, 1000, result); 44574c06356bSdh pmcs_pwork(pwp, pwrk); 4458*3be32c0fSJesse Butler pmcs_smp_release(iport); 4459*3be32c0fSJesse Butler pmcs_rele_iport(iport); 4460*3be32c0fSJesse Butler pmcs_lock_phy(expander); 44614c06356bSdh 44624c06356bSdh mutex_enter(&pwp->config_lock); 44634c06356bSdh if (pwp->config_changed) { 44644c06356bSdh RESTART_DISCOVERY_LOCKED(pwp); 44654c06356bSdh mutex_exit(&pwp->config_lock); 44664c06356bSdh result = 0; 44674c06356bSdh goto out; 44684c06356bSdh } 44694c06356bSdh mutex_exit(&pwp->config_lock); 44704c06356bSdh 44714c06356bSdh if (result) { 44726745c559SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__); 44734c06356bSdh if (pmcs_abort(pwp, expander, htag, 0, 0)) { 4474c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 44754c06356bSdh "%s: Unable to issue SMP ABORT for htag 0x%08x", 44764c06356bSdh __func__, htag); 44774c06356bSdh } else { 4478c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 44794c06356bSdh "%s: Issuing SMP ABORT for htag 0x%08x", 44804c06356bSdh __func__, htag); 44814c06356bSdh } 44824c06356bSdh result = -ETIMEDOUT; 44834c06356bSdh goto out; 44844c06356bSdh } 44854c06356bSdh ptr = (void *)pwp->scratch; 44864c06356bSdh /* 44874c06356bSdh * Point roff to the DMA offset for returned data 44884c06356bSdh */ 44894c06356bSdh roff = pwp->scratch; 44904c06356bSdh roff += rdoff; 44914c06356bSdh srf = (smp_response_frame_t *)roff; 44924c06356bSdh sdr = (smp_discover_resp_t *)(roff+4); 44934c06356bSdh status = LE_32(ptr[2]); 44944c06356bSdh if (status == PMCOUT_STATUS_UNDERFLOW || 44954c06356bSdh status == PMCOUT_STATUS_OVERFLOW) { 4496c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, pptr, NULL, 44974c06356bSdh "%s: over/underflow", __func__); 44984c06356bSdh status = PMCOUT_STATUS_OK; 44994c06356bSdh } 45004c06356bSdh if (status != PMCOUT_STATUS_OK) { 45014c06356bSdh char *nag = NULL; 45024c06356bSdh (void) snprintf(buf, sizeof (buf), 45034c06356bSdh "%s: SMP op failed (0x%x)", __func__, status); 45044c06356bSdh switch (status) { 45054c06356bSdh case PMCOUT_STATUS_ERROR_HW_TIMEOUT: 45064c06356bSdh DFM(nag, "Hardware Timeout"); 45074c06356bSdh /* FALLTHROUGH */ 45084c06356bSdh case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE: 45094c06356bSdh DFM(nag, "Internal SMP Resource Failure"); 45104c06356bSdh /* FALLTHROUGH */ 45114c06356bSdh case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY: 45124c06356bSdh DFM(nag, "PHY Not Ready"); 45134c06356bSdh /* FALLTHROUGH */ 45144c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: 45154c06356bSdh DFM(nag, "Connection Rate Not Supported"); 45164c06356bSdh /* FALLTHROUGH */ 45174c06356bSdh case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT: 45184c06356bSdh DFM(nag, "Open Retry Timeout"); 45194c06356bSdh /* FALLTHROUGH */ 4520a25672a1SDavid Hollister case PMCOUT_STATUS_IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY: 4521a25672a1SDavid Hollister DFM(nag, "HW Resource Busy"); 4522a25672a1SDavid Hollister /* FALLTHROUGH */ 45234c06356bSdh case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR: 45244c06356bSdh DFM(nag, "Response Connection Error"); 4525c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 45264c06356bSdh "%s: expander %s SMP operation failed (%s)", 45274c06356bSdh __func__, pptr->path, nag); 45284c06356bSdh break; 45294c06356bSdh default: 45304c06356bSdh pmcs_print_entry(pwp, PMCS_PRT_DEBUG, buf, ptr); 45314c06356bSdh result = -EIO; 45324c06356bSdh break; 45334c06356bSdh } 45344c06356bSdh goto out; 45354c06356bSdh } else if (srf->srf_frame_type != SMP_FRAME_TYPE_RESPONSE) { 4536c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 45374c06356bSdh "%s: bad response frame type 0x%x", 45384c06356bSdh __func__, srf->srf_frame_type); 45394c06356bSdh result = -EINVAL; 45404c06356bSdh goto out; 45414c06356bSdh } else if (srf->srf_function != SMP_FUNC_DISCOVER) { 4542c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 4543c3bc407cSdh "%s: bad response function 0x%x", 45444c06356bSdh __func__, srf->srf_function); 45454c06356bSdh result = -EINVAL; 45464c06356bSdh goto out; 45474c06356bSdh } else if (srf->srf_result != SMP_RES_FUNCTION_ACCEPTED) { 45484c06356bSdh result = pmcs_smp_function_result(pwp, srf); 45494c06356bSdh /* Need not fail if PHY is Vacant */ 45504c06356bSdh if (result != SMP_RES_PHY_VACANT) { 45514c06356bSdh result = -EINVAL; 45524c06356bSdh goto out; 45534c06356bSdh } 45544c06356bSdh } 45554c06356bSdh 45569aed1621SDavid Hollister /* 45579aed1621SDavid Hollister * Save off the DISCOVER response 45589aed1621SDavid Hollister */ 45599aed1621SDavid Hollister bcopy(sdr, &pptr->disc_resp, sizeof (smp_discover_resp_t)); 45609aed1621SDavid Hollister 45614c06356bSdh ini_support = (sdr->sdr_attached_sata_host | 45624c06356bSdh (sdr->sdr_attached_smp_initiator << 1) | 45634c06356bSdh (sdr->sdr_attached_stp_initiator << 2) | 45644c06356bSdh (sdr->sdr_attached_ssp_initiator << 3)); 45654c06356bSdh 45664c06356bSdh tgt_support = (sdr->sdr_attached_sata_device | 45674c06356bSdh (sdr->sdr_attached_smp_target << 1) | 45684c06356bSdh (sdr->sdr_attached_stp_target << 2) | 45694c06356bSdh (sdr->sdr_attached_ssp_target << 3)); 45704c06356bSdh 45714c06356bSdh pmcs_wwn2barray(BE_64(sdr->sdr_sas_addr), sas_address); 45724c06356bSdh pmcs_wwn2barray(BE_64(sdr->sdr_attached_sas_addr), att_sas_address); 45734c06356bSdh 4574c80dec56SDavid Hollister pptr->virtual = sdr->sdr_virtual_phy; 4575c80dec56SDavid Hollister 45769aed1621SDavid Hollister /* 45779aed1621SDavid Hollister * Set the routing attribute regardless of the PHY type. 45789aed1621SDavid Hollister */ 45799aed1621SDavid Hollister pptr->routing_attr = sdr->sdr_routing_attr; 45809aed1621SDavid Hollister 45814c06356bSdh switch (sdr->sdr_attached_device_type) { 45824c06356bSdh case SAS_IF_DTYPE_ENDPOINT: 4583c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 45844c06356bSdh "exp_content: %s atdt=0x%x lr=%x is=%x ts=%x SAS=" 45854c06356bSdh SAS_ADDR_FMT " attSAS=" SAS_ADDR_FMT " atPHY=%x", 45864c06356bSdh pptr->path, 45874c06356bSdh sdr->sdr_attached_device_type, 45884c06356bSdh sdr->sdr_negotiated_logical_link_rate, 45894c06356bSdh ini_support, 45904c06356bSdh tgt_support, 45914c06356bSdh SAS_ADDR_PRT(sas_address), 45924c06356bSdh SAS_ADDR_PRT(att_sas_address), 45934c06356bSdh sdr->sdr_attached_phy_identifier); 45944c06356bSdh 45954c06356bSdh if (sdr->sdr_attached_sata_device || 45964c06356bSdh sdr->sdr_attached_stp_target) { 45974c06356bSdh pptr->dtype = SATA; 45984c06356bSdh } else if (sdr->sdr_attached_ssp_target) { 45994c06356bSdh pptr->dtype = SAS; 46004c06356bSdh } else if (tgt_support || ini_support) { 4601c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 4602c3bc407cSdh "%s: %s has tgt support=%x init support=(%x)", 46034c06356bSdh __func__, pptr->path, tgt_support, ini_support); 46044c06356bSdh } 46059aed1621SDavid Hollister 46069aed1621SDavid Hollister switch (pptr->routing_attr) { 46079aed1621SDavid Hollister case SMP_ROUTING_SUBTRACTIVE: 46089aed1621SDavid Hollister case SMP_ROUTING_TABLE: 46099aed1621SDavid Hollister case SMP_ROUTING_DIRECT: 46109aed1621SDavid Hollister pptr->routing_method = SMP_ROUTING_DIRECT; 46119aed1621SDavid Hollister break; 46129aed1621SDavid Hollister default: 46139aed1621SDavid Hollister pptr->routing_method = 0xff; /* Invalid method */ 46149aed1621SDavid Hollister break; 46159aed1621SDavid Hollister } 4616616875b4SDavid Hollister pmcs_update_phy_pm_props(pptr, (1ULL << pptr->phynum), 4617616875b4SDavid Hollister (1ULL << sdr->sdr_attached_phy_identifier), B_TRUE); 46184c06356bSdh break; 46194c06356bSdh case SAS_IF_DTYPE_EDGE: 46204c06356bSdh case SAS_IF_DTYPE_FANOUT: 4621c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 46224c06356bSdh "exp_content: %s atdt=0x%x lr=%x is=%x ts=%x SAS=" 46234c06356bSdh SAS_ADDR_FMT " attSAS=" SAS_ADDR_FMT " atPHY=%x", 46244c06356bSdh pptr->path, 46254c06356bSdh sdr->sdr_attached_device_type, 46264c06356bSdh sdr->sdr_negotiated_logical_link_rate, 46274c06356bSdh ini_support, 46284c06356bSdh tgt_support, 46294c06356bSdh SAS_ADDR_PRT(sas_address), 46304c06356bSdh SAS_ADDR_PRT(att_sas_address), 46314c06356bSdh sdr->sdr_attached_phy_identifier); 46324c06356bSdh if (sdr->sdr_attached_smp_target) { 46334c06356bSdh /* 46344c06356bSdh * Avoid configuring phys that just point back 46354c06356bSdh * at a parent phy 46364c06356bSdh */ 46374c06356bSdh if (expander->parent && 46384c06356bSdh memcmp(expander->parent->sas_address, 46394c06356bSdh att_sas_address, 46404c06356bSdh sizeof (expander->parent->sas_address)) == 0) { 4641c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG3, pptr, NULL, 46424c06356bSdh "%s: skipping port back to parent " 46434c06356bSdh "expander (%s)", __func__, pptr->path); 46444c06356bSdh pptr->dtype = NOTHING; 46454c06356bSdh break; 46464c06356bSdh } 46474c06356bSdh pptr->dtype = EXPANDER; 46484c06356bSdh 46494c06356bSdh } else if (tgt_support || ini_support) { 4650c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 4651c3bc407cSdh "%s has tgt support=%x init support=(%x)", 46524c06356bSdh pptr->path, tgt_support, ini_support); 46534c06356bSdh pptr->dtype = EXPANDER; 46544c06356bSdh } 46559aed1621SDavid Hollister if (pptr->routing_attr == SMP_ROUTING_DIRECT) { 46569aed1621SDavid Hollister pptr->routing_method = 0xff; /* Invalid method */ 46579aed1621SDavid Hollister } else { 46589aed1621SDavid Hollister pptr->routing_method = pptr->routing_attr; 46599aed1621SDavid Hollister } 4660616875b4SDavid Hollister pmcs_update_phy_pm_props(pptr, (1ULL << pptr->phynum), 4661616875b4SDavid Hollister (1ULL << sdr->sdr_attached_phy_identifier), B_TRUE); 46624c06356bSdh break; 46634c06356bSdh default: 46644c06356bSdh pptr->dtype = NOTHING; 46654c06356bSdh break; 46664c06356bSdh } 46674c06356bSdh if (pptr->dtype != NOTHING) { 46684c06356bSdh pmcs_phy_t *ctmp; 46694c06356bSdh 46704c06356bSdh /* 46714c06356bSdh * If the attached device is a SATA device and the expander 46724c06356bSdh * is (possibly) a SAS2 compliant expander, check for whether 46734c06356bSdh * there is a NAA=5 WWN field starting at this offset and 46744c06356bSdh * use that for the SAS Address for this device. 46754c06356bSdh */ 46764c06356bSdh if (expander->tolerates_sas2 && pptr->dtype == SATA && 4677a25672a1SDavid Hollister (roff[SAS_ATTACHED_NAME_OFFSET] >> 8) == NAA_IEEE_REG) { 46784c06356bSdh (void) memcpy(pptr->sas_address, 46794c06356bSdh &roff[SAS_ATTACHED_NAME_OFFSET], 8); 46804c06356bSdh } else { 46814c06356bSdh (void) memcpy(pptr->sas_address, att_sas_address, 8); 46824c06356bSdh } 46834c06356bSdh pptr->atdt = (sdr->sdr_attached_device_type); 46844c06356bSdh /* 46854c06356bSdh * Now run up from the expander's parent up to the top to 46864c06356bSdh * make sure we only use the least common link_rate. 46874c06356bSdh */ 46884c06356bSdh for (ctmp = expander->parent; ctmp; ctmp = ctmp->parent) { 46894c06356bSdh if (ctmp->link_rate < 46904c06356bSdh sdr->sdr_negotiated_logical_link_rate) { 4691c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 46924c06356bSdh "%s: derating link rate from %x to %x due " 46934c06356bSdh "to %s being slower", pptr->path, 46944c06356bSdh sdr->sdr_negotiated_logical_link_rate, 46954c06356bSdh ctmp->link_rate, 46964c06356bSdh ctmp->path); 46974c06356bSdh sdr->sdr_negotiated_logical_link_rate = 46984c06356bSdh ctmp->link_rate; 46994c06356bSdh } 47004c06356bSdh } 47014c06356bSdh pptr->link_rate = sdr->sdr_negotiated_logical_link_rate; 47024c06356bSdh pptr->state.prog_min_rate = sdr->sdr_prog_min_phys_link_rate; 47034c06356bSdh pptr->state.hw_min_rate = sdr->sdr_hw_min_phys_link_rate; 47044c06356bSdh pptr->state.prog_max_rate = sdr->sdr_prog_max_phys_link_rate; 47054c06356bSdh pptr->state.hw_max_rate = sdr->sdr_hw_max_phys_link_rate; 47064c06356bSdh PHY_CHANGED(pwp, pptr); 47074c06356bSdh } else { 47084c06356bSdh pmcs_clear_phy(pwp, pptr); 47094c06356bSdh } 47104c06356bSdh result = 1; 47114c06356bSdh out: 47124c06356bSdh return (result); 47134c06356bSdh } 47144c06356bSdh 47154c06356bSdh /* 47164c06356bSdh * Get a work structure and assign it a tag with type and serial number 47174c06356bSdh * If a structure is returned, it is returned locked. 47184c06356bSdh */ 47194c06356bSdh pmcwork_t * 47204c06356bSdh pmcs_gwork(pmcs_hw_t *pwp, uint32_t tag_type, pmcs_phy_t *phyp) 47214c06356bSdh { 47224c06356bSdh pmcwork_t *p; 47234c06356bSdh uint16_t snum; 47244c06356bSdh uint32_t off; 47254c06356bSdh 47264c06356bSdh mutex_enter(&pwp->wfree_lock); 47274c06356bSdh p = STAILQ_FIRST(&pwp->wf); 47284c06356bSdh if (p == NULL) { 47294c06356bSdh /* 47304c06356bSdh * If we couldn't get a work structure, it's time to bite 47314c06356bSdh * the bullet, grab the pfree_lock and copy over all the 47324c06356bSdh * work structures from the pending free list to the actual 473302b04f6eSSrikanth, Ramana * free list (assuming it's not also empty). 47344c06356bSdh */ 47354c06356bSdh mutex_enter(&pwp->pfree_lock); 473602b04f6eSSrikanth, Ramana if (STAILQ_FIRST(&pwp->pf) == NULL) { 473702b04f6eSSrikanth, Ramana mutex_exit(&pwp->pfree_lock); 473802b04f6eSSrikanth, Ramana mutex_exit(&pwp->wfree_lock); 473902b04f6eSSrikanth, Ramana return (NULL); 474002b04f6eSSrikanth, Ramana } 47414c06356bSdh pwp->wf.stqh_first = pwp->pf.stqh_first; 47424c06356bSdh pwp->wf.stqh_last = pwp->pf.stqh_last; 47434c06356bSdh STAILQ_INIT(&pwp->pf); 47444c06356bSdh mutex_exit(&pwp->pfree_lock); 47454c06356bSdh 47464c06356bSdh p = STAILQ_FIRST(&pwp->wf); 474702b04f6eSSrikanth, Ramana ASSERT(p != NULL); 47484c06356bSdh } 47494c06356bSdh STAILQ_REMOVE(&pwp->wf, p, pmcwork, next); 47504c06356bSdh snum = pwp->wserno++; 47514c06356bSdh mutex_exit(&pwp->wfree_lock); 47524c06356bSdh 47534c06356bSdh off = p - pwp->work; 47544c06356bSdh 47554c06356bSdh mutex_enter(&p->lock); 47564c06356bSdh ASSERT(p->state == PMCS_WORK_STATE_NIL); 47574c06356bSdh ASSERT(p->htag == PMCS_TAG_FREE); 47584c06356bSdh p->htag = (tag_type << PMCS_TAG_TYPE_SHIFT) & PMCS_TAG_TYPE_MASK; 47594c06356bSdh p->htag |= ((snum << PMCS_TAG_SERNO_SHIFT) & PMCS_TAG_SERNO_MASK); 47604c06356bSdh p->htag |= ((off << PMCS_TAG_INDEX_SHIFT) & PMCS_TAG_INDEX_MASK); 47614c06356bSdh p->start = gethrtime(); 47624c06356bSdh p->state = PMCS_WORK_STATE_READY; 47634c06356bSdh p->ssp_event = 0; 47644c06356bSdh p->dead = 0; 47654c06356bSdh 47664c06356bSdh if (phyp) { 47674c06356bSdh p->phy = phyp; 47684c06356bSdh pmcs_inc_phy_ref_count(phyp); 47694c06356bSdh } 47704c06356bSdh 47714c06356bSdh return (p); 47724c06356bSdh } 47734c06356bSdh 47744c06356bSdh /* 47754c06356bSdh * Called with pwrk lock held. Returned with lock released. 47764c06356bSdh */ 47774c06356bSdh void 47784c06356bSdh pmcs_pwork(pmcs_hw_t *pwp, pmcwork_t *p) 47794c06356bSdh { 47804c06356bSdh ASSERT(p != NULL); 47814c06356bSdh ASSERT(mutex_owned(&p->lock)); 47824c06356bSdh 47834c06356bSdh p->last_ptr = p->ptr; 47844c06356bSdh p->last_arg = p->arg; 47854c06356bSdh p->last_phy = p->phy; 47864c06356bSdh p->last_xp = p->xp; 47874c06356bSdh p->last_htag = p->htag; 47884c06356bSdh p->last_state = p->state; 47894c06356bSdh p->finish = gethrtime(); 47904c06356bSdh 47914c06356bSdh if (p->phy) { 47924c06356bSdh pmcs_dec_phy_ref_count(p->phy); 47934c06356bSdh } 47944c06356bSdh 47954c06356bSdh p->state = PMCS_WORK_STATE_NIL; 47964c06356bSdh p->htag = PMCS_TAG_FREE; 47974c06356bSdh p->xp = NULL; 47984c06356bSdh p->ptr = NULL; 47994c06356bSdh p->arg = NULL; 48004c06356bSdh p->phy = NULL; 4801c3bc407cSdh p->abt_htag = 0; 48024c06356bSdh p->timer = 0; 48034c06356bSdh mutex_exit(&p->lock); 48044c06356bSdh 48054c06356bSdh if (mutex_tryenter(&pwp->wfree_lock) == 0) { 48064c06356bSdh mutex_enter(&pwp->pfree_lock); 48074c06356bSdh STAILQ_INSERT_TAIL(&pwp->pf, p, next); 48084c06356bSdh mutex_exit(&pwp->pfree_lock); 48094c06356bSdh } else { 48104c06356bSdh STAILQ_INSERT_TAIL(&pwp->wf, p, next); 48114c06356bSdh mutex_exit(&pwp->wfree_lock); 48124c06356bSdh } 48134c06356bSdh } 48144c06356bSdh 48154c06356bSdh /* 48164c06356bSdh * Find a work structure based upon a tag and make sure that the tag 48174c06356bSdh * serial number matches the work structure we've found. 48184c06356bSdh * If a structure is found, its lock is held upon return. 4819978d7443SSrikanth Suravajhala * If lock_phy is B_TRUE, then lock the phy also when returning the work struct 48204c06356bSdh */ 48214c06356bSdh pmcwork_t * 4822978d7443SSrikanth Suravajhala pmcs_tag2wp(pmcs_hw_t *pwp, uint32_t htag, boolean_t lock_phy) 48234c06356bSdh { 48244c06356bSdh pmcwork_t *p; 48254c06356bSdh uint32_t idx = PMCS_TAG_INDEX(htag); 48264c06356bSdh 48274c06356bSdh p = &pwp->work[idx]; 48284c06356bSdh 48294c06356bSdh mutex_enter(&p->lock); 48304c06356bSdh if (p->htag == htag) { 4831978d7443SSrikanth Suravajhala if (lock_phy) { 4832978d7443SSrikanth Suravajhala mutex_exit(&p->lock); 4833978d7443SSrikanth Suravajhala mutex_enter(&p->phy->phy_lock); 4834978d7443SSrikanth Suravajhala mutex_enter(&p->lock); 4835978d7443SSrikanth Suravajhala } 48364c06356bSdh return (p); 48374c06356bSdh } 48384c06356bSdh mutex_exit(&p->lock); 4839c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, 4840c3bc407cSdh "INDEX 0x%x HTAG 0x%x got p->htag 0x%x", idx, htag, p->htag); 48414c06356bSdh return (NULL); 48424c06356bSdh } 48434c06356bSdh 48444c06356bSdh /* 48454c06356bSdh * Issue an abort for a command or for all commands. 48464c06356bSdh * 48474c06356bSdh * Since this can be called from interrupt context, 48484c06356bSdh * we don't wait for completion if wait is not set. 48494c06356bSdh * 48504c06356bSdh * Called with PHY lock held. 48514c06356bSdh */ 48524c06356bSdh int 48534c06356bSdh pmcs_abort(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint32_t tag, int all_cmds, 48544c06356bSdh int wait) 48554c06356bSdh { 48564c06356bSdh pmcwork_t *pwrk; 48574c06356bSdh pmcs_xscsi_t *tgt; 48584c06356bSdh uint32_t msg[PMCS_MSG_SIZE], *ptr; 48594c06356bSdh int result, abt_type; 48604c06356bSdh uint32_t abt_htag, status; 48614c06356bSdh 48624c06356bSdh if (pptr->abort_all_start) { 4863c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, "%s: ABORT_ALL for " 4864c3bc407cSdh "(%s) already in progress.", __func__, pptr->path); 48654c06356bSdh return (EBUSY); 48664c06356bSdh } 48674c06356bSdh 48684c06356bSdh switch (pptr->dtype) { 48694c06356bSdh case SAS: 48704c06356bSdh abt_type = PMCIN_SSP_ABORT; 48714c06356bSdh break; 48724c06356bSdh case SATA: 48734c06356bSdh abt_type = PMCIN_SATA_ABORT; 48744c06356bSdh break; 48754c06356bSdh case EXPANDER: 48764c06356bSdh abt_type = PMCIN_SMP_ABORT; 48774c06356bSdh break; 48784c06356bSdh default: 48794c06356bSdh return (0); 48804c06356bSdh } 48814c06356bSdh 48824c06356bSdh pwrk = pmcs_gwork(pwp, wait ? PMCS_TAG_TYPE_WAIT : PMCS_TAG_TYPE_NONE, 48834c06356bSdh pptr); 48844c06356bSdh 48854c06356bSdh if (pwrk == NULL) { 4886c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__); 48874c06356bSdh return (ENOMEM); 48884c06356bSdh } 48894c06356bSdh 48904c06356bSdh pwrk->dtype = pptr->dtype; 4891*3be32c0fSJesse Butler pwrk->xp = pptr->target; 4892978d7443SSrikanth Suravajhala pwrk->htag |= PMCS_TAG_NONIO_CMD; 48934c06356bSdh if (wait) { 48944c06356bSdh pwrk->arg = msg; 48954c06356bSdh } 48964c06356bSdh if (pptr->valid_device_id == 0) { 48974c06356bSdh pmcs_pwork(pwp, pwrk); 4898c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 4899c3bc407cSdh "%s: Invalid DeviceID", __func__); 49004c06356bSdh return (ENODEV); 49014c06356bSdh } 49024c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, abt_type)); 49034c06356bSdh msg[1] = LE_32(pwrk->htag); 49044c06356bSdh msg[2] = LE_32(pptr->device_id); 49054c06356bSdh if (all_cmds) { 49064c06356bSdh msg[3] = 0; 49074c06356bSdh msg[4] = LE_32(1); 49084c06356bSdh pwrk->ptr = NULL; 49094c06356bSdh pptr->abort_all_start = gethrtime(); 49104c06356bSdh } else { 49114c06356bSdh msg[3] = LE_32(tag); 49124c06356bSdh msg[4] = 0; 4913c3bc407cSdh pwrk->abt_htag = tag; 49144c06356bSdh } 49154c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 49164c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 49174c06356bSdh if (ptr == NULL) { 49184c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 49194c06356bSdh pmcs_pwork(pwp, pwrk); 4920*3be32c0fSJesse Butler pptr->abort_all_start = 0; 4921c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__); 49224c06356bSdh return (ENOMEM); 49234c06356bSdh } 49244c06356bSdh 49254c06356bSdh COPY_MESSAGE(ptr, msg, 5); 49264c06356bSdh if (all_cmds) { 4927c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 49284c06356bSdh "%s: aborting all commands for %s device %s. (htag=0x%x)", 49294c06356bSdh __func__, pmcs_get_typename(pptr->dtype), pptr->path, 49304c06356bSdh msg[1]); 49314c06356bSdh } else { 4932c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 49334c06356bSdh "%s: aborting tag 0x%x for %s device %s. (htag=0x%x)", 49344c06356bSdh __func__, tag, pmcs_get_typename(pptr->dtype), pptr->path, 49354c06356bSdh msg[1]); 49364c06356bSdh } 49374c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 49384c06356bSdh 49394c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 49404c06356bSdh if (!wait) { 49414c06356bSdh mutex_exit(&pwrk->lock); 49424c06356bSdh return (0); 49434c06356bSdh } 49444c06356bSdh 49454c06356bSdh abt_htag = pwrk->htag; 4946*3be32c0fSJesse Butler pmcs_unlock_phy(pptr); 49474c06356bSdh WAIT_FOR(pwrk, 1000, result); 49484c06356bSdh pmcs_pwork(pwp, pwrk); 4949*3be32c0fSJesse Butler pmcs_lock_phy(pptr); 4950*3be32c0fSJesse Butler tgt = pptr->target; 49514c06356bSdh 49524c06356bSdh if (all_cmds) { 49534c06356bSdh pptr->abort_all_start = 0; 49544c06356bSdh cv_signal(&pptr->abort_all_cv); 49554c06356bSdh } 49564c06356bSdh 49574c06356bSdh if (result) { 4958c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, 49594c06356bSdh "%s: Abort (htag 0x%08x) request timed out", 49604c06356bSdh __func__, abt_htag); 49614c06356bSdh if (tgt != NULL) { 49624c06356bSdh mutex_enter(&tgt->statlock); 49634c06356bSdh if ((tgt->dev_state != PMCS_DEVICE_STATE_IN_RECOVERY) && 49644c06356bSdh (tgt->dev_state != 49654c06356bSdh PMCS_DEVICE_STATE_NON_OPERATIONAL)) { 4966c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, 49674c06356bSdh "%s: Trying DS error recovery for tgt 0x%p", 49684c06356bSdh __func__, (void *)tgt); 49694c06356bSdh (void) pmcs_send_err_recovery_cmd(pwp, 4970145e0143Sdh PMCS_DEVICE_STATE_IN_RECOVERY, pptr, tgt); 49714c06356bSdh } 49724c06356bSdh mutex_exit(&tgt->statlock); 49734c06356bSdh } 49744c06356bSdh return (ETIMEDOUT); 49754c06356bSdh } 49764c06356bSdh 49774c06356bSdh status = LE_32(msg[2]); 49784c06356bSdh if (status != PMCOUT_STATUS_OK) { 49794c06356bSdh /* 49804c06356bSdh * The only non-success status are IO_NOT_VALID & 49814c06356bSdh * IO_ABORT_IN_PROGRESS. 49824c06356bSdh * In case of IO_ABORT_IN_PROGRESS, the other ABORT cmd's 49834c06356bSdh * status is of concern and this duplicate cmd status can 49844c06356bSdh * be ignored. 49854c06356bSdh * If IO_NOT_VALID, that's not an error per-se. 49864c06356bSdh * For abort of single I/O complete the command anyway. 49874c06356bSdh * If, however, we were aborting all, that is a problem 49884c06356bSdh * as IO_NOT_VALID really means that the IO or device is 49894c06356bSdh * not there. So, discovery process will take of the cleanup. 49904c06356bSdh */ 4991c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, 4992c3bc407cSdh "%s: abort result 0x%x", __func__, LE_32(msg[2])); 49934c06356bSdh if (all_cmds) { 49944c06356bSdh PHY_CHANGED(pwp, pptr); 49954c06356bSdh RESTART_DISCOVERY(pwp); 49964c06356bSdh } else { 49974c06356bSdh return (EINVAL); 49984c06356bSdh } 49994c06356bSdh 50004c06356bSdh return (0); 50014c06356bSdh } 50024c06356bSdh 50034c06356bSdh if (tgt != NULL) { 50044c06356bSdh mutex_enter(&tgt->statlock); 50054c06356bSdh if (tgt->dev_state == PMCS_DEVICE_STATE_IN_RECOVERY) { 5006c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, 50074c06356bSdh "%s: Restoring OPERATIONAL dev_state for tgt 0x%p", 50084c06356bSdh __func__, (void *)tgt); 50094c06356bSdh (void) pmcs_send_err_recovery_cmd(pwp, 5010145e0143Sdh PMCS_DEVICE_STATE_OPERATIONAL, pptr, tgt); 50114c06356bSdh } 50124c06356bSdh mutex_exit(&tgt->statlock); 50134c06356bSdh } 50144c06356bSdh 50154c06356bSdh return (0); 50164c06356bSdh } 50174c06356bSdh 50184c06356bSdh /* 50194c06356bSdh * Issue a task management function to an SSP device. 50204c06356bSdh * 50214c06356bSdh * Called with PHY lock held. 50224c06356bSdh * statlock CANNOT be held upon entry. 50234c06356bSdh */ 50244c06356bSdh int 50254c06356bSdh pmcs_ssp_tmf(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint8_t tmf, uint32_t tag, 50264c06356bSdh uint64_t lun, uint32_t *response) 50274c06356bSdh { 50284c06356bSdh int result, ds; 50294c06356bSdh uint8_t local[PMCS_QENTRY_SIZE << 1], *xd; 50304c06356bSdh sas_ssp_rsp_iu_t *rptr = (void *)local; 50314c06356bSdh static const uint8_t ssp_rsp_evec[] = { 50324c06356bSdh 0x58, 0x61, 0x56, 0x72, 0x00 50334c06356bSdh }; 50344c06356bSdh uint32_t msg[PMCS_MSG_SIZE], *ptr, status; 50354c06356bSdh struct pmcwork *pwrk; 50364c06356bSdh pmcs_xscsi_t *xp; 50374c06356bSdh 50384c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 50394c06356bSdh if (pwrk == NULL) { 5040c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__); 50414c06356bSdh return (ENOMEM); 50424c06356bSdh } 50434c06356bSdh /* 50444c06356bSdh * NB: We use the PMCS_OQ_GENERAL outbound queue 50454c06356bSdh * NB: so as to not get entangled in normal I/O 50464c06356bSdh * NB: processing. 50474c06356bSdh */ 5048978d7443SSrikanth Suravajhala pwrk->htag |= PMCS_TAG_NONIO_CMD; 50494c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 50504c06356bSdh PMCIN_SSP_INI_TM_START)); 50514c06356bSdh msg[1] = LE_32(pwrk->htag); 50524c06356bSdh msg[2] = LE_32(pptr->device_id); 50534c06356bSdh if (tmf == SAS_ABORT_TASK || tmf == SAS_QUERY_TASK) { 50544c06356bSdh msg[3] = LE_32(tag); 50554c06356bSdh } else { 50564c06356bSdh msg[3] = 0; 50574c06356bSdh } 50584c06356bSdh msg[4] = LE_32(tmf); 50594c06356bSdh msg[5] = BE_32((uint32_t)lun); 50604c06356bSdh msg[6] = BE_32((uint32_t)(lun >> 32)); 50614c06356bSdh msg[7] = LE_32(PMCIN_MESSAGE_REPORT); 50624c06356bSdh 50634c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 50644c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 50654c06356bSdh if (ptr == NULL) { 50664c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 50674c06356bSdh pmcs_pwork(pwp, pwrk); 5068c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__); 50694c06356bSdh return (ENOMEM); 50704c06356bSdh } 50714c06356bSdh COPY_MESSAGE(ptr, msg, 7); 50724c06356bSdh pwrk->arg = msg; 50734c06356bSdh pwrk->dtype = pptr->dtype; 50744c06356bSdh xp = pptr->target; 507556976565SDavid Hollister pwrk->xp = xp; 507656976565SDavid Hollister 50774c06356bSdh if (xp != NULL) { 50784c06356bSdh mutex_enter(&xp->statlock); 50794c06356bSdh if (xp->dev_state == PMCS_DEVICE_STATE_NON_OPERATIONAL) { 50804c06356bSdh mutex_exit(&xp->statlock); 50814c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 50824c06356bSdh pmcs_pwork(pwp, pwrk); 5083c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, "%s: Not " 5084c3bc407cSdh "sending '%s' because DS is '%s'", __func__, 5085c3bc407cSdh pmcs_tmf2str(tmf), pmcs_status_str 50864c06356bSdh (PMCOUT_STATUS_IO_DS_NON_OPERATIONAL)); 50874c06356bSdh return (EIO); 50884c06356bSdh } 50894c06356bSdh mutex_exit(&xp->statlock); 50904c06356bSdh } 50914c06356bSdh 5092c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 50934c06356bSdh "%s: sending '%s' to %s (lun %llu) tag 0x%x", __func__, 50944c06356bSdh pmcs_tmf2str(tmf), pptr->path, (unsigned long long) lun, tag); 50954c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 50964c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 50974c06356bSdh 50984c06356bSdh pmcs_unlock_phy(pptr); 50994c06356bSdh /* 51004c06356bSdh * This is a command sent to the target device, so it can take 51014c06356bSdh * significant amount of time to complete when path & device is busy. 51024c06356bSdh * Set a timeout to 20 seconds 51034c06356bSdh */ 51044c06356bSdh WAIT_FOR(pwrk, 20000, result); 51054c06356bSdh pmcs_pwork(pwp, pwrk); 5106*3be32c0fSJesse Butler pmcs_lock_phy(pptr); 5107*3be32c0fSJesse Butler xp = pptr->target; 51084c06356bSdh 51094c06356bSdh if (result) { 51104c06356bSdh if (xp == NULL) { 51114c06356bSdh return (ETIMEDOUT); 51124c06356bSdh } 51134c06356bSdh 51144c06356bSdh mutex_enter(&xp->statlock); 51154c06356bSdh pmcs_start_dev_state_recovery(xp, pptr); 51164c06356bSdh mutex_exit(&xp->statlock); 51174c06356bSdh return (ETIMEDOUT); 51184c06356bSdh } 51194c06356bSdh 51204c06356bSdh status = LE_32(msg[2]); 51214c06356bSdh if (status != PMCOUT_STATUS_OK) { 5122c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 51234c06356bSdh "%s: status %s for TMF %s action to %s, lun %llu", 51244c06356bSdh __func__, pmcs_status_str(status), pmcs_tmf2str(tmf), 51254c06356bSdh pptr->path, (unsigned long long) lun); 51264c06356bSdh if ((status == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) || 51274c06356bSdh (status == PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK) || 51284c06356bSdh (status == PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS)) { 51294c06356bSdh ds = PMCS_DEVICE_STATE_NON_OPERATIONAL; 51304c06356bSdh } else if (status == PMCOUT_STATUS_IO_DS_IN_RECOVERY) { 51314c06356bSdh /* 51324c06356bSdh * If the status is IN_RECOVERY, it's an indication 51334c06356bSdh * that it's now time for us to request to have the 51344c06356bSdh * device state set to OPERATIONAL since we're the ones 51354c06356bSdh * that requested recovery to begin with. 51364c06356bSdh */ 51374c06356bSdh ds = PMCS_DEVICE_STATE_OPERATIONAL; 51384c06356bSdh } else { 51394c06356bSdh ds = PMCS_DEVICE_STATE_IN_RECOVERY; 51404c06356bSdh } 51414c06356bSdh if (xp != NULL) { 51424c06356bSdh mutex_enter(&xp->statlock); 51434c06356bSdh if (xp->dev_state != ds) { 5144c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 51454c06356bSdh "%s: Sending err recovery cmd" 51464c06356bSdh " for tgt 0x%p (status = %s)", 51474c06356bSdh __func__, (void *)xp, 51484c06356bSdh pmcs_status_str(status)); 5149145e0143Sdh (void) pmcs_send_err_recovery_cmd(pwp, ds, 5150145e0143Sdh pptr, xp); 51514c06356bSdh } 51524c06356bSdh mutex_exit(&xp->statlock); 51534c06356bSdh } 51544c06356bSdh return (EIO); 51554c06356bSdh } else { 51564c06356bSdh ds = PMCS_DEVICE_STATE_OPERATIONAL; 51574c06356bSdh if (xp != NULL) { 51584c06356bSdh mutex_enter(&xp->statlock); 51594c06356bSdh if (xp->dev_state != ds) { 5160c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 51614c06356bSdh "%s: Sending err recovery cmd" 51624c06356bSdh " for tgt 0x%p (status = %s)", 51634c06356bSdh __func__, (void *)xp, 51644c06356bSdh pmcs_status_str(status)); 5165145e0143Sdh (void) pmcs_send_err_recovery_cmd(pwp, ds, 5166145e0143Sdh pptr, xp); 51674c06356bSdh } 51684c06356bSdh mutex_exit(&xp->statlock); 51694c06356bSdh } 51704c06356bSdh } 51714c06356bSdh if (LE_32(msg[3]) == 0) { 5172c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 5173c3bc407cSdh "TMF completed with no response"); 51744c06356bSdh return (EIO); 51754c06356bSdh } 51764c06356bSdh pmcs_endian_transform(pwp, local, &msg[5], ssp_rsp_evec); 51774c06356bSdh xd = (uint8_t *)(&msg[5]); 51784c06356bSdh xd += SAS_RSP_HDR_SIZE; 51794c06356bSdh if (rptr->datapres != SAS_RSP_DATAPRES_RESPONSE_DATA) { 5180c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 51814c06356bSdh "%s: TMF response not RESPONSE DATA (0x%x)", 51824c06356bSdh __func__, rptr->datapres); 51834c06356bSdh return (EIO); 51844c06356bSdh } 51854c06356bSdh if (rptr->response_data_length != 4) { 51864c06356bSdh pmcs_print_entry(pwp, PMCS_PRT_DEBUG, 51874c06356bSdh "Bad SAS RESPONSE DATA LENGTH", msg); 51884c06356bSdh return (EIO); 51894c06356bSdh } 51904c06356bSdh (void) memcpy(&status, xd, sizeof (uint32_t)); 51914c06356bSdh status = BE_32(status); 51924c06356bSdh if (response != NULL) 51934c06356bSdh *response = status; 51944c06356bSdh /* 51954c06356bSdh * The status is actually in the low-order byte. The upper three 51964c06356bSdh * bytes contain additional information for the TMFs that support them. 51974c06356bSdh * However, at this time we do not issue any of those. In the other 51984c06356bSdh * cases, the upper three bytes are supposed to be 0, but it appears 51994c06356bSdh * they aren't always. Just mask them off. 52004c06356bSdh */ 52014c06356bSdh switch (status & 0xff) { 52024c06356bSdh case SAS_RSP_TMF_COMPLETE: 5203c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 5204c3bc407cSdh "%s: TMF complete", __func__); 52054c06356bSdh result = 0; 52064c06356bSdh break; 52074c06356bSdh case SAS_RSP_TMF_SUCCEEDED: 5208c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 5209c3bc407cSdh "%s: TMF succeeded", __func__); 52104c06356bSdh result = 0; 52114c06356bSdh break; 52124c06356bSdh case SAS_RSP_INVALID_FRAME: 5213c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 52144c06356bSdh "%s: TMF returned INVALID FRAME", __func__); 52154c06356bSdh result = EIO; 52164c06356bSdh break; 52174c06356bSdh case SAS_RSP_TMF_NOT_SUPPORTED: 5218c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 52194c06356bSdh "%s: TMF returned TMF NOT SUPPORTED", __func__); 52204c06356bSdh result = EIO; 52214c06356bSdh break; 52224c06356bSdh case SAS_RSP_TMF_FAILED: 5223c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 52244c06356bSdh "%s: TMF returned TMF FAILED", __func__); 52254c06356bSdh result = EIO; 52264c06356bSdh break; 52274c06356bSdh case SAS_RSP_TMF_INCORRECT_LUN: 5228c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 52294c06356bSdh "%s: TMF returned INCORRECT LUN", __func__); 52304c06356bSdh result = EIO; 52314c06356bSdh break; 52324c06356bSdh case SAS_RSP_OVERLAPPED_OIPTTA: 5233c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 52344c06356bSdh "%s: TMF returned OVERLAPPED INITIATOR PORT TRANSFER TAG " 52354c06356bSdh "ATTEMPTED", __func__); 52364c06356bSdh result = EIO; 52374c06356bSdh break; 52384c06356bSdh default: 5239c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 52404c06356bSdh "%s: TMF returned unknown code 0x%x", __func__, status); 52414c06356bSdh result = EIO; 52424c06356bSdh break; 52434c06356bSdh } 52444c06356bSdh return (result); 52454c06356bSdh } 52464c06356bSdh 52474c06356bSdh /* 52484c06356bSdh * Called with PHY lock held and scratch acquired 52494c06356bSdh */ 52504c06356bSdh int 52514c06356bSdh pmcs_sata_abort_ncq(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 52524c06356bSdh { 52534c06356bSdh const char *utag_fail_fmt = "%s: untagged NCQ command failure"; 52544c06356bSdh const char *tag_fail_fmt = "%s: NCQ command failure (tag 0x%x)"; 52554c06356bSdh uint32_t msg[PMCS_QENTRY_SIZE], *ptr, result, status; 52564c06356bSdh uint8_t *fp = pwp->scratch, ds; 52574c06356bSdh fis_t fis; 52584c06356bSdh pmcwork_t *pwrk; 52594c06356bSdh pmcs_xscsi_t *tgt; 52604c06356bSdh 52614c06356bSdh pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 52624c06356bSdh if (pwrk == NULL) { 52634c06356bSdh return (ENOMEM); 52644c06356bSdh } 5265978d7443SSrikanth Suravajhala pwrk->htag |= PMCS_TAG_NONIO_CMD; 52664c06356bSdh msg[0] = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE, 52674c06356bSdh PMCIN_SATA_HOST_IO_START)); 52684c06356bSdh msg[1] = LE_32(pwrk->htag); 52694c06356bSdh msg[2] = LE_32(pptr->device_id); 52704c06356bSdh msg[3] = LE_32(512); 52714c06356bSdh msg[4] = LE_32(SATA_PROTOCOL_PIO | PMCIN_DATADIR_2_INI); 52724c06356bSdh msg[5] = LE_32((READ_LOG_EXT << 16) | (C_BIT << 8) | FIS_REG_H2DEV); 52734c06356bSdh msg[6] = LE_32(0x10); 52744c06356bSdh msg[8] = LE_32(1); 52754c06356bSdh msg[9] = 0; 52764c06356bSdh msg[10] = 0; 52774c06356bSdh msg[11] = 0; 52784c06356bSdh msg[12] = LE_32(DWORD0(pwp->scratch_dma)); 52794c06356bSdh msg[13] = LE_32(DWORD1(pwp->scratch_dma)); 52804c06356bSdh msg[14] = LE_32(512); 52814c06356bSdh msg[15] = 0; 52824c06356bSdh 52834c06356bSdh pwrk->arg = msg; 52844c06356bSdh pwrk->dtype = pptr->dtype; 5285*3be32c0fSJesse Butler pwrk->xp = pptr->target; 52864c06356bSdh 52874c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 52884c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 52894c06356bSdh if (ptr == NULL) { 52904c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 52914c06356bSdh pmcs_pwork(pwp, pwrk); 52924c06356bSdh return (ENOMEM); 52934c06356bSdh } 52944c06356bSdh COPY_MESSAGE(ptr, msg, PMCS_QENTRY_SIZE); 52954c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 52964c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 52974c06356bSdh 52984c06356bSdh pmcs_unlock_phy(pptr); 52994c06356bSdh WAIT_FOR(pwrk, 250, result); 53004c06356bSdh pmcs_pwork(pwp, pwrk); 5301*3be32c0fSJesse Butler pmcs_lock_phy(pptr); 53024c06356bSdh 5303c3bc407cSdh tgt = pptr->target; 53044c06356bSdh if (result) { 53056745c559SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, pmcs_timeo, __func__); 53064c06356bSdh return (EIO); 53074c06356bSdh } 53084c06356bSdh status = LE_32(msg[2]); 53094c06356bSdh if (status != PMCOUT_STATUS_OK || LE_32(msg[3])) { 53104c06356bSdh if (tgt == NULL) { 5311c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, 53124c06356bSdh "%s: cannot find target for phy 0x%p for " 53134c06356bSdh "dev state recovery", __func__, (void *)pptr); 53144c06356bSdh return (EIO); 53154c06356bSdh } 53164c06356bSdh 53174c06356bSdh mutex_enter(&tgt->statlock); 53184c06356bSdh 53194c06356bSdh pmcs_print_entry(pwp, PMCS_PRT_DEBUG, "READ LOG EXT", msg); 53204c06356bSdh if ((status == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) || 53214c06356bSdh (status == PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK) || 53224c06356bSdh (status == PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS)) { 53234c06356bSdh ds = PMCS_DEVICE_STATE_NON_OPERATIONAL; 53244c06356bSdh } else { 53254c06356bSdh ds = PMCS_DEVICE_STATE_IN_RECOVERY; 53264c06356bSdh } 53274c06356bSdh if (tgt->dev_state != ds) { 5328c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, "%s: Trying " 5329c3bc407cSdh "SATA DS Recovery for tgt(0x%p) for status(%s)", 53304c06356bSdh __func__, (void *)tgt, pmcs_status_str(status)); 5331145e0143Sdh (void) pmcs_send_err_recovery_cmd(pwp, ds, pptr, tgt); 53324c06356bSdh } 53334c06356bSdh 53344c06356bSdh mutex_exit(&tgt->statlock); 53354c06356bSdh return (EIO); 53364c06356bSdh } 53374c06356bSdh fis[0] = (fp[4] << 24) | (fp[3] << 16) | (fp[2] << 8) | FIS_REG_D2H; 53384c06356bSdh fis[1] = (fp[8] << 24) | (fp[7] << 16) | (fp[6] << 8) | fp[5]; 53394c06356bSdh fis[2] = (fp[12] << 24) | (fp[11] << 16) | (fp[10] << 8) | fp[9]; 53404c06356bSdh fis[3] = (fp[16] << 24) | (fp[15] << 16) | (fp[14] << 8) | fp[13]; 53414c06356bSdh fis[4] = 0; 53424c06356bSdh if (fp[0] & 0x80) { 5343c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, 5344c3bc407cSdh utag_fail_fmt, __func__); 53454c06356bSdh } else { 5346c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, 5347c3bc407cSdh tag_fail_fmt, __func__, fp[0] & 0x1f); 53484c06356bSdh } 53494c06356bSdh pmcs_fis_dump(pwp, fis); 53504c06356bSdh pptr->need_rl_ext = 0; 53514c06356bSdh return (0); 53524c06356bSdh } 53534c06356bSdh 53544c06356bSdh /* 53554c06356bSdh * Transform a structure from CPU to Device endian format, or 53564c06356bSdh * vice versa, based upon a transformation vector. 53574c06356bSdh * 53584c06356bSdh * A transformation vector is an array of bytes, each byte 53594c06356bSdh * of which is defined thusly: 53604c06356bSdh * 53614c06356bSdh * bit 7: from CPU to desired endian, otherwise from desired endian 53624c06356bSdh * to CPU format 53634c06356bSdh * bit 6: Big Endian, else Little Endian 53644c06356bSdh * bits 5-4: 53654c06356bSdh * 00 Undefined 53664c06356bSdh * 01 One Byte quantities 53674c06356bSdh * 02 Two Byte quantities 53684c06356bSdh * 03 Four Byte quantities 53694c06356bSdh * 53704c06356bSdh * bits 3-0: 53714c06356bSdh * 00 Undefined 53724c06356bSdh * Number of quantities to transform 53734c06356bSdh * 53744c06356bSdh * The vector is terminated by a 0 value. 53754c06356bSdh */ 53764c06356bSdh 53774c06356bSdh void 53784c06356bSdh pmcs_endian_transform(pmcs_hw_t *pwp, void *orig_out, void *orig_in, 53794c06356bSdh const uint8_t *xfvec) 53804c06356bSdh { 53814c06356bSdh uint8_t c, *out = orig_out, *in = orig_in; 53824c06356bSdh 53834c06356bSdh if (xfvec == NULL) { 5384c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 5385c3bc407cSdh "%s: null xfvec", __func__); 53864c06356bSdh return; 53874c06356bSdh } 53884c06356bSdh if (out == NULL) { 5389c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 5390c3bc407cSdh "%s: null out", __func__); 53914c06356bSdh return; 53924c06356bSdh } 53934c06356bSdh if (in == NULL) { 5394c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 5395c3bc407cSdh "%s: null in", __func__); 53964c06356bSdh return; 53974c06356bSdh } 53984c06356bSdh while ((c = *xfvec++) != 0) { 53994c06356bSdh int nbyt = (c & 0xf); 54004c06356bSdh int size = (c >> 4) & 0x3; 54014c06356bSdh int bige = (c >> 4) & 0x4; 54024c06356bSdh 54034c06356bSdh switch (size) { 54044c06356bSdh case 1: 54054c06356bSdh { 54064c06356bSdh while (nbyt-- > 0) { 54074c06356bSdh *out++ = *in++; 54084c06356bSdh } 54094c06356bSdh break; 54104c06356bSdh } 54114c06356bSdh case 2: 54124c06356bSdh { 54134c06356bSdh uint16_t tmp; 54144c06356bSdh while (nbyt-- > 0) { 54154c06356bSdh (void) memcpy(&tmp, in, sizeof (uint16_t)); 54164c06356bSdh if (bige) { 54174c06356bSdh tmp = BE_16(tmp); 54184c06356bSdh } else { 54194c06356bSdh tmp = LE_16(tmp); 54204c06356bSdh } 54214c06356bSdh (void) memcpy(out, &tmp, sizeof (uint16_t)); 54224c06356bSdh out += sizeof (uint16_t); 54234c06356bSdh in += sizeof (uint16_t); 54244c06356bSdh } 54254c06356bSdh break; 54264c06356bSdh } 54274c06356bSdh case 3: 54284c06356bSdh { 54294c06356bSdh uint32_t tmp; 54304c06356bSdh while (nbyt-- > 0) { 54314c06356bSdh (void) memcpy(&tmp, in, sizeof (uint32_t)); 54324c06356bSdh if (bige) { 54334c06356bSdh tmp = BE_32(tmp); 54344c06356bSdh } else { 54354c06356bSdh tmp = LE_32(tmp); 54364c06356bSdh } 54374c06356bSdh (void) memcpy(out, &tmp, sizeof (uint32_t)); 54384c06356bSdh out += sizeof (uint32_t); 54394c06356bSdh in += sizeof (uint32_t); 54404c06356bSdh } 54414c06356bSdh break; 54424c06356bSdh } 54434c06356bSdh default: 5444c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 5445c3bc407cSdh "%s: bad size", __func__); 54464c06356bSdh return; 54474c06356bSdh } 54484c06356bSdh } 54494c06356bSdh } 54504c06356bSdh 54514c06356bSdh const char * 54524c06356bSdh pmcs_get_rate(unsigned int linkrt) 54534c06356bSdh { 54544c06356bSdh const char *rate; 54554c06356bSdh switch (linkrt) { 54564c06356bSdh case SAS_LINK_RATE_1_5GBIT: 54574c06356bSdh rate = "1.5"; 54584c06356bSdh break; 54594c06356bSdh case SAS_LINK_RATE_3GBIT: 54604c06356bSdh rate = "3.0"; 54614c06356bSdh break; 54624c06356bSdh case SAS_LINK_RATE_6GBIT: 54634c06356bSdh rate = "6.0"; 54644c06356bSdh break; 54654c06356bSdh default: 54664c06356bSdh rate = "???"; 54674c06356bSdh break; 54684c06356bSdh } 54694c06356bSdh return (rate); 54704c06356bSdh } 54714c06356bSdh 54724c06356bSdh const char * 54734c06356bSdh pmcs_get_typename(pmcs_dtype_t type) 54744c06356bSdh { 54754c06356bSdh switch (type) { 54764c06356bSdh case NOTHING: 54774c06356bSdh return ("NIL"); 54784c06356bSdh case SATA: 54794c06356bSdh return ("SATA"); 54804c06356bSdh case SAS: 54814c06356bSdh return ("SSP"); 54824c06356bSdh case EXPANDER: 54834c06356bSdh return ("EXPANDER"); 54844c06356bSdh } 54854c06356bSdh return ("????"); 54864c06356bSdh } 54874c06356bSdh 54884c06356bSdh const char * 54894c06356bSdh pmcs_tmf2str(int tmf) 54904c06356bSdh { 54914c06356bSdh switch (tmf) { 54924c06356bSdh case SAS_ABORT_TASK: 54934c06356bSdh return ("Abort Task"); 54944c06356bSdh case SAS_ABORT_TASK_SET: 54954c06356bSdh return ("Abort Task Set"); 54964c06356bSdh case SAS_CLEAR_TASK_SET: 54974c06356bSdh return ("Clear Task Set"); 54984c06356bSdh case SAS_LOGICAL_UNIT_RESET: 54994c06356bSdh return ("Logical Unit Reset"); 55004c06356bSdh case SAS_I_T_NEXUS_RESET: 55014c06356bSdh return ("I_T Nexus Reset"); 55024c06356bSdh case SAS_CLEAR_ACA: 55034c06356bSdh return ("Clear ACA"); 55044c06356bSdh case SAS_QUERY_TASK: 55054c06356bSdh return ("Query Task"); 55064c06356bSdh case SAS_QUERY_TASK_SET: 55074c06356bSdh return ("Query Task Set"); 55084c06356bSdh case SAS_QUERY_UNIT_ATTENTION: 55094c06356bSdh return ("Query Unit Attention"); 55104c06356bSdh default: 55114c06356bSdh return ("Unknown"); 55124c06356bSdh } 55134c06356bSdh } 55144c06356bSdh 55154c06356bSdh const char * 55164c06356bSdh pmcs_status_str(uint32_t status) 55174c06356bSdh { 55184c06356bSdh switch (status) { 55194c06356bSdh case PMCOUT_STATUS_OK: 55204c06356bSdh return ("OK"); 55214c06356bSdh case PMCOUT_STATUS_ABORTED: 55224c06356bSdh return ("ABORTED"); 55234c06356bSdh case PMCOUT_STATUS_OVERFLOW: 55244c06356bSdh return ("OVERFLOW"); 55254c06356bSdh case PMCOUT_STATUS_UNDERFLOW: 55264c06356bSdh return ("UNDERFLOW"); 55274c06356bSdh case PMCOUT_STATUS_FAILED: 55284c06356bSdh return ("FAILED"); 55294c06356bSdh case PMCOUT_STATUS_ABORT_RESET: 55304c06356bSdh return ("ABORT_RESET"); 55314c06356bSdh case PMCOUT_STATUS_IO_NOT_VALID: 55324c06356bSdh return ("IO_NOT_VALID"); 55334c06356bSdh case PMCOUT_STATUS_NO_DEVICE: 55344c06356bSdh return ("NO_DEVICE"); 55354c06356bSdh case PMCOUT_STATUS_ILLEGAL_PARAMETER: 55364c06356bSdh return ("ILLEGAL_PARAMETER"); 55374c06356bSdh case PMCOUT_STATUS_LINK_FAILURE: 55384c06356bSdh return ("LINK_FAILURE"); 55394c06356bSdh case PMCOUT_STATUS_PROG_ERROR: 55404c06356bSdh return ("PROG_ERROR"); 55414c06356bSdh case PMCOUT_STATUS_EDC_IN_ERROR: 55424c06356bSdh return ("EDC_IN_ERROR"); 55434c06356bSdh case PMCOUT_STATUS_EDC_OUT_ERROR: 55444c06356bSdh return ("EDC_OUT_ERROR"); 55454c06356bSdh case PMCOUT_STATUS_ERROR_HW_TIMEOUT: 55464c06356bSdh return ("ERROR_HW_TIMEOUT"); 55474c06356bSdh case PMCOUT_STATUS_XFER_ERR_BREAK: 55484c06356bSdh return ("XFER_ERR_BREAK"); 55494c06356bSdh case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY: 55504c06356bSdh return ("XFER_ERR_PHY_NOT_READY"); 55514c06356bSdh case PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED: 55524c06356bSdh return ("OPEN_CNX_PROTOCOL_NOT_SUPPORTED"); 55534c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION: 55544c06356bSdh return ("OPEN_CNX_ERROR_ZONE_VIOLATION"); 55554c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK: 55564c06356bSdh return ("OPEN_CNX_ERROR_BREAK"); 55574c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS: 55584c06356bSdh return ("OPEN_CNX_ERROR_IT_NEXUS_LOSS"); 55594c06356bSdh case PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION: 55604c06356bSdh return ("OPENCNX_ERROR_BAD_DESTINATION"); 55614c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: 55624c06356bSdh return ("OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED"); 55634c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY: 55644c06356bSdh return ("OPEN_CNX_ERROR_STP_RESOURCES_BUSY"); 55654c06356bSdh case PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION: 55664c06356bSdh return ("OPEN_CNX_ERROR_WRONG_DESTINATION"); 55679aed1621SDavid Hollister case PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_ERROR: 55689aed1621SDavid Hollister return ("OPEN_CNX_ERROR_UNKNOWN_ERROR"); 55694c06356bSdh case PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED: 55704c06356bSdh return ("IO_XFER_ERROR_NAK_RECEIVED"); 55714c06356bSdh case PMCOUT_STATUS_XFER_ERROR_ACK_NAK_TIMEOUT: 55724c06356bSdh return ("XFER_ERROR_ACK_NAK_TIMEOUT"); 55734c06356bSdh case PMCOUT_STATUS_XFER_ERROR_PEER_ABORTED: 55744c06356bSdh return ("XFER_ERROR_PEER_ABORTED"); 55754c06356bSdh case PMCOUT_STATUS_XFER_ERROR_RX_FRAME: 55764c06356bSdh return ("XFER_ERROR_RX_FRAME"); 55774c06356bSdh case PMCOUT_STATUS_IO_XFER_ERROR_DMA: 55784c06356bSdh return ("IO_XFER_ERROR_DMA"); 55794c06356bSdh case PMCOUT_STATUS_XFER_ERROR_CREDIT_TIMEOUT: 55804c06356bSdh return ("XFER_ERROR_CREDIT_TIMEOUT"); 55814c06356bSdh case PMCOUT_STATUS_XFER_ERROR_SATA_LINK_TIMEOUT: 55824c06356bSdh return ("XFER_ERROR_SATA_LINK_TIMEOUT"); 55834c06356bSdh case PMCOUT_STATUS_XFER_ERROR_SATA: 55844c06356bSdh return ("XFER_ERROR_SATA"); 55854c06356bSdh case PMCOUT_STATUS_XFER_ERROR_REJECTED_NCQ_MODE: 55864c06356bSdh return ("XFER_ERROR_REJECTED_NCQ_MODE"); 55874c06356bSdh case PMCOUT_STATUS_XFER_ERROR_ABORTED_DUE_TO_SRST: 55884c06356bSdh return ("XFER_ERROR_ABORTED_DUE_TO_SRST"); 55894c06356bSdh case PMCOUT_STATUS_XFER_ERROR_ABORTED_NCQ_MODE: 55904c06356bSdh return ("XFER_ERROR_ABORTED_NCQ_MODE"); 55914c06356bSdh case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT: 55924c06356bSdh return ("IO_XFER_OPEN_RETRY_TIMEOUT"); 55934c06356bSdh case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR: 55944c06356bSdh return ("SMP_RESP_CONNECTION_ERROR"); 55954c06356bSdh case PMCOUT_STATUS_XFER_ERROR_UNEXPECTED_PHASE: 55964c06356bSdh return ("XFER_ERROR_UNEXPECTED_PHASE"); 55974c06356bSdh case PMCOUT_STATUS_XFER_ERROR_RDY_OVERRUN: 55984c06356bSdh return ("XFER_ERROR_RDY_OVERRUN"); 55994c06356bSdh case PMCOUT_STATUS_XFER_ERROR_RDY_NOT_EXPECTED: 56004c06356bSdh return ("XFER_ERROR_RDY_NOT_EXPECTED"); 56014c06356bSdh case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT: 56024c06356bSdh return ("XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT"); 56034c06356bSdh case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK: 56044c06356bSdh return ("XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK"); 56054c06356bSdh case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK: 56064c06356bSdh return ("XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK"); 56074c06356bSdh case PMCOUT_STATUS_XFER_ERROR_OFFSET_MISMATCH: 56084c06356bSdh return ("XFER_ERROR_OFFSET_MISMATCH"); 56094c06356bSdh case PMCOUT_STATUS_XFER_ERROR_ZERO_DATA_LEN: 56104c06356bSdh return ("XFER_ERROR_ZERO_DATA_LEN"); 56114c06356bSdh case PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED: 56124c06356bSdh return ("XFER_CMD_FRAME_ISSUED"); 56134c06356bSdh case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE: 56144c06356bSdh return ("ERROR_INTERNAL_SMP_RESOURCE"); 56154c06356bSdh case PMCOUT_STATUS_IO_PORT_IN_RESET: 56164c06356bSdh return ("IO_PORT_IN_RESET"); 56174c06356bSdh case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL: 56184c06356bSdh return ("DEVICE STATE NON-OPERATIONAL"); 56194c06356bSdh case PMCOUT_STATUS_IO_DS_IN_RECOVERY: 56204c06356bSdh return ("DEVICE STATE IN RECOVERY"); 5621a25672a1SDavid Hollister case PMCOUT_STATUS_IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY: 5622a25672a1SDavid Hollister return ("OPEN CNX ERR HW RESOURCE BUSY"); 56234c06356bSdh default: 56244c06356bSdh return (NULL); 56254c06356bSdh } 56264c06356bSdh } 56274c06356bSdh 56284c06356bSdh uint64_t 56294c06356bSdh pmcs_barray2wwn(uint8_t ba[8]) 56304c06356bSdh { 56314c06356bSdh uint64_t result = 0; 56324c06356bSdh int i; 56334c06356bSdh 56344c06356bSdh for (i = 0; i < 8; i++) { 56354c06356bSdh result <<= 8; 56364c06356bSdh result |= ba[i]; 56374c06356bSdh } 56384c06356bSdh return (result); 56394c06356bSdh } 56404c06356bSdh 56414c06356bSdh void 56424c06356bSdh pmcs_wwn2barray(uint64_t wwn, uint8_t ba[8]) 56434c06356bSdh { 56444c06356bSdh int i; 56454c06356bSdh for (i = 0; i < 8; i++) { 56464c06356bSdh ba[7 - i] = wwn & 0xff; 56474c06356bSdh wwn >>= 8; 56484c06356bSdh } 56494c06356bSdh } 56504c06356bSdh 56514c06356bSdh void 56524c06356bSdh pmcs_report_fwversion(pmcs_hw_t *pwp) 56534c06356bSdh { 56544c06356bSdh const char *fwsupport; 56554c06356bSdh switch (PMCS_FW_TYPE(pwp)) { 56564c06356bSdh case PMCS_FW_TYPE_RELEASED: 56574c06356bSdh fwsupport = "Released"; 56584c06356bSdh break; 56594c06356bSdh case PMCS_FW_TYPE_DEVELOPMENT: 56604c06356bSdh fwsupport = "Development"; 56614c06356bSdh break; 56624c06356bSdh case PMCS_FW_TYPE_ALPHA: 56634c06356bSdh fwsupport = "Alpha"; 56644c06356bSdh break; 56654c06356bSdh case PMCS_FW_TYPE_BETA: 56664c06356bSdh fwsupport = "Beta"; 56674c06356bSdh break; 56684c06356bSdh default: 56694c06356bSdh fwsupport = "Special"; 56704c06356bSdh break; 56714c06356bSdh } 5672c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, 56732ac4abe8SDavid Hollister "Chip Revision: %c; F/W Revision %x.%x.%x %s (ILA rev %08x)", 56742ac4abe8SDavid Hollister 'A' + pwp->chiprev, PMCS_FW_MAJOR(pwp), PMCS_FW_MINOR(pwp), 56752ac4abe8SDavid Hollister PMCS_FW_MICRO(pwp), fwsupport, pwp->ila_ver); 56764c06356bSdh } 56774c06356bSdh 56784c06356bSdh void 56794c06356bSdh pmcs_phy_name(pmcs_hw_t *pwp, pmcs_phy_t *pptr, char *obuf, size_t olen) 56804c06356bSdh { 56814c06356bSdh if (pptr->parent) { 56824c06356bSdh pmcs_phy_name(pwp, pptr->parent, obuf, olen); 56834c06356bSdh (void) snprintf(obuf, olen, "%s.%02x", obuf, pptr->phynum); 56844c06356bSdh } else { 56854c06356bSdh (void) snprintf(obuf, olen, "pp%02x", pptr->phynum); 56864c06356bSdh } 56874c06356bSdh } 56884c06356bSdh 56894c06356bSdh /* 56904c06356bSdh * This function is called as a sanity check to ensure that a newly registered 56914c06356bSdh * PHY doesn't have a device_id that exists with another registered PHY. 56924c06356bSdh */ 56934c06356bSdh static boolean_t 56944c06356bSdh pmcs_validate_devid(pmcs_phy_t *parent, pmcs_phy_t *phyp, uint32_t device_id) 56954c06356bSdh { 56969aed1621SDavid Hollister pmcs_phy_t *pptr, *pchild; 56974c06356bSdh boolean_t rval; 56984c06356bSdh 56994c06356bSdh pptr = parent; 57004c06356bSdh 57014c06356bSdh while (pptr) { 57024c06356bSdh if (pptr->valid_device_id && (pptr != phyp) && 57034c06356bSdh (pptr->device_id == device_id)) { 57049aed1621SDavid Hollister /* 57059aed1621SDavid Hollister * This can still be OK if both of these PHYs actually 57069aed1621SDavid Hollister * represent the same device (e.g. expander). It could 57079aed1621SDavid Hollister * be a case of a new "primary" PHY. If the SAS address 57089aed1621SDavid Hollister * is the same and they have the same parent, we'll 57099aed1621SDavid Hollister * accept this if the PHY to be registered is the 57109aed1621SDavid Hollister * primary. 57119aed1621SDavid Hollister */ 57129aed1621SDavid Hollister if ((phyp->parent == pptr->parent) && 57139aed1621SDavid Hollister (memcmp(phyp->sas_address, 57149aed1621SDavid Hollister pptr->sas_address, 8) == 0) && (phyp->width > 1)) { 57159aed1621SDavid Hollister /* 57169aed1621SDavid Hollister * Move children over to the new primary and 57179aed1621SDavid Hollister * update both PHYs 57189aed1621SDavid Hollister */ 57199aed1621SDavid Hollister pmcs_lock_phy(pptr); 57209aed1621SDavid Hollister phyp->children = pptr->children; 57219aed1621SDavid Hollister pchild = phyp->children; 57229aed1621SDavid Hollister while (pchild) { 57239aed1621SDavid Hollister pchild->parent = phyp; 57249aed1621SDavid Hollister pchild = pchild->sibling; 57259aed1621SDavid Hollister } 57269aed1621SDavid Hollister phyp->subsidiary = 0; 57279aed1621SDavid Hollister phyp->ncphy = pptr->ncphy; 57289aed1621SDavid Hollister /* 57299aed1621SDavid Hollister * device_id, valid_device_id, and configured 57309aed1621SDavid Hollister * will be set by the caller 57319aed1621SDavid Hollister */ 57329aed1621SDavid Hollister pptr->children = NULL; 57339aed1621SDavid Hollister pptr->subsidiary = 1; 57349aed1621SDavid Hollister pptr->ncphy = 0; 57359aed1621SDavid Hollister pmcs_unlock_phy(pptr); 57369aed1621SDavid Hollister pmcs_prt(pptr->pwp, PMCS_PRT_DEBUG, pptr, NULL, 57379aed1621SDavid Hollister "%s: Moving device_id %d from PHY %s to %s", 57389aed1621SDavid Hollister __func__, device_id, pptr->path, 57399aed1621SDavid Hollister phyp->path); 57409aed1621SDavid Hollister return (B_TRUE); 57419aed1621SDavid Hollister } 5742c3bc407cSdh pmcs_prt(pptr->pwp, PMCS_PRT_DEBUG, pptr, NULL, 57434c06356bSdh "%s: phy %s already exists as %s with " 57444c06356bSdh "device id 0x%x", __func__, phyp->path, 57454c06356bSdh pptr->path, device_id); 57464c06356bSdh return (B_FALSE); 57474c06356bSdh } 57484c06356bSdh 57494c06356bSdh if (pptr->children) { 57504c06356bSdh rval = pmcs_validate_devid(pptr->children, phyp, 57514c06356bSdh device_id); 57524c06356bSdh if (rval == B_FALSE) { 57534c06356bSdh return (rval); 57544c06356bSdh } 57554c06356bSdh } 57564c06356bSdh 57574c06356bSdh pptr = pptr->sibling; 57584c06356bSdh } 57594c06356bSdh 57604c06356bSdh /* This PHY and device_id are valid */ 57614c06356bSdh return (B_TRUE); 57624c06356bSdh } 57634c06356bSdh 57644c06356bSdh /* 57654c06356bSdh * If the PHY is found, it is returned locked 57664c06356bSdh */ 57674c06356bSdh static pmcs_phy_t * 57684c06356bSdh pmcs_find_phy_by_wwn_impl(pmcs_phy_t *phyp, uint8_t *wwn) 57694c06356bSdh { 57704c06356bSdh pmcs_phy_t *matched_phy, *cphyp, *nphyp; 57714c06356bSdh 57724c06356bSdh ASSERT(!mutex_owned(&phyp->phy_lock)); 57734c06356bSdh 57744c06356bSdh while (phyp) { 57754c06356bSdh pmcs_lock_phy(phyp); 57764c06356bSdh 57774c06356bSdh if (phyp->valid_device_id) { 57784c06356bSdh if (memcmp(phyp->sas_address, wwn, 8) == 0) { 57794c06356bSdh return (phyp); 57804c06356bSdh } 57814c06356bSdh } 57824c06356bSdh 57834c06356bSdh if (phyp->children) { 57844c06356bSdh cphyp = phyp->children; 57854c06356bSdh pmcs_unlock_phy(phyp); 57864c06356bSdh matched_phy = pmcs_find_phy_by_wwn_impl(cphyp, wwn); 57874c06356bSdh if (matched_phy) { 57884c06356bSdh ASSERT(mutex_owned(&matched_phy->phy_lock)); 57894c06356bSdh return (matched_phy); 57904c06356bSdh } 57914c06356bSdh pmcs_lock_phy(phyp); 57924c06356bSdh } 57934c06356bSdh 57944c06356bSdh /* 57954c06356bSdh * Only iterate through non-root PHYs 57964c06356bSdh */ 57974c06356bSdh if (IS_ROOT_PHY(phyp)) { 57984c06356bSdh pmcs_unlock_phy(phyp); 57994c06356bSdh phyp = NULL; 58004c06356bSdh } else { 58014c06356bSdh nphyp = phyp->sibling; 58024c06356bSdh pmcs_unlock_phy(phyp); 58034c06356bSdh phyp = nphyp; 58044c06356bSdh } 58054c06356bSdh } 58064c06356bSdh 58074c06356bSdh return (NULL); 58084c06356bSdh } 58094c06356bSdh 58104c06356bSdh pmcs_phy_t * 58114c06356bSdh pmcs_find_phy_by_wwn(pmcs_hw_t *pwp, uint64_t wwn) 58124c06356bSdh { 58134c06356bSdh uint8_t ebstr[8]; 58144c06356bSdh pmcs_phy_t *pptr, *matched_phy; 58154c06356bSdh 58164c06356bSdh pmcs_wwn2barray(wwn, ebstr); 58174c06356bSdh 58184c06356bSdh pptr = pwp->root_phys; 58194c06356bSdh while (pptr) { 58204c06356bSdh matched_phy = pmcs_find_phy_by_wwn_impl(pptr, ebstr); 58214c06356bSdh if (matched_phy) { 58224c06356bSdh ASSERT(mutex_owned(&matched_phy->phy_lock)); 58234c06356bSdh return (matched_phy); 58244c06356bSdh } 58254c06356bSdh 58264c06356bSdh pptr = pptr->sibling; 58274c06356bSdh } 58284c06356bSdh 58294c06356bSdh return (NULL); 58304c06356bSdh } 58314c06356bSdh 58324c06356bSdh 58334c06356bSdh /* 58344c06356bSdh * pmcs_find_phy_by_sas_address 58354c06356bSdh * 58364c06356bSdh * Find a PHY that both matches "sas_addr" and is on "iport". 58374c06356bSdh * If a matching PHY is found, it is returned locked. 58384c06356bSdh */ 58394c06356bSdh pmcs_phy_t * 58404c06356bSdh pmcs_find_phy_by_sas_address(pmcs_hw_t *pwp, pmcs_iport_t *iport, 58414c06356bSdh pmcs_phy_t *root, char *sas_addr) 58424c06356bSdh { 58434c06356bSdh int ua_form = 1; 58444c06356bSdh uint64_t wwn; 58454c06356bSdh char addr[PMCS_MAX_UA_SIZE]; 58464c06356bSdh pmcs_phy_t *pptr, *pnext, *pchild; 58474c06356bSdh 58484c06356bSdh if (root == NULL) { 58494c06356bSdh pptr = pwp->root_phys; 58504c06356bSdh } else { 58514c06356bSdh pptr = root; 58524c06356bSdh } 58534c06356bSdh 58544c06356bSdh while (pptr) { 58554c06356bSdh pmcs_lock_phy(pptr); 58564c06356bSdh /* 58574c06356bSdh * If the PHY is dead or does not have a valid device ID, 58584c06356bSdh * skip it. 58594c06356bSdh */ 58604c06356bSdh if ((pptr->dead) || (!pptr->valid_device_id)) { 58614c06356bSdh goto next_phy; 58624c06356bSdh } 58634c06356bSdh 58644c06356bSdh if (pptr->iport != iport) { 58654c06356bSdh goto next_phy; 58664c06356bSdh } 58674c06356bSdh 58684c06356bSdh wwn = pmcs_barray2wwn(pptr->sas_address); 58694c06356bSdh (void *) scsi_wwn_to_wwnstr(wwn, ua_form, addr); 58704c06356bSdh if (strncmp(addr, sas_addr, strlen(addr)) == 0) { 58714c06356bSdh return (pptr); 58724c06356bSdh } 58734c06356bSdh 58744c06356bSdh if (pptr->children) { 58754c06356bSdh pchild = pptr->children; 58764c06356bSdh pmcs_unlock_phy(pptr); 58774c06356bSdh pnext = pmcs_find_phy_by_sas_address(pwp, iport, pchild, 58784c06356bSdh sas_addr); 58794c06356bSdh if (pnext) { 58804c06356bSdh return (pnext); 58814c06356bSdh } 58824c06356bSdh pmcs_lock_phy(pptr); 58834c06356bSdh } 58844c06356bSdh 58854c06356bSdh next_phy: 58864c06356bSdh pnext = pptr->sibling; 58874c06356bSdh pmcs_unlock_phy(pptr); 58884c06356bSdh pptr = pnext; 58894c06356bSdh } 58904c06356bSdh 58914c06356bSdh return (NULL); 58924c06356bSdh } 58934c06356bSdh 58944c06356bSdh void 58954c06356bSdh pmcs_fis_dump(pmcs_hw_t *pwp, fis_t fis) 58964c06356bSdh { 58974c06356bSdh switch (fis[0] & 0xff) { 58984c06356bSdh case FIS_REG_H2DEV: 5899c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, 5900c3bc407cSdh "FIS REGISTER HOST TO DEVICE: " 59014c06356bSdh "OP=0x%02x Feature=0x%04x Count=0x%04x Device=0x%02x " 59024c06356bSdh "LBA=%llu", BYTE2(fis[0]), BYTE3(fis[2]) << 8 | 59034c06356bSdh BYTE3(fis[0]), WORD0(fis[3]), BYTE3(fis[1]), 59044c06356bSdh (unsigned long long) 59054c06356bSdh (((uint64_t)fis[2] & 0x00ffffff) << 24 | 59064c06356bSdh ((uint64_t)fis[1] & 0x00ffffff))); 59074c06356bSdh break; 59084c06356bSdh case FIS_REG_D2H: 5909c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, 5910c3bc407cSdh "FIS REGISTER DEVICE TO HOST: Status=0x%02x " 5911c3bc407cSdh "Error=0x%02x Dev=0x%02x Count=0x%04x LBA=%llu", 59124c06356bSdh BYTE2(fis[0]), BYTE3(fis[0]), BYTE3(fis[1]), WORD0(fis[3]), 59134c06356bSdh (unsigned long long)(((uint64_t)fis[2] & 0x00ffffff) << 24 | 59144c06356bSdh ((uint64_t)fis[1] & 0x00ffffff))); 59154c06356bSdh break; 59164c06356bSdh default: 5917c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, 5918a25672a1SDavid Hollister "FIS: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x", 5919a25672a1SDavid Hollister fis[0], fis[1], fis[2], fis[3], fis[4]); 59204c06356bSdh break; 59214c06356bSdh } 59224c06356bSdh } 59234c06356bSdh 59244c06356bSdh void 59254c06356bSdh pmcs_print_entry(pmcs_hw_t *pwp, int level, char *msg, void *arg) 59264c06356bSdh { 59274c06356bSdh uint32_t *mb = arg; 59284c06356bSdh size_t i; 59294c06356bSdh 5930c3bc407cSdh pmcs_prt(pwp, level, NULL, NULL, msg); 59314c06356bSdh for (i = 0; i < (PMCS_QENTRY_SIZE / sizeof (uint32_t)); i += 4) { 5932c3bc407cSdh pmcs_prt(pwp, level, NULL, NULL, 5933c3bc407cSdh "Offset %2lu: 0x%08x 0x%08x 0x%08x 0x%08x", 5934c3bc407cSdh i * sizeof (uint32_t), LE_32(mb[i]), 5935c3bc407cSdh LE_32(mb[i+1]), LE_32(mb[i+2]), LE_32(mb[i+3])); 59364c06356bSdh } 59374c06356bSdh } 59384c06356bSdh 59394c06356bSdh /* 59404c06356bSdh * If phyp == NULL we're being called from the worker thread, in which 59414c06356bSdh * case we need to check all the PHYs. In this case, the softstate lock 59424c06356bSdh * will be held. 59434c06356bSdh * If phyp is non-NULL, just issue the spinup release for the specified PHY 59444c06356bSdh * (which will already be locked). 59454c06356bSdh */ 59464c06356bSdh void 59474c06356bSdh pmcs_spinup_release(pmcs_hw_t *pwp, pmcs_phy_t *phyp) 59484c06356bSdh { 59494c06356bSdh uint32_t *msg; 59504c06356bSdh struct pmcwork *pwrk; 59514c06356bSdh pmcs_phy_t *tphyp; 59524c06356bSdh 59534c06356bSdh if (phyp != NULL) { 59544c06356bSdh ASSERT(mutex_owned(&phyp->phy_lock)); 5955c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, NULL, 59564c06356bSdh "%s: Issuing spinup release only for PHY %s", __func__, 59574c06356bSdh phyp->path); 59584c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 59594c06356bSdh msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 59604c06356bSdh if (msg == NULL || (pwrk = 59614c06356bSdh pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) { 59624c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 59634c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_SPINUP_RELEASE); 59644c06356bSdh return; 59654c06356bSdh } 59664c06356bSdh 59674c06356bSdh phyp->spinup_hold = 0; 59684c06356bSdh bzero(msg, PMCS_QENTRY_SIZE); 5969978d7443SSrikanth Suravajhala pwrk->htag |= PMCS_TAG_NONIO_CMD; 59704c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 59714c06356bSdh PMCIN_LOCAL_PHY_CONTROL)); 59724c06356bSdh msg[1] = LE_32(pwrk->htag); 59734c06356bSdh msg[2] = LE_32((0x10 << 8) | phyp->phynum); 59744c06356bSdh 59754c06356bSdh pwrk->dtype = phyp->dtype; 59764c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 5977*3be32c0fSJesse Butler pwrk->xp = phyp->target; 59784c06356bSdh mutex_exit(&pwrk->lock); 59794c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 59804c06356bSdh return; 59814c06356bSdh } 59824c06356bSdh 59834c06356bSdh ASSERT(mutex_owned(&pwp->lock)); 59844c06356bSdh 59854c06356bSdh tphyp = pwp->root_phys; 59864c06356bSdh while (tphyp) { 59874c06356bSdh pmcs_lock_phy(tphyp); 59884c06356bSdh if (tphyp->spinup_hold == 0) { 59894c06356bSdh pmcs_unlock_phy(tphyp); 59904c06356bSdh tphyp = tphyp->sibling; 59914c06356bSdh continue; 59924c06356bSdh } 59934c06356bSdh 5994*3be32c0fSJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, tphyp, NULL, 59954c06356bSdh "%s: Issuing spinup release for PHY %s", __func__, 5996*3be32c0fSJesse Butler tphyp->path); 59974c06356bSdh 59984c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 59994c06356bSdh msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 60004c06356bSdh if (msg == NULL || (pwrk = 60014c06356bSdh pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) { 60024c06356bSdh pmcs_unlock_phy(tphyp); 60034c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 60044c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_SPINUP_RELEASE); 60054c06356bSdh break; 60064c06356bSdh } 60074c06356bSdh 60084c06356bSdh tphyp->spinup_hold = 0; 60094c06356bSdh bzero(msg, PMCS_QENTRY_SIZE); 60104c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 60114c06356bSdh PMCIN_LOCAL_PHY_CONTROL)); 60124c06356bSdh msg[1] = LE_32(pwrk->htag); 60134c06356bSdh msg[2] = LE_32((0x10 << 8) | tphyp->phynum); 60144c06356bSdh 6015*3be32c0fSJesse Butler pwrk->dtype = tphyp->dtype; 60164c06356bSdh pwrk->state = PMCS_WORK_STATE_ONCHIP; 6017*3be32c0fSJesse Butler pwrk->xp = tphyp->target; 60184c06356bSdh mutex_exit(&pwrk->lock); 60194c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 60204c06356bSdh pmcs_unlock_phy(tphyp); 60214c06356bSdh 60224c06356bSdh tphyp = tphyp->sibling; 60234c06356bSdh } 60244c06356bSdh } 60254c06356bSdh 60264c06356bSdh /* 60274c06356bSdh * Abort commands on dead PHYs and deregister them as well as removing 60284c06356bSdh * the associated targets. 60294c06356bSdh */ 60304c06356bSdh static int 60314c06356bSdh pmcs_kill_devices(pmcs_hw_t *pwp, pmcs_phy_t *phyp) 60324c06356bSdh { 60334c06356bSdh pmcs_phy_t *pnext, *pchild; 60344c06356bSdh boolean_t remove_device; 60354c06356bSdh int rval = 0; 60364c06356bSdh 60374c06356bSdh while (phyp) { 60384c06356bSdh pmcs_lock_phy(phyp); 60394c06356bSdh pchild = phyp->children; 60404c06356bSdh pnext = phyp->sibling; 60414c06356bSdh pmcs_unlock_phy(phyp); 60424c06356bSdh 60434c06356bSdh if (pchild) { 60444c06356bSdh rval = pmcs_kill_devices(pwp, pchild); 60454c06356bSdh if (rval) { 60464c06356bSdh return (rval); 60474c06356bSdh } 60484c06356bSdh } 60494c06356bSdh 60504c06356bSdh mutex_enter(&pwp->lock); 60514c06356bSdh pmcs_lock_phy(phyp); 60524c06356bSdh if (phyp->dead && phyp->valid_device_id) { 60534c06356bSdh remove_device = B_TRUE; 60544c06356bSdh } else { 60554c06356bSdh remove_device = B_FALSE; 60564c06356bSdh } 60574c06356bSdh 60584c06356bSdh if (remove_device) { 60594c06356bSdh pmcs_remove_device(pwp, phyp); 60604c06356bSdh mutex_exit(&pwp->lock); 60614c06356bSdh 60624c06356bSdh rval = pmcs_kill_device(pwp, phyp); 60634c06356bSdh if (rval) { 60644c06356bSdh pmcs_unlock_phy(phyp); 60654c06356bSdh return (rval); 60664c06356bSdh } 60674c06356bSdh } else { 60684c06356bSdh mutex_exit(&pwp->lock); 60694c06356bSdh } 60704c06356bSdh 60714c06356bSdh pmcs_unlock_phy(phyp); 60724c06356bSdh phyp = pnext; 60734c06356bSdh } 60744c06356bSdh 60754c06356bSdh return (rval); 60764c06356bSdh } 60774c06356bSdh 60784c06356bSdh /* 60794c06356bSdh * Called with PHY locked 60804c06356bSdh */ 60814c06356bSdh int 60824c06356bSdh pmcs_kill_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 60834c06356bSdh { 6084*3be32c0fSJesse Butler int rval; 60854c06356bSdh 6086c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, "kill %s device @ %s", 60874c06356bSdh pmcs_get_typename(pptr->dtype), pptr->path); 60884c06356bSdh 60894c06356bSdh /* 60904c06356bSdh * There may be an outstanding ABORT_ALL running, which we wouldn't 60914c06356bSdh * know just by checking abort_pending. We can, however, check 60924c06356bSdh * abort_all_start. If it's non-zero, there is one, and we'll just 60934c06356bSdh * sit here and wait for it to complete. If we don't, we'll remove 60944c06356bSdh * the device while there are still commands pending. 60954c06356bSdh */ 60964c06356bSdh if (pptr->abort_all_start) { 60974c06356bSdh while (pptr->abort_all_start) { 6098c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 60994c06356bSdh "%s: Waiting for outstanding ABORT_ALL on PHY 0x%p", 61004c06356bSdh __func__, (void *)pptr); 61014c06356bSdh cv_wait(&pptr->abort_all_cv, &pptr->phy_lock); 61024c06356bSdh } 61034c06356bSdh } else if (pptr->abort_pending) { 6104*3be32c0fSJesse Butler rval = pmcs_abort(pwp, pptr, pptr->device_id, 1, 1); 6105*3be32c0fSJesse Butler if (rval) { 6106c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 61074c06356bSdh "%s: ABORT_ALL returned non-zero status (%d) for " 6108*3be32c0fSJesse Butler "PHY 0x%p", __func__, rval, (void *)pptr); 6109*3be32c0fSJesse Butler return (rval); 61104c06356bSdh } 61114c06356bSdh pptr->abort_pending = 0; 61124c06356bSdh } 61134c06356bSdh 6114*3be32c0fSJesse Butler if (pptr->valid_device_id) { 6115*3be32c0fSJesse Butler pmcs_deregister_device(pwp, pptr); 61164c06356bSdh } 61174c06356bSdh 61184c06356bSdh PHY_CHANGED(pwp, pptr); 61194c06356bSdh RESTART_DISCOVERY(pwp); 61204c06356bSdh pptr->valid_device_id = 0; 61214c06356bSdh return (0); 61224c06356bSdh } 61234c06356bSdh 61244c06356bSdh /* 61254c06356bSdh * Acknowledge the SAS h/w events that need acknowledgement. 61264c06356bSdh * This is only needed for first level PHYs. 61274c06356bSdh */ 61284c06356bSdh void 61294c06356bSdh pmcs_ack_events(pmcs_hw_t *pwp) 61304c06356bSdh { 61314c06356bSdh uint32_t msg[PMCS_MSG_SIZE], *ptr; 61324c06356bSdh struct pmcwork *pwrk; 61334c06356bSdh pmcs_phy_t *pptr; 61344c06356bSdh 61354c06356bSdh for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) { 61364c06356bSdh pmcs_lock_phy(pptr); 61374c06356bSdh if (pptr->hw_event_ack == 0) { 61384c06356bSdh pmcs_unlock_phy(pptr); 61394c06356bSdh continue; 61404c06356bSdh } 61414c06356bSdh mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 61424c06356bSdh ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 61434c06356bSdh 61444c06356bSdh if ((ptr == NULL) || (pwrk = 61454c06356bSdh pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) { 61464c06356bSdh mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 61474c06356bSdh pmcs_unlock_phy(pptr); 61484c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_SAS_HW_ACK); 61494c06356bSdh break; 61504c06356bSdh } 61514c06356bSdh 61524c06356bSdh msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, 6153978d7443SSrikanth Suravajhala PMCIN_SAS_HW_EVENT_ACK)); 61544c06356bSdh msg[1] = LE_32(pwrk->htag); 61554c06356bSdh msg[2] = LE_32(pptr->hw_event_ack); 61564c06356bSdh 61574c06356bSdh mutex_exit(&pwrk->lock); 61584c06356bSdh pwrk->dtype = pptr->dtype; 61594c06356bSdh pptr->hw_event_ack = 0; 61604c06356bSdh COPY_MESSAGE(ptr, msg, 3); 61614c06356bSdh INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 61624c06356bSdh pmcs_unlock_phy(pptr); 61634c06356bSdh } 61644c06356bSdh } 61654c06356bSdh 61664c06356bSdh /* 61674c06356bSdh * Load DMA 61684c06356bSdh */ 61694c06356bSdh int 61704c06356bSdh pmcs_dma_load(pmcs_hw_t *pwp, pmcs_cmd_t *sp, uint32_t *msg) 61714c06356bSdh { 61724c06356bSdh ddi_dma_cookie_t *sg; 61734c06356bSdh pmcs_dmachunk_t *tc; 61744c06356bSdh pmcs_dmasgl_t *sgl, *prior; 61754c06356bSdh int seg, tsc; 61764c06356bSdh uint64_t sgl_addr; 61774c06356bSdh 61784c06356bSdh /* 61794c06356bSdh * If we have no data segments, we're done. 61804c06356bSdh */ 61814c06356bSdh if (CMD2PKT(sp)->pkt_numcookies == 0) { 61824c06356bSdh return (0); 61834c06356bSdh } 61844c06356bSdh 61854c06356bSdh /* 61864c06356bSdh * Get the S/G list pointer. 61874c06356bSdh */ 61884c06356bSdh sg = CMD2PKT(sp)->pkt_cookies; 61894c06356bSdh 61904c06356bSdh /* 61914c06356bSdh * If we only have one dma segment, we can directly address that 61924c06356bSdh * data within the Inbound message itself. 61934c06356bSdh */ 61944c06356bSdh if (CMD2PKT(sp)->pkt_numcookies == 1) { 61954c06356bSdh msg[12] = LE_32(DWORD0(sg->dmac_laddress)); 61964c06356bSdh msg[13] = LE_32(DWORD1(sg->dmac_laddress)); 61974c06356bSdh msg[14] = LE_32(sg->dmac_size); 61984c06356bSdh msg[15] = 0; 61994c06356bSdh return (0); 62004c06356bSdh } 62014c06356bSdh 62024c06356bSdh /* 62034c06356bSdh * Otherwise, we'll need one or more external S/G list chunks. 62044c06356bSdh * Get the first one and its dma address into the Inbound message. 62054c06356bSdh */ 62064c06356bSdh mutex_enter(&pwp->dma_lock); 62074c06356bSdh tc = pwp->dma_freelist; 62084c06356bSdh if (tc == NULL) { 62094c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS); 62104c06356bSdh mutex_exit(&pwp->dma_lock); 6211c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, 6212c3bc407cSdh "%s: out of SG lists", __func__); 62134c06356bSdh return (-1); 62144c06356bSdh } 62154c06356bSdh pwp->dma_freelist = tc->nxt; 62164c06356bSdh mutex_exit(&pwp->dma_lock); 62174c06356bSdh 62184c06356bSdh tc->nxt = NULL; 62194c06356bSdh sp->cmd_clist = tc; 62204c06356bSdh sgl = tc->chunks; 62214c06356bSdh (void) memset(tc->chunks, 0, PMCS_SGL_CHUNKSZ); 62224c06356bSdh sgl_addr = tc->addr; 62234c06356bSdh msg[12] = LE_32(DWORD0(sgl_addr)); 62244c06356bSdh msg[13] = LE_32(DWORD1(sgl_addr)); 62254c06356bSdh msg[14] = 0; 62264c06356bSdh msg[15] = LE_32(PMCS_DMASGL_EXTENSION); 62274c06356bSdh 62284c06356bSdh prior = sgl; 62294c06356bSdh tsc = 0; 62304c06356bSdh 62314c06356bSdh for (seg = 0; seg < CMD2PKT(sp)->pkt_numcookies; seg++) { 62324c06356bSdh /* 62334c06356bSdh * If the current segment count for this chunk is one less than 62344c06356bSdh * the number s/g lists per chunk and we have more than one seg 62354c06356bSdh * to go, we need another chunk. Get it, and make sure that the 62364c06356bSdh * tail end of the the previous chunk points the new chunk 62374c06356bSdh * (if remembering an offset can be called 'pointing to'). 62384c06356bSdh * 62394c06356bSdh * Note that we can store the offset into our command area that 62404c06356bSdh * represents the new chunk in the length field of the part 62414c06356bSdh * that points the PMC chip at the next chunk- the PMC chip 62424c06356bSdh * ignores this field when the EXTENSION bit is set. 62434c06356bSdh * 62444c06356bSdh * This is required for dma unloads later. 62454c06356bSdh */ 62464c06356bSdh if (tsc == (PMCS_SGL_NCHUNKS - 1) && 62474c06356bSdh seg < (CMD2PKT(sp)->pkt_numcookies - 1)) { 62484c06356bSdh mutex_enter(&pwp->dma_lock); 62494c06356bSdh tc = pwp->dma_freelist; 62504c06356bSdh if (tc == NULL) { 62514c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS); 62524c06356bSdh mutex_exit(&pwp->dma_lock); 62534c06356bSdh pmcs_dma_unload(pwp, sp); 6254c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, 62554c06356bSdh "%s: out of SG lists", __func__); 62564c06356bSdh return (-1); 62574c06356bSdh } 62584c06356bSdh pwp->dma_freelist = tc->nxt; 62594c06356bSdh tc->nxt = sp->cmd_clist; 62604c06356bSdh mutex_exit(&pwp->dma_lock); 62614c06356bSdh 62624c06356bSdh sp->cmd_clist = tc; 62634c06356bSdh (void) memset(tc->chunks, 0, PMCS_SGL_CHUNKSZ); 62644c06356bSdh sgl = tc->chunks; 62654c06356bSdh sgl_addr = tc->addr; 62664c06356bSdh prior[PMCS_SGL_NCHUNKS-1].sglal = 62674c06356bSdh LE_32(DWORD0(sgl_addr)); 62684c06356bSdh prior[PMCS_SGL_NCHUNKS-1].sglah = 62694c06356bSdh LE_32(DWORD1(sgl_addr)); 62704c06356bSdh prior[PMCS_SGL_NCHUNKS-1].sglen = 0; 62714c06356bSdh prior[PMCS_SGL_NCHUNKS-1].flags = 62724c06356bSdh LE_32(PMCS_DMASGL_EXTENSION); 62734c06356bSdh prior = sgl; 62744c06356bSdh tsc = 0; 62754c06356bSdh } 62764c06356bSdh sgl[tsc].sglal = LE_32(DWORD0(sg->dmac_laddress)); 62774c06356bSdh sgl[tsc].sglah = LE_32(DWORD1(sg->dmac_laddress)); 62784c06356bSdh sgl[tsc].sglen = LE_32(sg->dmac_size); 62794c06356bSdh sgl[tsc++].flags = 0; 62804c06356bSdh sg++; 62814c06356bSdh } 62824c06356bSdh return (0); 62834c06356bSdh } 62844c06356bSdh 62854c06356bSdh /* 62864c06356bSdh * Unload DMA 62874c06356bSdh */ 62884c06356bSdh void 62894c06356bSdh pmcs_dma_unload(pmcs_hw_t *pwp, pmcs_cmd_t *sp) 62904c06356bSdh { 62914c06356bSdh pmcs_dmachunk_t *cp; 62924c06356bSdh 62934c06356bSdh mutex_enter(&pwp->dma_lock); 62944c06356bSdh while ((cp = sp->cmd_clist) != NULL) { 62954c06356bSdh sp->cmd_clist = cp->nxt; 62964c06356bSdh cp->nxt = pwp->dma_freelist; 62974c06356bSdh pwp->dma_freelist = cp; 62984c06356bSdh } 62994c06356bSdh mutex_exit(&pwp->dma_lock); 63004c06356bSdh } 63014c06356bSdh 63024c06356bSdh /* 63034c06356bSdh * Take a chunk of consistent memory that has just been allocated and inserted 63044c06356bSdh * into the cip indices and prepare it for DMA chunk usage and add it to the 63054c06356bSdh * freelist. 63064c06356bSdh * 63074c06356bSdh * Called with dma_lock locked (except during attach when it's unnecessary) 63084c06356bSdh */ 63094c06356bSdh void 63104c06356bSdh pmcs_idma_chunks(pmcs_hw_t *pwp, pmcs_dmachunk_t *dcp, 63114c06356bSdh pmcs_chunk_t *pchunk, unsigned long lim) 63124c06356bSdh { 63134c06356bSdh unsigned long off, n; 63144c06356bSdh pmcs_dmachunk_t *np = dcp; 63154c06356bSdh pmcs_chunk_t *tmp_chunk; 63164c06356bSdh 63174c06356bSdh if (pwp->dma_chunklist == NULL) { 63184c06356bSdh pwp->dma_chunklist = pchunk; 63194c06356bSdh } else { 63204c06356bSdh tmp_chunk = pwp->dma_chunklist; 63214c06356bSdh while (tmp_chunk->next) { 63224c06356bSdh tmp_chunk = tmp_chunk->next; 63234c06356bSdh } 63244c06356bSdh tmp_chunk->next = pchunk; 63254c06356bSdh } 63264c06356bSdh 63274c06356bSdh /* 63284c06356bSdh * Install offsets into chunk lists. 63294c06356bSdh */ 63304c06356bSdh for (n = 0, off = 0; off < lim; off += PMCS_SGL_CHUNKSZ, n++) { 63314c06356bSdh np->chunks = (void *)&pchunk->addrp[off]; 63324c06356bSdh np->addr = pchunk->dma_addr + off; 63334c06356bSdh np->acc_handle = pchunk->acc_handle; 63344c06356bSdh np->dma_handle = pchunk->dma_handle; 63354c06356bSdh if ((off + PMCS_SGL_CHUNKSZ) < lim) { 63364c06356bSdh np = np->nxt; 63374c06356bSdh } 63384c06356bSdh } 63394c06356bSdh np->nxt = pwp->dma_freelist; 63404c06356bSdh pwp->dma_freelist = dcp; 6341c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, 63424c06356bSdh "added %lu DMA chunks ", n); 63434c06356bSdh } 63444c06356bSdh 63454c06356bSdh /* 63464c06356bSdh * Change the value of the interrupt coalescing timer. This is done currently 63474c06356bSdh * only for I/O completions. If we're using the "auto clear" feature, it can 63484c06356bSdh * be turned back on when interrupt coalescing is turned off and must be 63494c06356bSdh * turned off when the coalescing timer is on. 63504c06356bSdh * NOTE: PMCS_MSIX_GENERAL and PMCS_OQ_IODONE are the same value. As long 63514c06356bSdh * as that's true, we don't need to distinguish between them. 63524c06356bSdh */ 63534c06356bSdh 63544c06356bSdh void 63554c06356bSdh pmcs_set_intr_coal_timer(pmcs_hw_t *pwp, pmcs_coal_timer_adj_t adj) 63564c06356bSdh { 63574c06356bSdh if (adj == DECREASE_TIMER) { 63584c06356bSdh /* If the timer is already off, nothing to do. */ 63594c06356bSdh if (pwp->io_intr_coal.timer_on == B_FALSE) { 63604c06356bSdh return; 63614c06356bSdh } 63624c06356bSdh 63634c06356bSdh pwp->io_intr_coal.intr_coal_timer -= PMCS_COAL_TIMER_GRAN; 63644c06356bSdh 63654c06356bSdh if (pwp->io_intr_coal.intr_coal_timer == 0) { 63664c06356bSdh /* Disable the timer */ 63674c06356bSdh pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_CONTROL, 0); 63684c06356bSdh 63694c06356bSdh if (pwp->odb_auto_clear & (1 << PMCS_MSIX_IODONE)) { 63704c06356bSdh pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR, 63714c06356bSdh pwp->odb_auto_clear); 63724c06356bSdh } 63734c06356bSdh 63744c06356bSdh pwp->io_intr_coal.timer_on = B_FALSE; 63754c06356bSdh pwp->io_intr_coal.max_io_completions = B_FALSE; 63764c06356bSdh pwp->io_intr_coal.num_intrs = 0; 63774c06356bSdh pwp->io_intr_coal.int_cleared = B_FALSE; 63784c06356bSdh pwp->io_intr_coal.num_io_completions = 0; 63794c06356bSdh 63804c06356bSdh DTRACE_PROBE1(pmcs__intr__coalesce__timer__off, 63814c06356bSdh pmcs_io_intr_coal_t *, &pwp->io_intr_coal); 63824c06356bSdh } else { 63834c06356bSdh pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_TIMER, 63844c06356bSdh pwp->io_intr_coal.intr_coal_timer); 63854c06356bSdh } 63864c06356bSdh } else { 63874c06356bSdh /* 63884c06356bSdh * If the timer isn't on yet, do the setup for it now. 63894c06356bSdh */ 63904c06356bSdh if (pwp->io_intr_coal.timer_on == B_FALSE) { 63914c06356bSdh /* If auto clear is being used, turn it off. */ 63924c06356bSdh if (pwp->odb_auto_clear & (1 << PMCS_MSIX_IODONE)) { 63934c06356bSdh pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR, 63944c06356bSdh (pwp->odb_auto_clear & 63954c06356bSdh ~(1 << PMCS_MSIX_IODONE))); 63964c06356bSdh } 63974c06356bSdh 63984c06356bSdh pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_CONTROL, 63994c06356bSdh (1 << PMCS_MSIX_IODONE)); 64004c06356bSdh pwp->io_intr_coal.timer_on = B_TRUE; 64014c06356bSdh pwp->io_intr_coal.intr_coal_timer = 64024c06356bSdh PMCS_COAL_TIMER_GRAN; 64034c06356bSdh 64044c06356bSdh DTRACE_PROBE1(pmcs__intr__coalesce__timer__on, 64054c06356bSdh pmcs_io_intr_coal_t *, &pwp->io_intr_coal); 64064c06356bSdh } else { 64074c06356bSdh pwp->io_intr_coal.intr_coal_timer += 64084c06356bSdh PMCS_COAL_TIMER_GRAN; 64094c06356bSdh } 64104c06356bSdh 64114c06356bSdh if (pwp->io_intr_coal.intr_coal_timer > PMCS_MAX_COAL_TIMER) { 64124c06356bSdh pwp->io_intr_coal.intr_coal_timer = PMCS_MAX_COAL_TIMER; 64134c06356bSdh } 64144c06356bSdh 64154c06356bSdh pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_TIMER, 64164c06356bSdh pwp->io_intr_coal.intr_coal_timer); 64174c06356bSdh } 64184c06356bSdh 64194c06356bSdh /* 64204c06356bSdh * Adjust the interrupt threshold based on the current timer value 64214c06356bSdh */ 64224c06356bSdh pwp->io_intr_coal.intr_threshold = 64234c06356bSdh PMCS_INTR_THRESHOLD(PMCS_QUANTUM_TIME_USECS * 1000 / 64244c06356bSdh (pwp->io_intr_coal.intr_latency + 64254c06356bSdh (pwp->io_intr_coal.intr_coal_timer * 1000))); 64264c06356bSdh } 64274c06356bSdh 64284c06356bSdh /* 64294c06356bSdh * Register Access functions 64304c06356bSdh */ 64314c06356bSdh uint32_t 64324c06356bSdh pmcs_rd_iqci(pmcs_hw_t *pwp, uint32_t qnum) 64334c06356bSdh { 64344c06356bSdh uint32_t iqci; 64354c06356bSdh 64364c06356bSdh if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORKERNEL) != 64374c06356bSdh DDI_SUCCESS) { 6438c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6439c3bc407cSdh "%s: ddi_dma_sync failed?", __func__); 64404c06356bSdh } 64414c06356bSdh 64424c06356bSdh iqci = LE_32( 64434c06356bSdh ((uint32_t *)((void *)pwp->cip))[IQ_OFFSET(qnum) >> 2]); 64444c06356bSdh 64454c06356bSdh return (iqci); 64464c06356bSdh } 64474c06356bSdh 64484c06356bSdh uint32_t 64494c06356bSdh pmcs_rd_oqpi(pmcs_hw_t *pwp, uint32_t qnum) 64504c06356bSdh { 64514c06356bSdh uint32_t oqpi; 64524c06356bSdh 64534c06356bSdh if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORKERNEL) != 64544c06356bSdh DDI_SUCCESS) { 6455c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6456c3bc407cSdh "%s: ddi_dma_sync failed?", __func__); 64574c06356bSdh } 64584c06356bSdh 64594c06356bSdh oqpi = LE_32( 64604c06356bSdh ((uint32_t *)((void *)pwp->cip))[OQ_OFFSET(qnum) >> 2]); 64614c06356bSdh 64624c06356bSdh return (oqpi); 64634c06356bSdh } 64644c06356bSdh 64654c06356bSdh uint32_t 64662ac4abe8SDavid Hollister pmcs_rd_gsm_reg(pmcs_hw_t *pwp, uint8_t hi, uint32_t off) 64674c06356bSdh { 64682ac4abe8SDavid Hollister uint32_t rv, newaxil, oldaxil, oldaxih; 64694c06356bSdh 64704c06356bSdh newaxil = off & ~GSM_BASE_MASK; 64714c06356bSdh off &= GSM_BASE_MASK; 64724c06356bSdh mutex_enter(&pwp->axil_lock); 64734c06356bSdh oldaxil = ddi_get32(pwp->top_acc_handle, 64744c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2]); 64754c06356bSdh ddi_put32(pwp->top_acc_handle, 64764c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2], newaxil); 64774c06356bSdh drv_usecwait(10); 64784c06356bSdh if (ddi_get32(pwp->top_acc_handle, 64794c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != newaxil) { 6480c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6481c3bc407cSdh "AXIL register update failed"); 64824c06356bSdh } 64832ac4abe8SDavid Hollister if (hi) { 64842ac4abe8SDavid Hollister oldaxih = ddi_get32(pwp->top_acc_handle, 64852ac4abe8SDavid Hollister &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2]); 64862ac4abe8SDavid Hollister ddi_put32(pwp->top_acc_handle, 64872ac4abe8SDavid Hollister &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2], hi); 64882ac4abe8SDavid Hollister drv_usecwait(10); 64892ac4abe8SDavid Hollister if (ddi_get32(pwp->top_acc_handle, 64902ac4abe8SDavid Hollister &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2]) != hi) { 64912ac4abe8SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 64922ac4abe8SDavid Hollister "AXIH register update failed"); 64932ac4abe8SDavid Hollister } 64942ac4abe8SDavid Hollister } 64954c06356bSdh rv = ddi_get32(pwp->gsm_acc_handle, &pwp->gsm_regs[off >> 2]); 64962ac4abe8SDavid Hollister if (hi) { 64972ac4abe8SDavid Hollister ddi_put32(pwp->top_acc_handle, 64982ac4abe8SDavid Hollister &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2], oldaxih); 64992ac4abe8SDavid Hollister drv_usecwait(10); 65002ac4abe8SDavid Hollister if (ddi_get32(pwp->top_acc_handle, 65012ac4abe8SDavid Hollister &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2]) != oldaxih) { 65022ac4abe8SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 65032ac4abe8SDavid Hollister "AXIH register restore failed"); 65042ac4abe8SDavid Hollister } 65052ac4abe8SDavid Hollister } 65064c06356bSdh ddi_put32(pwp->top_acc_handle, 65074c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2], oldaxil); 65084c06356bSdh drv_usecwait(10); 65094c06356bSdh if (ddi_get32(pwp->top_acc_handle, 65104c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != oldaxil) { 6511c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6512c3bc407cSdh "AXIL register restore failed"); 65134c06356bSdh } 65144c06356bSdh mutex_exit(&pwp->axil_lock); 65154c06356bSdh return (rv); 65164c06356bSdh } 65174c06356bSdh 65184c06356bSdh void 65194c06356bSdh pmcs_wr_gsm_reg(pmcs_hw_t *pwp, uint32_t off, uint32_t val) 65204c06356bSdh { 65214c06356bSdh uint32_t newaxil, oldaxil; 65224c06356bSdh 65234c06356bSdh newaxil = off & ~GSM_BASE_MASK; 65244c06356bSdh off &= GSM_BASE_MASK; 65254c06356bSdh mutex_enter(&pwp->axil_lock); 65264c06356bSdh oldaxil = ddi_get32(pwp->top_acc_handle, 65274c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2]); 65284c06356bSdh ddi_put32(pwp->top_acc_handle, 65294c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2], newaxil); 65304c06356bSdh drv_usecwait(10); 65314c06356bSdh if (ddi_get32(pwp->top_acc_handle, 65324c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != newaxil) { 6533c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6534c3bc407cSdh "AXIL register update failed"); 65354c06356bSdh } 65364c06356bSdh ddi_put32(pwp->gsm_acc_handle, &pwp->gsm_regs[off >> 2], val); 65374c06356bSdh ddi_put32(pwp->top_acc_handle, 65384c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2], oldaxil); 65394c06356bSdh drv_usecwait(10); 65404c06356bSdh if (ddi_get32(pwp->top_acc_handle, 65414c06356bSdh &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != oldaxil) { 6542c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6543c3bc407cSdh "AXIL register restore failed"); 65444c06356bSdh } 65454c06356bSdh mutex_exit(&pwp->axil_lock); 65464c06356bSdh } 65474c06356bSdh 65484c06356bSdh uint32_t 65494c06356bSdh pmcs_rd_topunit(pmcs_hw_t *pwp, uint32_t off) 65504c06356bSdh { 65514c06356bSdh switch (off) { 65524c06356bSdh case PMCS_SPC_RESET: 65534c06356bSdh case PMCS_SPC_BOOT_STRAP: 65544c06356bSdh case PMCS_SPC_DEVICE_ID: 65554c06356bSdh case PMCS_DEVICE_REVISION: 65562ac4abe8SDavid Hollister off = pmcs_rd_gsm_reg(pwp, 0, off); 65574c06356bSdh break; 65584c06356bSdh default: 65594c06356bSdh off = ddi_get32(pwp->top_acc_handle, 65604c06356bSdh &pwp->top_regs[off >> 2]); 65614c06356bSdh break; 65624c06356bSdh } 65634c06356bSdh return (off); 65644c06356bSdh } 65654c06356bSdh 65664c06356bSdh void 65674c06356bSdh pmcs_wr_topunit(pmcs_hw_t *pwp, uint32_t off, uint32_t val) 65684c06356bSdh { 65694c06356bSdh switch (off) { 65704c06356bSdh case PMCS_SPC_RESET: 65714c06356bSdh case PMCS_DEVICE_REVISION: 65724c06356bSdh pmcs_wr_gsm_reg(pwp, off, val); 65734c06356bSdh break; 65744c06356bSdh default: 65754c06356bSdh ddi_put32(pwp->top_acc_handle, &pwp->top_regs[off >> 2], val); 65764c06356bSdh break; 65774c06356bSdh } 65784c06356bSdh } 65794c06356bSdh 65804c06356bSdh uint32_t 65814c06356bSdh pmcs_rd_msgunit(pmcs_hw_t *pwp, uint32_t off) 65824c06356bSdh { 65834c06356bSdh return (ddi_get32(pwp->msg_acc_handle, &pwp->msg_regs[off >> 2])); 65844c06356bSdh } 65854c06356bSdh 65864c06356bSdh uint32_t 65874c06356bSdh pmcs_rd_mpi_tbl(pmcs_hw_t *pwp, uint32_t off) 65884c06356bSdh { 65894c06356bSdh return (ddi_get32(pwp->mpi_acc_handle, 65904c06356bSdh &pwp->mpi_regs[(pwp->mpi_offset + off) >> 2])); 65914c06356bSdh } 65924c06356bSdh 65934c06356bSdh uint32_t 65944c06356bSdh pmcs_rd_gst_tbl(pmcs_hw_t *pwp, uint32_t off) 65954c06356bSdh { 65964c06356bSdh return (ddi_get32(pwp->mpi_acc_handle, 65974c06356bSdh &pwp->mpi_regs[(pwp->mpi_gst_offset + off) >> 2])); 65984c06356bSdh } 65994c06356bSdh 66004c06356bSdh uint32_t 66014c06356bSdh pmcs_rd_iqc_tbl(pmcs_hw_t *pwp, uint32_t off) 66024c06356bSdh { 66034c06356bSdh return (ddi_get32(pwp->mpi_acc_handle, 66044c06356bSdh &pwp->mpi_regs[(pwp->mpi_iqc_offset + off) >> 2])); 66054c06356bSdh } 66064c06356bSdh 66074c06356bSdh uint32_t 66084c06356bSdh pmcs_rd_oqc_tbl(pmcs_hw_t *pwp, uint32_t off) 66094c06356bSdh { 66104c06356bSdh return (ddi_get32(pwp->mpi_acc_handle, 66114c06356bSdh &pwp->mpi_regs[(pwp->mpi_oqc_offset + off) >> 2])); 66124c06356bSdh } 66134c06356bSdh 66144c06356bSdh uint32_t 66154c06356bSdh pmcs_rd_iqpi(pmcs_hw_t *pwp, uint32_t qnum) 66164c06356bSdh { 66174c06356bSdh return (ddi_get32(pwp->mpi_acc_handle, 66184c06356bSdh &pwp->mpi_regs[pwp->iqpi_offset[qnum] >> 2])); 66194c06356bSdh } 66204c06356bSdh 66214c06356bSdh uint32_t 66224c06356bSdh pmcs_rd_oqci(pmcs_hw_t *pwp, uint32_t qnum) 66234c06356bSdh { 66244c06356bSdh return (ddi_get32(pwp->mpi_acc_handle, 66254c06356bSdh &pwp->mpi_regs[pwp->oqci_offset[qnum] >> 2])); 66264c06356bSdh } 66274c06356bSdh 66284c06356bSdh void 66294c06356bSdh pmcs_wr_msgunit(pmcs_hw_t *pwp, uint32_t off, uint32_t val) 66304c06356bSdh { 66314c06356bSdh ddi_put32(pwp->msg_acc_handle, &pwp->msg_regs[off >> 2], val); 66324c06356bSdh } 66334c06356bSdh 66344c06356bSdh void 66354c06356bSdh pmcs_wr_mpi_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val) 66364c06356bSdh { 66374c06356bSdh ddi_put32(pwp->mpi_acc_handle, 66384c06356bSdh &pwp->mpi_regs[(pwp->mpi_offset + off) >> 2], (val)); 66394c06356bSdh } 66404c06356bSdh 66414c06356bSdh void 66424c06356bSdh pmcs_wr_gst_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val) 66434c06356bSdh { 66444c06356bSdh ddi_put32(pwp->mpi_acc_handle, 66454c06356bSdh &pwp->mpi_regs[(pwp->mpi_gst_offset + off) >> 2], val); 66464c06356bSdh } 66474c06356bSdh 66484c06356bSdh void 66494c06356bSdh pmcs_wr_iqc_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val) 66504c06356bSdh { 66514c06356bSdh ddi_put32(pwp->mpi_acc_handle, 66524c06356bSdh &pwp->mpi_regs[(pwp->mpi_iqc_offset + off) >> 2], val); 66534c06356bSdh } 66544c06356bSdh 66554c06356bSdh void 66564c06356bSdh pmcs_wr_oqc_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val) 66574c06356bSdh { 66584c06356bSdh ddi_put32(pwp->mpi_acc_handle, 66594c06356bSdh &pwp->mpi_regs[(pwp->mpi_oqc_offset + off) >> 2], val); 66604c06356bSdh } 66614c06356bSdh 66624c06356bSdh void 66634c06356bSdh pmcs_wr_iqci(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val) 66644c06356bSdh { 66654c06356bSdh ((uint32_t *)((void *)pwp->cip))[IQ_OFFSET(qnum) >> 2] = val; 66664c06356bSdh if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORDEV) != 66674c06356bSdh DDI_SUCCESS) { 6668c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6669c3bc407cSdh "%s: ddi_dma_sync failed?", __func__); 66704c06356bSdh } 66714c06356bSdh } 66724c06356bSdh 66734c06356bSdh void 66744c06356bSdh pmcs_wr_iqpi(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val) 66754c06356bSdh { 66764c06356bSdh ddi_put32(pwp->mpi_acc_handle, 66774c06356bSdh &pwp->mpi_regs[pwp->iqpi_offset[qnum] >> 2], val); 66784c06356bSdh } 66794c06356bSdh 66804c06356bSdh void 66814c06356bSdh pmcs_wr_oqci(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val) 66824c06356bSdh { 66834c06356bSdh ddi_put32(pwp->mpi_acc_handle, 66844c06356bSdh &pwp->mpi_regs[pwp->oqci_offset[qnum] >> 2], val); 66854c06356bSdh } 66864c06356bSdh 66874c06356bSdh void 66884c06356bSdh pmcs_wr_oqpi(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val) 66894c06356bSdh { 66904c06356bSdh ((uint32_t *)((void *)pwp->cip))[OQ_OFFSET(qnum) >> 2] = val; 66914c06356bSdh if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORDEV) != 66924c06356bSdh DDI_SUCCESS) { 6693c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6694c3bc407cSdh "%s: ddi_dma_sync failed?", __func__); 66954c06356bSdh } 66964c06356bSdh } 66974c06356bSdh 66984c06356bSdh /* 66994c06356bSdh * Check the status value of an outbound IOMB and report anything bad 67004c06356bSdh */ 67014c06356bSdh 67024c06356bSdh void 67034c06356bSdh pmcs_check_iomb_status(pmcs_hw_t *pwp, uint32_t *iomb) 67044c06356bSdh { 67054c06356bSdh uint16_t opcode; 67064c06356bSdh int offset; 67074c06356bSdh 67084c06356bSdh if (iomb == NULL) { 67094c06356bSdh return; 67104c06356bSdh } 67114c06356bSdh 67124c06356bSdh opcode = LE_32(iomb[0]) & 0xfff; 67134c06356bSdh 67144c06356bSdh switch (opcode) { 67154c06356bSdh /* 67164c06356bSdh * The following have no status field, so ignore them 67174c06356bSdh */ 67184c06356bSdh case PMCOUT_ECHO: 67194c06356bSdh case PMCOUT_SAS_HW_EVENT: 67204c06356bSdh case PMCOUT_GET_DEVICE_HANDLE: 67214c06356bSdh case PMCOUT_SATA_EVENT: 67224c06356bSdh case PMCOUT_SSP_EVENT: 67234c06356bSdh case PMCOUT_DEVICE_HANDLE_ARRIVED: 67244c06356bSdh case PMCOUT_SMP_REQUEST_RECEIVED: 67254c06356bSdh case PMCOUT_GPIO: 67264c06356bSdh case PMCOUT_GPIO_EVENT: 67274c06356bSdh case PMCOUT_GET_TIME_STAMP: 67284c06356bSdh case PMCOUT_SKIP_ENTRIES: 67294c06356bSdh case PMCOUT_GET_NVMD_DATA: /* Actually lower 16 bits of word 3 */ 67304c06356bSdh case PMCOUT_SET_NVMD_DATA: /* but ignore - we don't use these */ 67314c06356bSdh case PMCOUT_DEVICE_HANDLE_REMOVED: 67324c06356bSdh case PMCOUT_SSP_REQUEST_RECEIVED: 67334c06356bSdh return; 67344c06356bSdh 67354c06356bSdh case PMCOUT_GENERAL_EVENT: 67364c06356bSdh offset = 1; 67374c06356bSdh break; 67384c06356bSdh 67394c06356bSdh case PMCOUT_SSP_COMPLETION: 67404c06356bSdh case PMCOUT_SMP_COMPLETION: 67414c06356bSdh case PMCOUT_DEVICE_REGISTRATION: 67424c06356bSdh case PMCOUT_DEREGISTER_DEVICE_HANDLE: 67434c06356bSdh case PMCOUT_SATA_COMPLETION: 67444c06356bSdh case PMCOUT_DEVICE_INFO: 67454c06356bSdh case PMCOUT_FW_FLASH_UPDATE: 67464c06356bSdh case PMCOUT_SSP_ABORT: 67474c06356bSdh case PMCOUT_SATA_ABORT: 67484c06356bSdh case PMCOUT_SAS_DIAG_MODE_START_END: 67494c06356bSdh case PMCOUT_SAS_HW_EVENT_ACK_ACK: 67504c06356bSdh case PMCOUT_SMP_ABORT: 67514c06356bSdh case PMCOUT_SET_DEVICE_STATE: 67524c06356bSdh case PMCOUT_GET_DEVICE_STATE: 67534c06356bSdh case PMCOUT_SET_DEVICE_INFO: 67544c06356bSdh offset = 2; 67554c06356bSdh break; 67564c06356bSdh 67574c06356bSdh case PMCOUT_LOCAL_PHY_CONTROL: 67584c06356bSdh case PMCOUT_SAS_DIAG_EXECUTE: 67594c06356bSdh case PMCOUT_PORT_CONTROL: 67604c06356bSdh offset = 3; 67614c06356bSdh break; 67624c06356bSdh 67634c06356bSdh case PMCOUT_GET_INFO: 67644c06356bSdh case PMCOUT_GET_VPD: 67654c06356bSdh case PMCOUT_SAS_ASSISTED_DISCOVERY_EVENT: 67664c06356bSdh case PMCOUT_SATA_ASSISTED_DISCOVERY_EVENT: 67674c06356bSdh case PMCOUT_SET_VPD: 67684c06356bSdh case PMCOUT_TWI: 67694c06356bSdh pmcs_print_entry(pwp, PMCS_PRT_DEBUG, 67704c06356bSdh "Got response for deprecated opcode", iomb); 67714c06356bSdh return; 67724c06356bSdh 67734c06356bSdh default: 67744c06356bSdh pmcs_print_entry(pwp, PMCS_PRT_DEBUG, 67754c06356bSdh "Got response for unknown opcode", iomb); 67764c06356bSdh return; 67774c06356bSdh } 67784c06356bSdh 67794c06356bSdh if (LE_32(iomb[offset]) != PMCOUT_STATUS_OK) { 67804c06356bSdh pmcs_print_entry(pwp, PMCS_PRT_DEBUG, 67814c06356bSdh "bad status on TAG_TYPE_NONE command", iomb); 67824c06356bSdh } 67834c06356bSdh } 67844c06356bSdh 67854c06356bSdh /* 67864c06356bSdh * Called with statlock held 67874c06356bSdh */ 67884c06356bSdh void 67894c06356bSdh pmcs_clear_xp(pmcs_hw_t *pwp, pmcs_xscsi_t *xp) 67904c06356bSdh { 67914c06356bSdh _NOTE(ARGUNUSED(pwp)); 67924c06356bSdh 67934c06356bSdh ASSERT(mutex_owned(&xp->statlock)); 67944c06356bSdh 6795c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, "%s: Device 0x%p is gone.", 6796c3bc407cSdh __func__, (void *)xp); 67974c06356bSdh 67984c06356bSdh /* 6799b18a19c2SJesse Butler * Clear the dip now. This keeps pmcs_remove_device from attempting 68004c06356bSdh * to call us on the same device while we're still flushing queues. 68014c06356bSdh * The only side effect is we can no longer update SM-HBA properties, 68024c06356bSdh * but this device is going away anyway, so no matter. 68034c06356bSdh */ 68044c06356bSdh xp->dip = NULL; 6805499cfd15SDavid Hollister xp->smpd = NULL; 68064c06356bSdh xp->special_running = 0; 68074c06356bSdh xp->recovering = 0; 68084c06356bSdh xp->recover_wait = 0; 68094c06356bSdh xp->draining = 0; 68104c06356bSdh xp->new = 0; 68114c06356bSdh xp->assigned = 0; 68124c06356bSdh xp->dev_state = 0; 68134c06356bSdh xp->tagmap = 0; 68144c06356bSdh xp->dev_gone = 1; 68154c06356bSdh xp->event_recovery = 0; 68164c06356bSdh xp->dtype = NOTHING; 68174c06356bSdh xp->wq_recovery_tail = NULL; 68184c06356bSdh /* Don't clear xp->phy */ 68194c06356bSdh /* Don't clear xp->actv_cnt */ 6820601c90f1SSrikanth, Ramana /* Don't clear xp->actv_pkts */ 6821b18a19c2SJesse Butler 6822b18a19c2SJesse Butler /* 6823b18a19c2SJesse Butler * Flush all target queues 6824b18a19c2SJesse Butler */ 6825b18a19c2SJesse Butler pmcs_flush_target_queues(pwp, xp, PMCS_TGT_ALL_QUEUES); 68264c06356bSdh } 68274c06356bSdh 68284c06356bSdh static int 68294c06356bSdh pmcs_smp_function_result(pmcs_hw_t *pwp, smp_response_frame_t *srf) 68304c06356bSdh { 68314c06356bSdh int result = srf->srf_result; 68324c06356bSdh 68334c06356bSdh switch (result) { 68344c06356bSdh case SMP_RES_UNKNOWN_FUNCTION: 6835c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6836c3bc407cSdh "%s: SMP DISCOVER Response " 68374c06356bSdh "Function Result: Unknown SMP Function(0x%x)", 68384c06356bSdh __func__, result); 68394c06356bSdh break; 68404c06356bSdh case SMP_RES_FUNCTION_FAILED: 6841c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6842c3bc407cSdh "%s: SMP DISCOVER Response " 68434c06356bSdh "Function Result: SMP Function Failed(0x%x)", 68444c06356bSdh __func__, result); 68454c06356bSdh break; 68464c06356bSdh case SMP_RES_INVALID_REQUEST_FRAME_LENGTH: 6847c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6848c3bc407cSdh "%s: SMP DISCOVER Response " 68494c06356bSdh "Function Result: Invalid Request Frame Length(0x%x)", 68504c06356bSdh __func__, result); 68514c06356bSdh break; 68524c06356bSdh case SMP_RES_INCOMPLETE_DESCRIPTOR_LIST: 6853c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6854c3bc407cSdh "%s: SMP DISCOVER Response " 68554c06356bSdh "Function Result: Incomplete Descriptor List(0x%x)", 68564c06356bSdh __func__, result); 68574c06356bSdh break; 68584c06356bSdh case SMP_RES_PHY_DOES_NOT_EXIST: 6859c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6860c3bc407cSdh "%s: SMP DISCOVER Response " 68614c06356bSdh "Function Result: PHY does not exist(0x%x)", 68624c06356bSdh __func__, result); 68634c06356bSdh break; 68644c06356bSdh case SMP_RES_PHY_VACANT: 6865c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6866c3bc407cSdh "%s: SMP DISCOVER Response " 68674c06356bSdh "Function Result: PHY Vacant(0x%x)", 68684c06356bSdh __func__, result); 68694c06356bSdh break; 68704c06356bSdh default: 6871c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6872c3bc407cSdh "%s: SMP DISCOVER Response " 68734c06356bSdh "Function Result: (0x%x)", 68744c06356bSdh __func__, result); 68754c06356bSdh break; 68764c06356bSdh } 68774c06356bSdh 68784c06356bSdh return (result); 68794c06356bSdh } 68804c06356bSdh 68814c06356bSdh /* 68824c06356bSdh * Do all the repetitive stuff necessary to setup for DMA 68834c06356bSdh * 68844c06356bSdh * pwp: Used for dip 68854c06356bSdh * dma_attr: ddi_dma_attr_t to use for the mapping 68864c06356bSdh * acch: ddi_acc_handle_t to use for the mapping 68874c06356bSdh * dmah: ddi_dma_handle_t to use 68884c06356bSdh * length: Amount of memory for mapping 6889af4c679fSSean McEnroe * kvap: Pointer filled in with kernel virtual address on successful return 68904c06356bSdh * dma_addr: Pointer filled in with DMA address on successful return 68914c06356bSdh */ 68924c06356bSdh boolean_t 68934c06356bSdh pmcs_dma_setup(pmcs_hw_t *pwp, ddi_dma_attr_t *dma_attr, ddi_acc_handle_t *acch, 6894af4c679fSSean McEnroe ddi_dma_handle_t *dmah, size_t length, caddr_t *kvap, uint64_t *dma_addr) 68954c06356bSdh { 68964c06356bSdh dev_info_t *dip = pwp->dip; 68974c06356bSdh ddi_dma_cookie_t cookie; 68984c06356bSdh size_t real_length; 68994c06356bSdh uint_t ddma_flag = DDI_DMA_CONSISTENT; 69004c06356bSdh uint_t ddabh_flag = DDI_DMA_CONSISTENT | DDI_DMA_RDWR; 69014c06356bSdh uint_t cookie_cnt; 69024c06356bSdh ddi_device_acc_attr_t mattr = { 69034c06356bSdh DDI_DEVICE_ATTR_V0, 69044c06356bSdh DDI_NEVERSWAP_ACC, 69054c06356bSdh DDI_STRICTORDER_ACC, 69064c06356bSdh DDI_DEFAULT_ACC 69074c06356bSdh }; 69084c06356bSdh 69094c06356bSdh *acch = NULL; 69104c06356bSdh *dmah = NULL; 69114c06356bSdh 69124c06356bSdh if (ddi_dma_alloc_handle(dip, dma_attr, DDI_DMA_SLEEP, NULL, dmah) != 69134c06356bSdh DDI_SUCCESS) { 6914c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6915c3bc407cSdh "Failed to allocate DMA handle"); 69164c06356bSdh return (B_FALSE); 69174c06356bSdh } 69184c06356bSdh 69194c06356bSdh if (ddi_dma_mem_alloc(*dmah, length, &mattr, ddma_flag, DDI_DMA_SLEEP, 6920af4c679fSSean McEnroe NULL, kvap, &real_length, acch) != DDI_SUCCESS) { 6921c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6922c3bc407cSdh "Failed to allocate DMA mem"); 69234c06356bSdh ddi_dma_free_handle(dmah); 69244c06356bSdh *dmah = NULL; 69254c06356bSdh return (B_FALSE); 69264c06356bSdh } 69274c06356bSdh 6928af4c679fSSean McEnroe if (ddi_dma_addr_bind_handle(*dmah, NULL, *kvap, real_length, 69294c06356bSdh ddabh_flag, DDI_DMA_SLEEP, NULL, &cookie, &cookie_cnt) 69304c06356bSdh != DDI_DMA_MAPPED) { 6931c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "Failed to bind DMA"); 69324c06356bSdh ddi_dma_free_handle(dmah); 69334c06356bSdh ddi_dma_mem_free(acch); 69344c06356bSdh *dmah = NULL; 69354c06356bSdh *acch = NULL; 69364c06356bSdh return (B_FALSE); 69374c06356bSdh } 69384c06356bSdh 69394c06356bSdh if (cookie_cnt != 1) { 6940c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "Multiple cookies"); 69414c06356bSdh if (ddi_dma_unbind_handle(*dmah) != DDI_SUCCESS) { 6942c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "Condition " 6943c3bc407cSdh "failed at %s():%d", __func__, __LINE__); 69444c06356bSdh } 69454c06356bSdh ddi_dma_free_handle(dmah); 69464c06356bSdh ddi_dma_mem_free(acch); 69474c06356bSdh *dmah = NULL; 69484c06356bSdh *acch = NULL; 69494c06356bSdh return (B_FALSE); 69504c06356bSdh } 69514c06356bSdh 69524c06356bSdh *dma_addr = cookie.dmac_laddress; 69534c06356bSdh 69544c06356bSdh return (B_TRUE); 69554c06356bSdh } 69564c06356bSdh 69574c06356bSdh /* 69584c06356bSdh * Flush requested queues for a particular target. Called with statlock held 69594c06356bSdh */ 69604c06356bSdh void 69614c06356bSdh pmcs_flush_target_queues(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt, uint8_t queues) 69624c06356bSdh { 69639aed1621SDavid Hollister pmcs_cmd_t *sp, *sp_next; 69644c06356bSdh pmcwork_t *pwrk; 69654c06356bSdh 69664c06356bSdh ASSERT(pwp != NULL); 69674c06356bSdh ASSERT(tgt != NULL); 69684c06356bSdh 6969c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, tgt, 69704c06356bSdh "%s: Flushing queues (%d) for target 0x%p", __func__, 69714c06356bSdh queues, (void *)tgt); 69724c06356bSdh 69734c06356bSdh /* 69744c06356bSdh * Commands on the wait queue (or the special queue below) don't have 69754c06356bSdh * work structures associated with them. 69764c06356bSdh */ 69774c06356bSdh if (queues & PMCS_TGT_WAIT_QUEUE) { 69784c06356bSdh mutex_enter(&tgt->wqlock); 69794c06356bSdh while ((sp = STAILQ_FIRST(&tgt->wq)) != NULL) { 69804c06356bSdh STAILQ_REMOVE(&tgt->wq, sp, pmcs_cmd, cmd_next); 6981c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, tgt, 69824c06356bSdh "%s: Removing cmd 0x%p from wq for target 0x%p", 69834c06356bSdh __func__, (void *)sp, (void *)tgt); 69844c06356bSdh CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE; 69854c06356bSdh CMD2PKT(sp)->pkt_state = STATE_GOT_BUS; 69864c06356bSdh mutex_exit(&tgt->wqlock); 69874c06356bSdh pmcs_dma_unload(pwp, sp); 69884c06356bSdh mutex_enter(&pwp->cq_lock); 69894c06356bSdh STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 69908f514e74SDavid Hollister PMCS_CQ_RUN_LOCKED(pwp); 69914c06356bSdh mutex_exit(&pwp->cq_lock); 69924c06356bSdh mutex_enter(&tgt->wqlock); 69934c06356bSdh } 69944c06356bSdh mutex_exit(&tgt->wqlock); 69954c06356bSdh } 69964c06356bSdh 69974c06356bSdh /* 69984c06356bSdh * Commands on the active queue will have work structures associated 69994c06356bSdh * with them. 70004c06356bSdh */ 70014c06356bSdh if (queues & PMCS_TGT_ACTIVE_QUEUE) { 700232b54db7SJesse Butler mutex_exit(&tgt->statlock); 70034c06356bSdh mutex_enter(&tgt->aqlock); 70049aed1621SDavid Hollister sp = STAILQ_FIRST(&tgt->aq); 70059aed1621SDavid Hollister while (sp) { 70069aed1621SDavid Hollister sp_next = STAILQ_NEXT(sp, cmd_next); 7007978d7443SSrikanth Suravajhala pwrk = pmcs_tag2wp(pwp, sp->cmd_tag, B_FALSE); 70089aed1621SDavid Hollister 70099aed1621SDavid Hollister /* 70109aed1621SDavid Hollister * If we don't find a work structure, it's because 70119aed1621SDavid Hollister * the command is already complete. If so, move on 70129aed1621SDavid Hollister * to the next one. 70139aed1621SDavid Hollister */ 70149aed1621SDavid Hollister if (pwrk == NULL) { 70159aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG1, tgt->phy, tgt, 70169aed1621SDavid Hollister "%s: Not removing cmd 0x%p (htag 0x%x) " 70179aed1621SDavid Hollister "from aq", __func__, (void *)sp, 70189aed1621SDavid Hollister sp->cmd_tag); 70199aed1621SDavid Hollister sp = sp_next; 70209aed1621SDavid Hollister continue; 70219aed1621SDavid Hollister } 70229aed1621SDavid Hollister 70239aed1621SDavid Hollister STAILQ_REMOVE(&tgt->aq, sp, pmcs_cmd, cmd_next); 70249aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG1, tgt->phy, tgt, 70259aed1621SDavid Hollister "%s: Removing cmd 0x%p (htag 0x%x) from aq for " 70269aed1621SDavid Hollister "target 0x%p", __func__, (void *)sp, sp->cmd_tag, 70279aed1621SDavid Hollister (void *)tgt); 70284c06356bSdh mutex_exit(&tgt->aqlock); 7029*3be32c0fSJesse Butler 70304c06356bSdh /* 70319aed1621SDavid Hollister * Mark the work structure as dead and complete it 70324c06356bSdh */ 70339aed1621SDavid Hollister pwrk->dead = 1; 70349aed1621SDavid Hollister CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE; 70359aed1621SDavid Hollister CMD2PKT(sp)->pkt_state = STATE_GOT_BUS; 70369aed1621SDavid Hollister pmcs_complete_work_impl(pwp, pwrk, NULL, 0); 70374c06356bSdh pmcs_dma_unload(pwp, sp); 70384c06356bSdh mutex_enter(&pwp->cq_lock); 70394c06356bSdh STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 70404c06356bSdh mutex_exit(&pwp->cq_lock); 70414c06356bSdh mutex_enter(&tgt->aqlock); 70429aed1621SDavid Hollister sp = sp_next; 70434c06356bSdh } 70444c06356bSdh mutex_exit(&tgt->aqlock); 704532b54db7SJesse Butler mutex_enter(&tgt->statlock); 70464c06356bSdh } 70474c06356bSdh 70484c06356bSdh if (queues & PMCS_TGT_SPECIAL_QUEUE) { 70494c06356bSdh while ((sp = STAILQ_FIRST(&tgt->sq)) != NULL) { 70504c06356bSdh STAILQ_REMOVE(&tgt->sq, sp, pmcs_cmd, cmd_next); 70519aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG1, tgt->phy, tgt, 70524c06356bSdh "%s: Removing cmd 0x%p from sq for target 0x%p", 70534c06356bSdh __func__, (void *)sp, (void *)tgt); 70544c06356bSdh CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE; 70554c06356bSdh CMD2PKT(sp)->pkt_state = STATE_GOT_BUS; 70564c06356bSdh pmcs_dma_unload(pwp, sp); 70574c06356bSdh mutex_enter(&pwp->cq_lock); 70584c06356bSdh STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 70594c06356bSdh mutex_exit(&pwp->cq_lock); 70604c06356bSdh } 70614c06356bSdh } 7062978d7443SSrikanth Suravajhala 7063978d7443SSrikanth Suravajhala if (queues == PMCS_TGT_ALL_QUEUES) { 7064978d7443SSrikanth Suravajhala mutex_exit(&tgt->statlock); 7065*3be32c0fSJesse Butler pmcs_flush_nonio_cmds(pwp, tgt); 7066978d7443SSrikanth Suravajhala mutex_enter(&tgt->statlock); 7067978d7443SSrikanth Suravajhala } 7068978d7443SSrikanth Suravajhala } 7069978d7443SSrikanth Suravajhala 7070978d7443SSrikanth Suravajhala /* 7071*3be32c0fSJesse Butler * Flush non-IO commands for this target. This cleans up the off-queue 7072*3be32c0fSJesse Butler * work with no pmcs_cmd_t associated. 7073978d7443SSrikanth Suravajhala */ 7074*3be32c0fSJesse Butler static void 7075*3be32c0fSJesse Butler pmcs_flush_nonio_cmds(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt) 7076978d7443SSrikanth Suravajhala { 7077978d7443SSrikanth Suravajhala int i; 7078978d7443SSrikanth Suravajhala pmcwork_t *p; 7079978d7443SSrikanth Suravajhala 7080978d7443SSrikanth Suravajhala for (i = 0; i < pwp->max_cmd; i++) { 7081978d7443SSrikanth Suravajhala p = &pwp->work[i]; 7082978d7443SSrikanth Suravajhala mutex_enter(&p->lock); 7083*3be32c0fSJesse Butler if (p->xp != tgt) { 7084*3be32c0fSJesse Butler mutex_exit(&p->lock); 7085*3be32c0fSJesse Butler continue; 7086*3be32c0fSJesse Butler } 7087978d7443SSrikanth Suravajhala if (p->htag & PMCS_TAG_NONIO_CMD) { 7088978d7443SSrikanth Suravajhala if (!PMCS_COMMAND_ACTIVE(p) || PMCS_COMMAND_DONE(p)) { 7089978d7443SSrikanth Suravajhala mutex_exit(&p->lock); 7090978d7443SSrikanth Suravajhala continue; 7091978d7443SSrikanth Suravajhala } 7092978d7443SSrikanth Suravajhala pmcs_prt(pwp, PMCS_PRT_DEBUG, p->phy, p->xp, 7093978d7443SSrikanth Suravajhala "%s: Completing non-io cmd with HTAG 0x%x", 7094978d7443SSrikanth Suravajhala __func__, p->htag); 7095978d7443SSrikanth Suravajhala pmcs_complete_work_impl(pwp, p, NULL, 0); 7096978d7443SSrikanth Suravajhala } else { 7097978d7443SSrikanth Suravajhala mutex_exit(&p->lock); 7098978d7443SSrikanth Suravajhala } 7099978d7443SSrikanth Suravajhala } 71004c06356bSdh } 71014c06356bSdh 71024c06356bSdh void 71034c06356bSdh pmcs_complete_work_impl(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *iomb, 71044c06356bSdh size_t amt) 71054c06356bSdh { 71064c06356bSdh switch (PMCS_TAG_TYPE(pwrk->htag)) { 71074c06356bSdh case PMCS_TAG_TYPE_CBACK: 71084c06356bSdh { 71094c06356bSdh pmcs_cb_t callback = (pmcs_cb_t)pwrk->ptr; 71104c06356bSdh (*callback)(pwp, pwrk, iomb); 71114c06356bSdh break; 71124c06356bSdh } 71134c06356bSdh case PMCS_TAG_TYPE_WAIT: 71144c06356bSdh if (pwrk->arg && iomb && amt) { 71154c06356bSdh (void) memcpy(pwrk->arg, iomb, amt); 71164c06356bSdh } 71174c06356bSdh cv_signal(&pwrk->sleep_cv); 71184c06356bSdh mutex_exit(&pwrk->lock); 71194c06356bSdh break; 71204c06356bSdh case PMCS_TAG_TYPE_NONE: 71214c06356bSdh #ifdef DEBUG 71224c06356bSdh pmcs_check_iomb_status(pwp, iomb); 71234c06356bSdh #endif 71244c06356bSdh pmcs_pwork(pwp, pwrk); 71254c06356bSdh break; 71264c06356bSdh default: 71274c06356bSdh /* 71284c06356bSdh * We will leak a structure here if we don't know 71294c06356bSdh * what happened 71304c06356bSdh */ 7131c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 7132c3bc407cSdh "%s: Unknown PMCS_TAG_TYPE (%x)", 71334c06356bSdh __func__, PMCS_TAG_TYPE(pwrk->htag)); 71344c06356bSdh break; 71354c06356bSdh } 71364c06356bSdh } 71374c06356bSdh 71384c06356bSdh /* 71394c06356bSdh * Determine if iport still has targets. During detach(9E), if SCSA is 71404c06356bSdh * successfull in its guarantee of tran_tgt_free(9E) before detach(9E), 71414c06356bSdh * this should always return B_FALSE. 71424c06356bSdh */ 71434c06356bSdh boolean_t 71444c06356bSdh pmcs_iport_has_targets(pmcs_hw_t *pwp, pmcs_iport_t *iport) 71454c06356bSdh { 71464c06356bSdh pmcs_xscsi_t *xp; 71474c06356bSdh int i; 71484c06356bSdh 71494c06356bSdh mutex_enter(&pwp->lock); 71504c06356bSdh 71514c06356bSdh if (!pwp->targets || !pwp->max_dev) { 71524c06356bSdh mutex_exit(&pwp->lock); 71534c06356bSdh return (B_FALSE); 71544c06356bSdh } 71554c06356bSdh 71564c06356bSdh for (i = 0; i < pwp->max_dev; i++) { 71574c06356bSdh xp = pwp->targets[i]; 71584c06356bSdh if ((xp == NULL) || (xp->phy == NULL) || 71594c06356bSdh (xp->phy->iport != iport)) { 71604c06356bSdh continue; 71614c06356bSdh } 71624c06356bSdh 71634c06356bSdh mutex_exit(&pwp->lock); 71644c06356bSdh return (B_TRUE); 71654c06356bSdh } 71664c06356bSdh 71674c06356bSdh mutex_exit(&pwp->lock); 71684c06356bSdh return (B_FALSE); 71694c06356bSdh } 71704c06356bSdh 71714c06356bSdh /* 71724c06356bSdh * Called with softstate lock held 71734c06356bSdh */ 71744c06356bSdh void 71754c06356bSdh pmcs_destroy_target(pmcs_xscsi_t *target) 71764c06356bSdh { 71774c06356bSdh pmcs_hw_t *pwp = target->pwp; 71784c06356bSdh pmcs_iport_t *iport; 71794c06356bSdh 71804c06356bSdh ASSERT(pwp); 71814c06356bSdh ASSERT(mutex_owned(&pwp->lock)); 71824c06356bSdh 71834c06356bSdh if (!target->ua) { 7184c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, target, 7185c3bc407cSdh "%s: target %p iport address is null", 71864c06356bSdh __func__, (void *)target); 71874c06356bSdh } 71884c06356bSdh 71894c06356bSdh iport = pmcs_get_iport_by_ua(pwp, target->ua); 71904c06356bSdh if (iport == NULL) { 7191c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, target, 71924c06356bSdh "%s: no iport associated with tgt(0x%p)", 71934c06356bSdh __func__, (void *)target); 71944c06356bSdh return; 71954c06356bSdh } 71964c06356bSdh 7197c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, target, 71984c06356bSdh "%s: free target %p", __func__, (void *)target); 71994c06356bSdh if (target->ua) { 72004c06356bSdh strfree(target->ua); 72014c06356bSdh } 72024c06356bSdh 72034c06356bSdh mutex_destroy(&target->wqlock); 72044c06356bSdh mutex_destroy(&target->aqlock); 72054c06356bSdh mutex_destroy(&target->statlock); 72064c06356bSdh cv_destroy(&target->reset_cv); 72074c06356bSdh cv_destroy(&target->abort_cv); 72084c06356bSdh ddi_soft_state_bystr_fini(&target->lun_sstate); 72094c06356bSdh ddi_soft_state_bystr_free(iport->tgt_sstate, target->unit_address); 72104c06356bSdh pmcs_rele_iport(iport); 72114c06356bSdh } 72124c06356bSdh 72134c06356bSdh /* 72144c06356bSdh * pmcs_lock_phy_impl 72154c06356bSdh * 72164c06356bSdh * This function is what does the actual work for pmcs_lock_phy. It will 72174c06356bSdh * lock all PHYs from phyp down in a top-down fashion. 72184c06356bSdh * 72194c06356bSdh * Locking notes: 72204c06356bSdh * 1. level starts from 0 for the PHY ("parent") that's passed in. It is 72214c06356bSdh * not a reflection of the actual level of the PHY in the SAS topology. 72224c06356bSdh * 2. If parent is an expander, then parent is locked along with all its 72234c06356bSdh * descendents. 72244c06356bSdh * 3. Expander subsidiary PHYs at level 0 are not locked. It is the 72254c06356bSdh * responsibility of the caller to individually lock expander subsidiary PHYs 72264c06356bSdh * at level 0 if necessary. 72274c06356bSdh * 4. Siblings at level 0 are not traversed due to the possibility that we're 72284c06356bSdh * locking a PHY on the dead list. The siblings could be pointing to invalid 72294c06356bSdh * PHYs. We don't lock siblings at level 0 anyway. 72304c06356bSdh */ 72314c06356bSdh static void 72324c06356bSdh pmcs_lock_phy_impl(pmcs_phy_t *phyp, int level) 72334c06356bSdh { 72344c06356bSdh pmcs_phy_t *tphyp; 72354c06356bSdh 72364c06356bSdh ASSERT((phyp->dtype == SAS) || (phyp->dtype == SATA) || 72374c06356bSdh (phyp->dtype == EXPANDER) || (phyp->dtype == NOTHING)); 72384c06356bSdh 72394c06356bSdh /* 72404c06356bSdh * Start walking the PHYs. 72414c06356bSdh */ 72424c06356bSdh tphyp = phyp; 72434c06356bSdh while (tphyp) { 72444c06356bSdh /* 72454c06356bSdh * If we're at the top level, only lock ourselves. For anything 72464c06356bSdh * at level > 0, traverse children while locking everything. 72474c06356bSdh */ 72484c06356bSdh if ((level > 0) || (tphyp == phyp)) { 7249c3bc407cSdh pmcs_prt(tphyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, tphyp, 7250c3bc407cSdh NULL, "%s: PHY 0x%p parent 0x%p path %s lvl %d", 72514c06356bSdh __func__, (void *)tphyp, (void *)tphyp->parent, 72524c06356bSdh tphyp->path, level); 72534c06356bSdh mutex_enter(&tphyp->phy_lock); 72544c06356bSdh 72554c06356bSdh if (tphyp->children) { 72564c06356bSdh pmcs_lock_phy_impl(tphyp->children, level + 1); 72574c06356bSdh } 72584c06356bSdh } 72594c06356bSdh 72604c06356bSdh if (level == 0) { 72614c06356bSdh return; 72624c06356bSdh } 72634c06356bSdh 72644c06356bSdh tphyp = tphyp->sibling; 72654c06356bSdh } 72664c06356bSdh } 72674c06356bSdh 72684c06356bSdh /* 72694c06356bSdh * pmcs_lock_phy 72704c06356bSdh * 72714c06356bSdh * This function is responsible for locking a PHY and all its descendents 72724c06356bSdh */ 72734c06356bSdh void 72744c06356bSdh pmcs_lock_phy(pmcs_phy_t *phyp) 72754c06356bSdh { 72764c06356bSdh #ifdef DEBUG 72774c06356bSdh char *callername = NULL; 72784c06356bSdh ulong_t off; 72794c06356bSdh 72804c06356bSdh ASSERT(phyp != NULL); 72814c06356bSdh 72824c06356bSdh callername = modgetsymname((uintptr_t)caller(), &off); 72834c06356bSdh 72844c06356bSdh if (callername == NULL) { 7285c3bc407cSdh pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL, 72864c06356bSdh "%s: PHY 0x%p path %s caller: unknown", __func__, 72874c06356bSdh (void *)phyp, phyp->path); 72884c06356bSdh } else { 7289c3bc407cSdh pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL, 72904c06356bSdh "%s: PHY 0x%p path %s caller: %s+%lx", __func__, 72914c06356bSdh (void *)phyp, phyp->path, callername, off); 72924c06356bSdh } 72934c06356bSdh #else 7294c3bc407cSdh pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL, 72954c06356bSdh "%s: PHY 0x%p path %s", __func__, (void *)phyp, phyp->path); 72964c06356bSdh #endif 72974c06356bSdh pmcs_lock_phy_impl(phyp, 0); 72984c06356bSdh } 72994c06356bSdh 73004c06356bSdh /* 73014c06356bSdh * pmcs_unlock_phy_impl 73024c06356bSdh * 73034c06356bSdh * Unlock all PHYs from phyp down in a bottom-up fashion. 73044c06356bSdh */ 73054c06356bSdh static void 73064c06356bSdh pmcs_unlock_phy_impl(pmcs_phy_t *phyp, int level) 73074c06356bSdh { 73084c06356bSdh pmcs_phy_t *phy_next; 73094c06356bSdh 73104c06356bSdh ASSERT((phyp->dtype == SAS) || (phyp->dtype == SATA) || 73114c06356bSdh (phyp->dtype == EXPANDER) || (phyp->dtype == NOTHING)); 73124c06356bSdh 73134c06356bSdh /* 73144c06356bSdh * Recurse down to the bottom PHYs 73154c06356bSdh */ 73164c06356bSdh if (level == 0) { 73174c06356bSdh if (phyp->children) { 73184c06356bSdh pmcs_unlock_phy_impl(phyp->children, level + 1); 73194c06356bSdh } 73204c06356bSdh } else { 73214c06356bSdh phy_next = phyp; 73224c06356bSdh while (phy_next) { 73234c06356bSdh if (phy_next->children) { 73244c06356bSdh pmcs_unlock_phy_impl(phy_next->children, 73254c06356bSdh level + 1); 73264c06356bSdh } 73274c06356bSdh phy_next = phy_next->sibling; 73284c06356bSdh } 73294c06356bSdh } 73304c06356bSdh 73314c06356bSdh /* 73324c06356bSdh * Iterate through PHYs unlocking all at level > 0 as well the top PHY 73334c06356bSdh */ 73344c06356bSdh phy_next = phyp; 73354c06356bSdh while (phy_next) { 73364c06356bSdh if ((level > 0) || (phy_next == phyp)) { 73374c06356bSdh pmcs_prt(phy_next->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, 7338c3bc407cSdh phy_next, NULL, 73394c06356bSdh "%s: PHY 0x%p parent 0x%p path %s lvl %d", 73404c06356bSdh __func__, (void *)phy_next, 73414c06356bSdh (void *)phy_next->parent, phy_next->path, level); 73424c06356bSdh mutex_exit(&phy_next->phy_lock); 73434c06356bSdh } 73444c06356bSdh 73454c06356bSdh if (level == 0) { 73464c06356bSdh return; 73474c06356bSdh } 73484c06356bSdh 73494c06356bSdh phy_next = phy_next->sibling; 73504c06356bSdh } 73514c06356bSdh } 73524c06356bSdh 73534c06356bSdh /* 73544c06356bSdh * pmcs_unlock_phy 73554c06356bSdh * 73564c06356bSdh * Unlock a PHY and all its descendents 73574c06356bSdh */ 73584c06356bSdh void 73594c06356bSdh pmcs_unlock_phy(pmcs_phy_t *phyp) 73604c06356bSdh { 73614c06356bSdh #ifdef DEBUG 73624c06356bSdh char *callername = NULL; 73634c06356bSdh ulong_t off; 73644c06356bSdh 73654c06356bSdh ASSERT(phyp != NULL); 73664c06356bSdh 73674c06356bSdh callername = modgetsymname((uintptr_t)caller(), &off); 73684c06356bSdh 73694c06356bSdh if (callername == NULL) { 7370c3bc407cSdh pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL, 73714c06356bSdh "%s: PHY 0x%p path %s caller: unknown", __func__, 73724c06356bSdh (void *)phyp, phyp->path); 73734c06356bSdh } else { 7374c3bc407cSdh pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL, 73754c06356bSdh "%s: PHY 0x%p path %s caller: %s+%lx", __func__, 73764c06356bSdh (void *)phyp, phyp->path, callername, off); 73774c06356bSdh } 73784c06356bSdh #else 7379c3bc407cSdh pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL, 73804c06356bSdh "%s: PHY 0x%p path %s", __func__, (void *)phyp, phyp->path); 73814c06356bSdh #endif 73824c06356bSdh pmcs_unlock_phy_impl(phyp, 0); 73834c06356bSdh } 73844c06356bSdh 73854c06356bSdh /* 73864c06356bSdh * pmcs_get_root_phy 73874c06356bSdh * 73884c06356bSdh * For a given phy pointer return its root phy. 73899aed1621SDavid Hollister * This function must only be called during discovery in order to ensure that 73909aed1621SDavid Hollister * the chain of PHYs from phyp up to the root PHY doesn't change. 73914c06356bSdh */ 73924c06356bSdh pmcs_phy_t * 73934c06356bSdh pmcs_get_root_phy(pmcs_phy_t *phyp) 73944c06356bSdh { 73954c06356bSdh ASSERT(phyp); 73964c06356bSdh 73974c06356bSdh while (phyp) { 73984c06356bSdh if (IS_ROOT_PHY(phyp)) { 73994c06356bSdh break; 74004c06356bSdh } 74014c06356bSdh phyp = phyp->parent; 74024c06356bSdh } 74034c06356bSdh 74044c06356bSdh return (phyp); 74054c06356bSdh } 74064c06356bSdh 74074c06356bSdh /* 74084c06356bSdh * pmcs_free_dma_chunklist 74094c06356bSdh * 74104c06356bSdh * Free DMA S/G chunk list 74114c06356bSdh */ 74124c06356bSdh void 74134c06356bSdh pmcs_free_dma_chunklist(pmcs_hw_t *pwp) 74144c06356bSdh { 74154c06356bSdh pmcs_chunk_t *pchunk; 74164c06356bSdh 74174c06356bSdh while (pwp->dma_chunklist) { 74184c06356bSdh pchunk = pwp->dma_chunklist; 74194c06356bSdh pwp->dma_chunklist = pwp->dma_chunklist->next; 74204c06356bSdh if (pchunk->dma_handle) { 74214c06356bSdh if (ddi_dma_unbind_handle(pchunk->dma_handle) != 74224c06356bSdh DDI_SUCCESS) { 7423c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 7424c3bc407cSdh "Condition failed at %s():%d", 7425c3bc407cSdh __func__, __LINE__); 74264c06356bSdh } 74274c06356bSdh ddi_dma_free_handle(&pchunk->dma_handle); 74284c06356bSdh ddi_dma_mem_free(&pchunk->acc_handle); 74294c06356bSdh } 74304c06356bSdh kmem_free(pchunk, sizeof (pmcs_chunk_t)); 74314c06356bSdh } 74324c06356bSdh } 74334c06356bSdh 74344c06356bSdh /*ARGSUSED2*/ 74354c06356bSdh int 74364c06356bSdh pmcs_phy_constructor(void *buf, void *arg, int kmflags) 74374c06356bSdh { 74384c06356bSdh pmcs_hw_t *pwp = (pmcs_hw_t *)arg; 74394c06356bSdh pmcs_phy_t *phyp = (pmcs_phy_t *)buf; 74404c06356bSdh 74414c06356bSdh mutex_init(&phyp->phy_lock, NULL, MUTEX_DRIVER, 74424c06356bSdh DDI_INTR_PRI(pwp->intr_pri)); 74434c06356bSdh cv_init(&phyp->abort_all_cv, NULL, CV_DRIVER, NULL); 74444c06356bSdh return (0); 74454c06356bSdh } 74464c06356bSdh 74474c06356bSdh /*ARGSUSED1*/ 74484c06356bSdh void 74494c06356bSdh pmcs_phy_destructor(void *buf, void *arg) 74504c06356bSdh { 74514c06356bSdh pmcs_phy_t *phyp = (pmcs_phy_t *)buf; 74524c06356bSdh 74534c06356bSdh cv_destroy(&phyp->abort_all_cv); 74544c06356bSdh mutex_destroy(&phyp->phy_lock); 74554c06356bSdh } 74564c06356bSdh 74574c06356bSdh /* 74584c06356bSdh * Free all PHYs from the kmem_cache starting at phyp as well as everything 74594c06356bSdh * on the dead_phys list. 74604c06356bSdh * 74614c06356bSdh * NOTE: This function does not free root PHYs as they are not allocated 74624c06356bSdh * from the kmem_cache. 74634c06356bSdh * 74644c06356bSdh * No PHY locks are acquired as this should only be called during DDI_DETACH 74654c06356bSdh * or soft reset (while pmcs interrupts are disabled). 74664c06356bSdh */ 74674c06356bSdh void 74684c06356bSdh pmcs_free_all_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp) 74694c06356bSdh { 74701b94a41bSChris Horne pmcs_phy_t *tphyp, *nphyp, *cphyp; 74714c06356bSdh 74724c06356bSdh if (phyp == NULL) { 74734c06356bSdh return; 74744c06356bSdh } 74754c06356bSdh 74761b94a41bSChris Horne for (tphyp = phyp; tphyp; tphyp = nphyp) { 74774c06356bSdh nphyp = tphyp->sibling; 74781b94a41bSChris Horne cphyp = tphyp->children; 74794c06356bSdh 74801b94a41bSChris Horne if (cphyp) { 74814c06356bSdh tphyp->children = NULL; 74821b94a41bSChris Horne pmcs_free_all_phys(pwp, cphyp); 74834c06356bSdh } 74841b94a41bSChris Horne 74854c06356bSdh if (!IS_ROOT_PHY(tphyp)) { 74864c06356bSdh kmem_cache_free(pwp->phy_cache, tphyp); 74874c06356bSdh } 74884c06356bSdh } 74894c06356bSdh 7490978d7443SSrikanth Suravajhala mutex_enter(&pwp->dead_phylist_lock); 74911b94a41bSChris Horne for (tphyp = pwp->dead_phys; tphyp; tphyp = nphyp) { 7492978d7443SSrikanth Suravajhala nphyp = tphyp->dead_next; 74934c06356bSdh kmem_cache_free(pwp->phy_cache, tphyp); 74944c06356bSdh } 74954c06356bSdh pwp->dead_phys = NULL; 7496978d7443SSrikanth Suravajhala mutex_exit(&pwp->dead_phylist_lock); 74974c06356bSdh } 74984c06356bSdh 74994c06356bSdh /* 75004c06356bSdh * Free a list of PHYs linked together by the sibling pointer back to the 75014c06356bSdh * kmem cache from whence they came. This function does not recurse, so the 75024c06356bSdh * caller must ensure there are no children. 75034c06356bSdh */ 75044c06356bSdh void 75054c06356bSdh pmcs_free_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp) 75064c06356bSdh { 75074c06356bSdh pmcs_phy_t *next_phy; 75084c06356bSdh 75094c06356bSdh while (phyp) { 75104c06356bSdh next_phy = phyp->sibling; 75114c06356bSdh ASSERT(!mutex_owned(&phyp->phy_lock)); 75124c06356bSdh kmem_cache_free(pwp->phy_cache, phyp); 75134c06356bSdh phyp = next_phy; 75144c06356bSdh } 75154c06356bSdh } 75164c06356bSdh 75174c06356bSdh /* 75184c06356bSdh * Make a copy of an existing PHY structure. This is used primarily in 75194c06356bSdh * discovery to compare the contents of an existing PHY with what gets 75204c06356bSdh * reported back by an expander. 75214c06356bSdh * 75224c06356bSdh * This function must not be called from any context where sleeping is 75234c06356bSdh * not possible. 75244c06356bSdh * 75254c06356bSdh * The new PHY is returned unlocked. 75264c06356bSdh */ 75274c06356bSdh static pmcs_phy_t * 75284c06356bSdh pmcs_clone_phy(pmcs_phy_t *orig_phy) 75294c06356bSdh { 75304c06356bSdh pmcs_phy_t *local; 75314c06356bSdh 75324c06356bSdh local = kmem_cache_alloc(orig_phy->pwp->phy_cache, KM_SLEEP); 75334c06356bSdh 75344c06356bSdh /* 75354c06356bSdh * Go ahead and just copy everything... 75364c06356bSdh */ 75374c06356bSdh *local = *orig_phy; 75384c06356bSdh 75394c06356bSdh /* 75404c06356bSdh * But the following must be set appropriately for this copy 75414c06356bSdh */ 75424c06356bSdh local->sibling = NULL; 75434c06356bSdh local->children = NULL; 75444c06356bSdh mutex_init(&local->phy_lock, NULL, MUTEX_DRIVER, 75454c06356bSdh DDI_INTR_PRI(orig_phy->pwp->intr_pri)); 75464c06356bSdh 75474c06356bSdh return (local); 75484c06356bSdh } 75494c06356bSdh 75504c06356bSdh int 75514c06356bSdh pmcs_check_acc_handle(ddi_acc_handle_t handle) 75524c06356bSdh { 75534c06356bSdh ddi_fm_error_t de; 75544c06356bSdh 75554c06356bSdh if (handle == NULL) { 75564c06356bSdh return (DDI_FAILURE); 75574c06356bSdh } 75584c06356bSdh ddi_fm_acc_err_get(handle, &de, DDI_FME_VER0); 75594c06356bSdh return (de.fme_status); 75604c06356bSdh } 75614c06356bSdh 75624c06356bSdh int 75634c06356bSdh pmcs_check_dma_handle(ddi_dma_handle_t handle) 75644c06356bSdh { 75654c06356bSdh ddi_fm_error_t de; 75664c06356bSdh 75674c06356bSdh if (handle == NULL) { 75684c06356bSdh return (DDI_FAILURE); 75694c06356bSdh } 75704c06356bSdh ddi_fm_dma_err_get(handle, &de, DDI_FME_VER0); 75714c06356bSdh return (de.fme_status); 75724c06356bSdh } 75734c06356bSdh 75744c06356bSdh 75754c06356bSdh void 75764c06356bSdh pmcs_fm_ereport(pmcs_hw_t *pwp, char *detail) 75774c06356bSdh { 75784c06356bSdh uint64_t ena; 75794c06356bSdh char buf[FM_MAX_CLASS]; 75804c06356bSdh 75814c06356bSdh (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", DDI_FM_DEVICE, detail); 75824c06356bSdh ena = fm_ena_generate(0, FM_ENA_FMT1); 75834c06356bSdh if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities)) { 75844c06356bSdh ddi_fm_ereport_post(pwp->dip, buf, ena, DDI_NOSLEEP, 75854c06356bSdh FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, NULL); 75864c06356bSdh } 75874c06356bSdh } 75884c06356bSdh 75894c06356bSdh int 75904c06356bSdh pmcs_check_acc_dma_handle(pmcs_hw_t *pwp) 75914c06356bSdh { 75924c06356bSdh pmcs_chunk_t *pchunk; 75934c06356bSdh int i; 75944c06356bSdh 75954c06356bSdh /* check all acc & dma handles allocated in attach */ 75964c06356bSdh if ((pmcs_check_acc_handle(pwp->pci_acc_handle) != DDI_SUCCESS) || 75974c06356bSdh (pmcs_check_acc_handle(pwp->msg_acc_handle) != DDI_SUCCESS) || 75984c06356bSdh (pmcs_check_acc_handle(pwp->top_acc_handle) != DDI_SUCCESS) || 75994c06356bSdh (pmcs_check_acc_handle(pwp->mpi_acc_handle) != DDI_SUCCESS) || 76004c06356bSdh (pmcs_check_acc_handle(pwp->gsm_acc_handle) != DDI_SUCCESS)) { 76014c06356bSdh goto check_failed; 76024c06356bSdh } 76034c06356bSdh 76044c06356bSdh for (i = 0; i < PMCS_NIQ; i++) { 76054c06356bSdh if ((pmcs_check_dma_handle( 76064c06356bSdh pwp->iqp_handles[i]) != DDI_SUCCESS) || 76074c06356bSdh (pmcs_check_acc_handle( 76084c06356bSdh pwp->iqp_acchdls[i]) != DDI_SUCCESS)) { 76094c06356bSdh goto check_failed; 76104c06356bSdh } 76114c06356bSdh } 76124c06356bSdh 76134c06356bSdh for (i = 0; i < PMCS_NOQ; i++) { 76144c06356bSdh if ((pmcs_check_dma_handle( 76154c06356bSdh pwp->oqp_handles[i]) != DDI_SUCCESS) || 76164c06356bSdh (pmcs_check_acc_handle( 76174c06356bSdh pwp->oqp_acchdls[i]) != DDI_SUCCESS)) { 76184c06356bSdh goto check_failed; 76194c06356bSdh } 76204c06356bSdh } 76214c06356bSdh 76224c06356bSdh if ((pmcs_check_dma_handle(pwp->cip_handles) != DDI_SUCCESS) || 76234c06356bSdh (pmcs_check_acc_handle(pwp->cip_acchdls) != DDI_SUCCESS)) { 76244c06356bSdh goto check_failed; 76254c06356bSdh } 76264c06356bSdh 76274c06356bSdh if (pwp->fwlog && 76284c06356bSdh ((pmcs_check_dma_handle(pwp->fwlog_hndl) != DDI_SUCCESS) || 76294c06356bSdh (pmcs_check_acc_handle(pwp->fwlog_acchdl) != DDI_SUCCESS))) { 76304c06356bSdh goto check_failed; 76314c06356bSdh } 76324c06356bSdh 76334c06356bSdh if (pwp->regdump_hndl && pwp->regdump_acchdl && 76344c06356bSdh ((pmcs_check_dma_handle(pwp->regdump_hndl) != DDI_SUCCESS) || 76354c06356bSdh (pmcs_check_acc_handle(pwp->regdump_acchdl) 76364c06356bSdh != DDI_SUCCESS))) { 76374c06356bSdh goto check_failed; 76384c06356bSdh } 76394c06356bSdh 76404c06356bSdh 76414c06356bSdh pchunk = pwp->dma_chunklist; 76424c06356bSdh while (pchunk) { 76434c06356bSdh if ((pmcs_check_acc_handle(pchunk->acc_handle) 76444c06356bSdh != DDI_SUCCESS) || 76454c06356bSdh (pmcs_check_dma_handle(pchunk->dma_handle) 76464c06356bSdh != DDI_SUCCESS)) { 76474c06356bSdh goto check_failed; 76484c06356bSdh } 76494c06356bSdh pchunk = pchunk->next; 76504c06356bSdh } 76514c06356bSdh 76524c06356bSdh return (0); 76534c06356bSdh 76544c06356bSdh check_failed: 76554c06356bSdh 76564c06356bSdh return (1); 76574c06356bSdh } 76584c06356bSdh 76594c06356bSdh /* 76604c06356bSdh * pmcs_handle_dead_phys 76614c06356bSdh * 76624c06356bSdh * If the PHY has no outstanding work associated with it, remove it from 76634c06356bSdh * the dead PHY list and free it. 76644c06356bSdh * 76654c06356bSdh * If pwp->ds_err_recovering or pwp->configuring is set, don't run. 76664c06356bSdh * This keeps routines that need to submit work to the chip from having to 76674c06356bSdh * hold PHY locks to ensure that PHYs don't disappear while they do their work. 76684c06356bSdh */ 76694c06356bSdh void 76704c06356bSdh pmcs_handle_dead_phys(pmcs_hw_t *pwp) 76714c06356bSdh { 76724c06356bSdh pmcs_phy_t *phyp, *nphyp, *pphyp; 76734c06356bSdh 76744c06356bSdh mutex_enter(&pwp->lock); 76754c06356bSdh mutex_enter(&pwp->config_lock); 76764c06356bSdh 76774c06356bSdh if (pwp->configuring | pwp->ds_err_recovering) { 76784c06356bSdh mutex_exit(&pwp->config_lock); 76794c06356bSdh mutex_exit(&pwp->lock); 76804c06356bSdh return; 76814c06356bSdh } 76824c06356bSdh 76834c06356bSdh /* 76844c06356bSdh * Check every PHY in the dead PHY list 76854c06356bSdh */ 76864c06356bSdh mutex_enter(&pwp->dead_phylist_lock); 76874c06356bSdh phyp = pwp->dead_phys; 76884c06356bSdh pphyp = NULL; /* Set previous PHY to NULL */ 76894c06356bSdh 76904c06356bSdh while (phyp != NULL) { 76914c06356bSdh pmcs_lock_phy(phyp); 76924c06356bSdh ASSERT(phyp->dead); 76934c06356bSdh 76944c06356bSdh nphyp = phyp->dead_next; 76954c06356bSdh 76964c06356bSdh /* 76974c06356bSdh * Check for outstanding work 76984c06356bSdh */ 76994c06356bSdh if (phyp->ref_count > 0) { 77004c06356bSdh pmcs_unlock_phy(phyp); 77014c06356bSdh pphyp = phyp; /* This PHY becomes "previous" */ 77024c06356bSdh } else if (phyp->target) { 77034c06356bSdh pmcs_unlock_phy(phyp); 7704c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG1, phyp, phyp->target, 77054c06356bSdh "%s: Not freeing PHY 0x%p: target 0x%p is not free", 77064c06356bSdh __func__, (void *)phyp, (void *)phyp->target); 77074c06356bSdh pphyp = phyp; 77084c06356bSdh } else { 77094c06356bSdh /* 77104c06356bSdh * No outstanding work or target references. Remove it 77114c06356bSdh * from the list and free it 77124c06356bSdh */ 7713c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, phyp->target, 77144c06356bSdh "%s: Freeing inactive dead PHY 0x%p @ %s " 77154c06356bSdh "target = 0x%p", __func__, (void *)phyp, 77164c06356bSdh phyp->path, (void *)phyp->target); 77174c06356bSdh /* 77184c06356bSdh * If pphyp is NULL, then phyp was the head of the list, 77194c06356bSdh * so just reset the head to nphyp. Otherwise, the 77204c06356bSdh * previous PHY will now point to nphyp (the next PHY) 77214c06356bSdh */ 77224c06356bSdh if (pphyp == NULL) { 77234c06356bSdh pwp->dead_phys = nphyp; 77244c06356bSdh } else { 77254c06356bSdh pphyp->dead_next = nphyp; 77264c06356bSdh } 77274c06356bSdh /* 77284c06356bSdh * If the target still points to this PHY, remove 77294c06356bSdh * that linkage now. 77304c06356bSdh */ 77314c06356bSdh if (phyp->target) { 77324c06356bSdh mutex_enter(&phyp->target->statlock); 77334c06356bSdh if (phyp->target->phy == phyp) { 77344c06356bSdh phyp->target->phy = NULL; 77354c06356bSdh } 77364c06356bSdh mutex_exit(&phyp->target->statlock); 77374c06356bSdh } 7738b18a19c2SJesse Butler pmcs_unlock_phy(phyp); 77394c06356bSdh kmem_cache_free(pwp->phy_cache, phyp); 77404c06356bSdh } 77414c06356bSdh 77424c06356bSdh phyp = nphyp; 77434c06356bSdh } 77444c06356bSdh 77454c06356bSdh mutex_exit(&pwp->dead_phylist_lock); 77464c06356bSdh mutex_exit(&pwp->config_lock); 77474c06356bSdh mutex_exit(&pwp->lock); 77484c06356bSdh } 77494c06356bSdh 77504c06356bSdh void 77514c06356bSdh pmcs_inc_phy_ref_count(pmcs_phy_t *phyp) 77524c06356bSdh { 77534c06356bSdh atomic_inc_32(&phyp->ref_count); 77544c06356bSdh } 77554c06356bSdh 77564c06356bSdh void 77574c06356bSdh pmcs_dec_phy_ref_count(pmcs_phy_t *phyp) 77584c06356bSdh { 77594c06356bSdh ASSERT(phyp->ref_count != 0); 77604c06356bSdh atomic_dec_32(&phyp->ref_count); 77614c06356bSdh } 77624c06356bSdh 77634c06356bSdh /* 77644c06356bSdh * pmcs_reap_dead_phy 77654c06356bSdh * 77664c06356bSdh * This function is called from pmcs_new_tport when we have a PHY 77674c06356bSdh * without a target pointer. It's possible in that case that this PHY 77684c06356bSdh * may have a "brother" on the dead_phys list. That is, it may be the same as 77694c06356bSdh * this one but with a different root PHY number (e.g. pp05 vs. pp04). If 77704c06356bSdh * that's the case, update the dead PHY and this new PHY. If that's not the 77714c06356bSdh * case, we should get a tran_tgt_init on this after it's reported to SCSA. 77724c06356bSdh * 77734c06356bSdh * Called with PHY locked. 77744c06356bSdh */ 77754c06356bSdh static void 77764c06356bSdh pmcs_reap_dead_phy(pmcs_phy_t *phyp) 77774c06356bSdh { 77784c06356bSdh pmcs_hw_t *pwp = phyp->pwp; 77794c06356bSdh pmcs_phy_t *ctmp; 778073a3eccdSDavid Hollister pmcs_iport_t *iport_cmp; 77814c06356bSdh 77824c06356bSdh ASSERT(mutex_owned(&phyp->phy_lock)); 77834c06356bSdh 77844c06356bSdh /* 77854c06356bSdh * Check the dead PHYs list 77864c06356bSdh */ 77874c06356bSdh mutex_enter(&pwp->dead_phylist_lock); 77884c06356bSdh ctmp = pwp->dead_phys; 77894c06356bSdh while (ctmp) { 779073a3eccdSDavid Hollister /* 779173a3eccdSDavid Hollister * If the iport is NULL, compare against last_iport. 779273a3eccdSDavid Hollister */ 779373a3eccdSDavid Hollister if (ctmp->iport) { 779473a3eccdSDavid Hollister iport_cmp = ctmp->iport; 779573a3eccdSDavid Hollister } else { 779673a3eccdSDavid Hollister iport_cmp = ctmp->last_iport; 779773a3eccdSDavid Hollister } 779873a3eccdSDavid Hollister 779973a3eccdSDavid Hollister if ((iport_cmp != phyp->iport) || 78004c06356bSdh (memcmp((void *)&ctmp->sas_address[0], 78014c06356bSdh (void *)&phyp->sas_address[0], 8))) { 78024c06356bSdh ctmp = ctmp->dead_next; 78034c06356bSdh continue; 78044c06356bSdh } 78054c06356bSdh 78064c06356bSdh /* 78074c06356bSdh * Same SAS address on same iport. Now check to see if 78084c06356bSdh * the PHY path is the same with the possible exception 78094c06356bSdh * of the root PHY number. 78104c06356bSdh * The "5" is the string length of "pp00." 78114c06356bSdh */ 78124c06356bSdh if ((strnlen(phyp->path, 5) >= 5) && 78134c06356bSdh (strnlen(ctmp->path, 5) >= 5)) { 78144c06356bSdh if (memcmp((void *)&phyp->path[5], 78154c06356bSdh (void *)&ctmp->path[5], 78164c06356bSdh strnlen(phyp->path, 32) - 5) == 0) { 78174c06356bSdh break; 78184c06356bSdh } 78194c06356bSdh } 78204c06356bSdh 78214c06356bSdh ctmp = ctmp->dead_next; 78224c06356bSdh } 78234c06356bSdh mutex_exit(&pwp->dead_phylist_lock); 78244c06356bSdh 78254c06356bSdh /* 78264c06356bSdh * Found a match. Remove the target linkage and drop the 78274c06356bSdh * ref count on the old PHY. Then, increment the ref count 78284c06356bSdh * on the new PHY to compensate. 78294c06356bSdh */ 78304c06356bSdh if (ctmp) { 7831c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL, 783273a3eccdSDavid Hollister "%s: Found match in dead PHY list (0x%p) for new PHY %s", 783373a3eccdSDavid Hollister __func__, (void *)ctmp, phyp->path); 783473a3eccdSDavid Hollister /* 783573a3eccdSDavid Hollister * If there is a pointer to the target in the dead PHY, move 783673a3eccdSDavid Hollister * all reference counts to the new PHY. 783773a3eccdSDavid Hollister */ 78384c06356bSdh if (ctmp->target) { 783973a3eccdSDavid Hollister mutex_enter(&ctmp->target->statlock); 78404c06356bSdh phyp->target = ctmp->target; 784173a3eccdSDavid Hollister 784273a3eccdSDavid Hollister while (ctmp->ref_count != 0) { 784373a3eccdSDavid Hollister pmcs_inc_phy_ref_count(phyp); 784473a3eccdSDavid Hollister pmcs_dec_phy_ref_count(ctmp); 784573a3eccdSDavid Hollister } 78464c06356bSdh /* 78474c06356bSdh * Update the target's linkage as well 78484c06356bSdh */ 78494c06356bSdh phyp->target->phy = phyp; 78504c06356bSdh phyp->target->dtype = phyp->dtype; 785173a3eccdSDavid Hollister ctmp->target = NULL; 78524c06356bSdh mutex_exit(&phyp->target->statlock); 78534c06356bSdh } 78544c06356bSdh } 78554c06356bSdh } 78564c06356bSdh 78574c06356bSdh /* 78584c06356bSdh * Called with iport lock held 78594c06356bSdh */ 78604c06356bSdh void 78614c06356bSdh pmcs_add_phy_to_iport(pmcs_iport_t *iport, pmcs_phy_t *phyp) 78624c06356bSdh { 78634c06356bSdh ASSERT(mutex_owned(&iport->lock)); 78644c06356bSdh ASSERT(phyp); 78654c06356bSdh ASSERT(!list_link_active(&phyp->list_node)); 7866978d7443SSrikanth Suravajhala 78674c06356bSdh iport->nphy++; 7868145e0143Sdh list_insert_tail(&iport->phys, phyp); 78694c06356bSdh pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS, 78704c06356bSdh &iport->nphy); 787135dae232SSrikanth Suravajhala mutex_enter(&phyp->phy_lock); 787235dae232SSrikanth Suravajhala pmcs_create_one_phy_stats(iport, phyp); 787335dae232SSrikanth Suravajhala mutex_exit(&phyp->phy_lock); 7874*3be32c0fSJesse Butler pmcs_hold_iport(iport); 78754c06356bSdh } 78764c06356bSdh 78774c06356bSdh /* 78784c06356bSdh * Called with the iport lock held 78794c06356bSdh */ 78804c06356bSdh void 78814c06356bSdh pmcs_remove_phy_from_iport(pmcs_iport_t *iport, pmcs_phy_t *phyp) 78824c06356bSdh { 78834c06356bSdh pmcs_phy_t *pptr, *next_pptr; 78844c06356bSdh 78854c06356bSdh ASSERT(mutex_owned(&iport->lock)); 78864c06356bSdh 78874c06356bSdh /* 78884c06356bSdh * If phyp is NULL, remove all PHYs from the iport 78894c06356bSdh */ 78904c06356bSdh if (phyp == NULL) { 78914c06356bSdh for (pptr = list_head(&iport->phys); pptr != NULL; 78924c06356bSdh pptr = next_pptr) { 78934c06356bSdh next_pptr = list_next(&iport->phys, pptr); 78944c06356bSdh mutex_enter(&pptr->phy_lock); 789535dae232SSrikanth Suravajhala if (pptr->phy_stats != NULL) { 789635dae232SSrikanth Suravajhala kstat_delete(pptr->phy_stats); 789735dae232SSrikanth Suravajhala pptr->phy_stats = NULL; 789835dae232SSrikanth Suravajhala } 78994c06356bSdh pptr->iport = NULL; 79005c45adf0SJesse Butler pmcs_update_phy_pm_props(pptr, pptr->att_port_pm_tmp, 79015c45adf0SJesse Butler pptr->tgt_port_pm_tmp, B_FALSE); 79024c06356bSdh mutex_exit(&pptr->phy_lock); 79034c06356bSdh pmcs_rele_iport(iport); 79044c06356bSdh list_remove(&iport->phys, pptr); 79059aed1621SDavid Hollister pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, 79069aed1621SDavid Hollister PMCS_NUM_PHYS, &iport->nphy); 79074c06356bSdh } 79084c06356bSdh iport->nphy = 0; 79094c06356bSdh return; 79104c06356bSdh } 79114c06356bSdh 79124c06356bSdh ASSERT(phyp); 79134c06356bSdh ASSERT(iport->nphy > 0); 79144c06356bSdh ASSERT(list_link_active(&phyp->list_node)); 79154c06356bSdh iport->nphy--; 7916145e0143Sdh list_remove(&iport->phys, phyp); 7917499cfd15SDavid Hollister pmcs_update_phy_pm_props(phyp, phyp->att_port_pm_tmp, 7918499cfd15SDavid Hollister phyp->tgt_port_pm_tmp, B_FALSE); 79194c06356bSdh pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS, 79204c06356bSdh &iport->nphy); 79214c06356bSdh pmcs_rele_iport(iport); 79224c06356bSdh } 79234c06356bSdh 79244c06356bSdh /* 79254c06356bSdh * This function checks to see if the target pointed to by phyp is still 79264c06356bSdh * correct. This is done by comparing the target's unit address with the 79274c06356bSdh * SAS address in phyp. 79284c06356bSdh * 79294c06356bSdh * Called with PHY locked and target statlock held 79304c06356bSdh */ 79314c06356bSdh static boolean_t 79324c06356bSdh pmcs_phy_target_match(pmcs_phy_t *phyp) 79334c06356bSdh { 79344c06356bSdh uint64_t wwn; 79354c06356bSdh char unit_address[PMCS_MAX_UA_SIZE]; 79364c06356bSdh boolean_t rval = B_FALSE; 79374c06356bSdh 79384c06356bSdh ASSERT(phyp); 79394c06356bSdh ASSERT(phyp->target); 79404c06356bSdh ASSERT(mutex_owned(&phyp->phy_lock)); 79414c06356bSdh ASSERT(mutex_owned(&phyp->target->statlock)); 79424c06356bSdh 79434c06356bSdh wwn = pmcs_barray2wwn(phyp->sas_address); 79444c06356bSdh (void) scsi_wwn_to_wwnstr(wwn, 1, unit_address); 79454c06356bSdh 79464c06356bSdh if (memcmp((void *)unit_address, (void *)phyp->target->unit_address, 79474c06356bSdh strnlen(phyp->target->unit_address, PMCS_MAX_UA_SIZE)) == 0) { 79484c06356bSdh rval = B_TRUE; 79494c06356bSdh } 79504c06356bSdh 79514c06356bSdh return (rval); 79524c06356bSdh } 79536745c559SJesse Butler /* 79546745c559SJesse Butler * Commands used to serialize SMP requests. 79556745c559SJesse Butler * 79566745c559SJesse Butler * The SPC only allows 2 SMP commands per SMP target: 1 cmd pending and 1 cmd 79576745c559SJesse Butler * queued for the same SMP target. If a third SMP cmd is sent to the SPC for an 79586745c559SJesse Butler * SMP target that already has a SMP cmd pending and one queued, then the 79596745c559SJesse Butler * SPC responds with the ERROR_INTERNAL_SMP_RESOURCE response. 79606745c559SJesse Butler * 79616745c559SJesse Butler * Additionally, the SPC has an 8 entry deep cmd queue and the number of SMP 79626745c559SJesse Butler * cmds that can be queued is controlled by the PORT_CONTROL IOMB. The 79636745c559SJesse Butler * SPC default is 1 SMP command/port (iport). These 2 queued SMP cmds would 79646745c559SJesse Butler * have to be for different SMP targets. The INTERNAL_SMP_RESOURCE error will 79656745c559SJesse Butler * also be returned if a 2nd SMP cmd is sent to the controller when there is 79666745c559SJesse Butler * already 1 SMP cmd queued for that port or if a 3rd SMP cmd is sent to the 79676745c559SJesse Butler * queue if there are already 2 queued SMP cmds. 79686745c559SJesse Butler */ 79696745c559SJesse Butler void 79706745c559SJesse Butler pmcs_smp_acquire(pmcs_iport_t *iport) 79716745c559SJesse Butler { 79726745c559SJesse Butler if (iport == NULL) { 79736745c559SJesse Butler return; 79746745c559SJesse Butler } 79756745c559SJesse Butler 79766745c559SJesse Butler mutex_enter(&iport->smp_lock); 79776745c559SJesse Butler while (iport->smp_active) { 79786745c559SJesse Butler pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL, 79796745c559SJesse Butler "%s: SMP is active on thread 0x%p, waiting", __func__, 79806745c559SJesse Butler (void *)iport->smp_active_thread); 79816745c559SJesse Butler cv_wait(&iport->smp_cv, &iport->smp_lock); 79826745c559SJesse Butler } 79836745c559SJesse Butler iport->smp_active = B_TRUE; 79846745c559SJesse Butler iport->smp_active_thread = curthread; 79859aed1621SDavid Hollister pmcs_prt(iport->pwp, PMCS_PRT_DEBUG3, NULL, NULL, 79866745c559SJesse Butler "%s: SMP acquired by thread 0x%p", __func__, 79876745c559SJesse Butler (void *)iport->smp_active_thread); 79886745c559SJesse Butler mutex_exit(&iport->smp_lock); 79896745c559SJesse Butler } 79906745c559SJesse Butler 79916745c559SJesse Butler void 79926745c559SJesse Butler pmcs_smp_release(pmcs_iport_t *iport) 79936745c559SJesse Butler { 79946745c559SJesse Butler if (iport == NULL) { 79956745c559SJesse Butler return; 79966745c559SJesse Butler } 79976745c559SJesse Butler 79986745c559SJesse Butler mutex_enter(&iport->smp_lock); 79999aed1621SDavid Hollister pmcs_prt(iport->pwp, PMCS_PRT_DEBUG3, NULL, NULL, 80006745c559SJesse Butler "%s: SMP released by thread 0x%p", __func__, (void *)curthread); 80016745c559SJesse Butler iport->smp_active = B_FALSE; 80026745c559SJesse Butler iport->smp_active_thread = NULL; 80036745c559SJesse Butler cv_signal(&iport->smp_cv); 80046745c559SJesse Butler mutex_exit(&iport->smp_lock); 80056745c559SJesse Butler } 8006499cfd15SDavid Hollister 8007499cfd15SDavid Hollister /* 8008499cfd15SDavid Hollister * Update a PHY's attached-port-pm and target-port-pm properties 8009499cfd15SDavid Hollister * 8010499cfd15SDavid Hollister * phyp: PHY whose properties are to be updated 8011499cfd15SDavid Hollister * 8012499cfd15SDavid Hollister * att_bv: Bit value of the attached-port-pm property to be updated in the 8013499cfd15SDavid Hollister * 64-bit holding area for the PHY. 8014499cfd15SDavid Hollister * 8015499cfd15SDavid Hollister * tgt_bv: Bit value of the target-port-pm property to update in the 64-bit 8016499cfd15SDavid Hollister * holding area for the PHY. 8017499cfd15SDavid Hollister * 8018499cfd15SDavid Hollister * prop_add_val: If TRUE, we're adding bits into the property value. 8019499cfd15SDavid Hollister * Otherwise, we're taking them out. Either way, the properties for this 8020499cfd15SDavid Hollister * PHY will be updated. 8021499cfd15SDavid Hollister */ 8022499cfd15SDavid Hollister void 8023499cfd15SDavid Hollister pmcs_update_phy_pm_props(pmcs_phy_t *phyp, uint64_t att_bv, uint64_t tgt_bv, 8024499cfd15SDavid Hollister boolean_t prop_add_val) 8025499cfd15SDavid Hollister { 8026499cfd15SDavid Hollister if (prop_add_val) { 8027499cfd15SDavid Hollister /* 8028499cfd15SDavid Hollister * If the values are currently 0, then we're setting the 8029499cfd15SDavid Hollister * phymask for just this PHY as well. 8030499cfd15SDavid Hollister */ 8031499cfd15SDavid Hollister if (phyp->att_port_pm_tmp == 0) { 8032616875b4SDavid Hollister phyp->att_port_pm = att_bv; 8033616875b4SDavid Hollister phyp->tgt_port_pm = tgt_bv; 8034499cfd15SDavid Hollister } 8035616875b4SDavid Hollister phyp->att_port_pm_tmp |= att_bv; 8036616875b4SDavid Hollister phyp->tgt_port_pm_tmp |= tgt_bv; 8037499cfd15SDavid Hollister (void) snprintf(phyp->att_port_pm_str, PMCS_PM_MAX_NAMELEN, 8038499cfd15SDavid Hollister "%"PRIx64, phyp->att_port_pm_tmp); 8039499cfd15SDavid Hollister (void) snprintf(phyp->tgt_port_pm_str, PMCS_PM_MAX_NAMELEN, 8040499cfd15SDavid Hollister "%"PRIx64, phyp->tgt_port_pm_tmp); 8041499cfd15SDavid Hollister } else { 8042616875b4SDavid Hollister phyp->att_port_pm_tmp &= ~att_bv; 8043616875b4SDavid Hollister phyp->tgt_port_pm_tmp &= ~tgt_bv; 8044499cfd15SDavid Hollister if (phyp->att_port_pm_tmp) { 8045499cfd15SDavid Hollister (void) snprintf(phyp->att_port_pm_str, 8046499cfd15SDavid Hollister PMCS_PM_MAX_NAMELEN, "%"PRIx64, 8047499cfd15SDavid Hollister phyp->att_port_pm_tmp); 8048499cfd15SDavid Hollister } else { 8049499cfd15SDavid Hollister phyp->att_port_pm_str[0] = '\0'; 8050499cfd15SDavid Hollister phyp->att_port_pm = 0; 8051499cfd15SDavid Hollister } 8052499cfd15SDavid Hollister if (phyp->tgt_port_pm_tmp) { 8053499cfd15SDavid Hollister (void) snprintf(phyp->tgt_port_pm_str, 8054499cfd15SDavid Hollister PMCS_PM_MAX_NAMELEN, "%"PRIx64, 8055499cfd15SDavid Hollister phyp->tgt_port_pm_tmp); 8056499cfd15SDavid Hollister } else { 8057499cfd15SDavid Hollister phyp->tgt_port_pm_str[0] = '\0'; 8058499cfd15SDavid Hollister phyp->tgt_port_pm = 0; 8059499cfd15SDavid Hollister } 8060499cfd15SDavid Hollister } 8061499cfd15SDavid Hollister 8062499cfd15SDavid Hollister if (phyp->target == NULL) { 8063499cfd15SDavid Hollister return; 8064499cfd15SDavid Hollister } 8065499cfd15SDavid Hollister 806673a3eccdSDavid Hollister mutex_enter(&phyp->target->statlock); 806773a3eccdSDavid Hollister if (!list_is_empty(&phyp->target->lun_list)) { 806873a3eccdSDavid Hollister pmcs_lun_t *lunp; 806973a3eccdSDavid Hollister 807073a3eccdSDavid Hollister lunp = list_head(&phyp->target->lun_list); 807173a3eccdSDavid Hollister while (lunp) { 807273a3eccdSDavid Hollister (void) scsi_device_prop_update_string(lunp->sd, 807373a3eccdSDavid Hollister SCSI_DEVICE_PROP_PATH, 807473a3eccdSDavid Hollister SCSI_ADDR_PROP_ATTACHED_PORT_PM, 807573a3eccdSDavid Hollister phyp->att_port_pm_str); 807673a3eccdSDavid Hollister (void) scsi_device_prop_update_string(lunp->sd, 807773a3eccdSDavid Hollister SCSI_DEVICE_PROP_PATH, 807873a3eccdSDavid Hollister SCSI_ADDR_PROP_TARGET_PORT_PM, 807973a3eccdSDavid Hollister phyp->tgt_port_pm_str); 808073a3eccdSDavid Hollister lunp = list_next(&phyp->target->lun_list, lunp); 808173a3eccdSDavid Hollister } 8082499cfd15SDavid Hollister } else if (phyp->target->smpd) { 8083499cfd15SDavid Hollister (void) smp_device_prop_update_string(phyp->target->smpd, 8084499cfd15SDavid Hollister SCSI_ADDR_PROP_ATTACHED_PORT_PM, 8085499cfd15SDavid Hollister phyp->att_port_pm_str); 8086499cfd15SDavid Hollister (void) smp_device_prop_update_string(phyp->target->smpd, 8087499cfd15SDavid Hollister SCSI_ADDR_PROP_TARGET_PORT_PM, 8088499cfd15SDavid Hollister phyp->tgt_port_pm_str); 8089499cfd15SDavid Hollister } 809073a3eccdSDavid Hollister mutex_exit(&phyp->target->statlock); 8091499cfd15SDavid Hollister } 8092601c90f1SSrikanth, Ramana 8093601c90f1SSrikanth, Ramana /* ARGSUSED */ 8094601c90f1SSrikanth, Ramana void 8095601c90f1SSrikanth, Ramana pmcs_deregister_device_work(pmcs_hw_t *pwp, pmcs_phy_t *phyp) 8096601c90f1SSrikanth, Ramana { 8097601c90f1SSrikanth, Ramana pmcs_phy_t *pptr; 8098601c90f1SSrikanth, Ramana 8099601c90f1SSrikanth, Ramana for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) { 8100601c90f1SSrikanth, Ramana pmcs_lock_phy(pptr); 8101601c90f1SSrikanth, Ramana if (pptr->deregister_wait) { 8102601c90f1SSrikanth, Ramana pmcs_deregister_device(pwp, pptr); 8103601c90f1SSrikanth, Ramana } 8104601c90f1SSrikanth, Ramana pmcs_unlock_phy(pptr); 8105601c90f1SSrikanth, Ramana } 8106601c90f1SSrikanth, Ramana } 81079aed1621SDavid Hollister 81089aed1621SDavid Hollister /* 81099aed1621SDavid Hollister * pmcs_iport_active 81109aed1621SDavid Hollister * 81119aed1621SDavid Hollister * Mark this iport as active. Called with the iport lock held. 81129aed1621SDavid Hollister */ 81139aed1621SDavid Hollister static void 81149aed1621SDavid Hollister pmcs_iport_active(pmcs_iport_t *iport) 81159aed1621SDavid Hollister { 81169aed1621SDavid Hollister ASSERT(mutex_owned(&iport->lock)); 81179aed1621SDavid Hollister 81189aed1621SDavid Hollister iport->ua_state = UA_ACTIVE; 81199aed1621SDavid Hollister iport->smp_active = B_FALSE; 81209aed1621SDavid Hollister iport->smp_active_thread = NULL; 81219aed1621SDavid Hollister } 81229aed1621SDavid Hollister 81239aed1621SDavid Hollister /* ARGSUSED */ 81249aed1621SDavid Hollister static void 81259aed1621SDavid Hollister pmcs_tgtmap_activate_cb(void *tgtmap_priv, char *tgt_addr, 81269aed1621SDavid Hollister scsi_tgtmap_tgt_type_t tgt_type, void **tgt_privp) 81279aed1621SDavid Hollister { 81289aed1621SDavid Hollister pmcs_iport_t *iport = (pmcs_iport_t *)tgtmap_priv; 81295c45adf0SJesse Butler pmcs_hw_t *pwp = iport->pwp; 81305c45adf0SJesse Butler pmcs_xscsi_t *target; 81319aed1621SDavid Hollister 81325c45adf0SJesse Butler /* 81335c45adf0SJesse Butler * Look up the target. If there is one, and it doesn't have a PHY 81345c45adf0SJesse Butler * pointer, re-establish that linkage here. 81355c45adf0SJesse Butler */ 81365c45adf0SJesse Butler mutex_enter(&pwp->lock); 81375c45adf0SJesse Butler target = pmcs_get_target(iport, tgt_addr, B_FALSE); 81385c45adf0SJesse Butler mutex_exit(&pwp->lock); 81395c45adf0SJesse Butler 81405c45adf0SJesse Butler /* 81415c45adf0SJesse Butler * If we got a target, it will now have a PHY pointer and the PHY 81425c45adf0SJesse Butler * will point to the target. The PHY will be locked, so we'll need 81435c45adf0SJesse Butler * to unlock it. 81445c45adf0SJesse Butler */ 8145*3be32c0fSJesse Butler if (target != NULL) { 81465c45adf0SJesse Butler pmcs_unlock_phy(target->phy); 81475c45adf0SJesse Butler } 81489aed1621SDavid Hollister 81499aed1621SDavid Hollister /* 81509aed1621SDavid Hollister * Update config_restart_time so we don't try to restart discovery 81519aed1621SDavid Hollister * while enumeration is still in progress. 81529aed1621SDavid Hollister */ 81535c45adf0SJesse Butler mutex_enter(&pwp->config_lock); 81545c45adf0SJesse Butler pwp->config_restart_time = ddi_get_lbolt() + 81559aed1621SDavid Hollister drv_usectohz(PMCS_REDISCOVERY_DELAY); 81565c45adf0SJesse Butler mutex_exit(&pwp->config_lock); 81579aed1621SDavid Hollister } 81589aed1621SDavid Hollister 81599aed1621SDavid Hollister /* ARGSUSED */ 81609aed1621SDavid Hollister static boolean_t 81619aed1621SDavid Hollister pmcs_tgtmap_deactivate_cb(void *tgtmap_priv, char *tgt_addr, 81629aed1621SDavid Hollister scsi_tgtmap_tgt_type_t tgt_type, void *tgt_priv, 81639aed1621SDavid Hollister scsi_tgtmap_deact_rsn_t tgt_deact_rsn) 81649aed1621SDavid Hollister { 81659aed1621SDavid Hollister pmcs_iport_t *iport = (pmcs_iport_t *)tgtmap_priv; 81669aed1621SDavid Hollister pmcs_phy_t *phyp; 81679aed1621SDavid Hollister boolean_t rediscover = B_FALSE; 81689aed1621SDavid Hollister 81699aed1621SDavid Hollister ASSERT(iport); 81709aed1621SDavid Hollister 81719aed1621SDavid Hollister phyp = pmcs_find_phy_by_sas_address(iport->pwp, iport, NULL, tgt_addr); 81729aed1621SDavid Hollister if (phyp == NULL) { 81739aed1621SDavid Hollister pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL, 81749aed1621SDavid Hollister "%s: Couldn't find PHY at %s", __func__, tgt_addr); 81759aed1621SDavid Hollister return (rediscover); 81769aed1621SDavid Hollister } 81779aed1621SDavid Hollister /* phyp is locked */ 81789aed1621SDavid Hollister 81799aed1621SDavid Hollister if (!phyp->reenumerate && phyp->configured) { 81809aed1621SDavid Hollister pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, phyp, phyp->target, 81819aed1621SDavid Hollister "%s: PHY @ %s is configured... re-enumerate", __func__, 81829aed1621SDavid Hollister tgt_addr); 81839aed1621SDavid Hollister phyp->reenumerate = 1; 81849aed1621SDavid Hollister } 81859aed1621SDavid Hollister 81869aed1621SDavid Hollister /* 81879aed1621SDavid Hollister * Check to see if reenumerate is set, and if so, if we've reached our 81889aed1621SDavid Hollister * maximum number of retries. 81899aed1621SDavid Hollister */ 81909aed1621SDavid Hollister if (phyp->reenumerate) { 81919aed1621SDavid Hollister if (phyp->enum_attempts == PMCS_MAX_REENUMERATE) { 81929aed1621SDavid Hollister pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, phyp, 81939aed1621SDavid Hollister phyp->target, 81949aed1621SDavid Hollister "%s: No more enumeration attempts for %s", __func__, 81959aed1621SDavid Hollister tgt_addr); 81969aed1621SDavid Hollister } else { 81979aed1621SDavid Hollister pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, phyp, 81989aed1621SDavid Hollister phyp->target, "%s: Re-attempt enumeration for %s", 81999aed1621SDavid Hollister __func__, tgt_addr); 82009aed1621SDavid Hollister ++phyp->enum_attempts; 82019aed1621SDavid Hollister rediscover = B_TRUE; 82029aed1621SDavid Hollister } 82039aed1621SDavid Hollister 82049aed1621SDavid Hollister phyp->reenumerate = 0; 82059aed1621SDavid Hollister } 82069aed1621SDavid Hollister 82079aed1621SDavid Hollister pmcs_unlock_phy(phyp); 82089aed1621SDavid Hollister 82099aed1621SDavid Hollister mutex_enter(&iport->pwp->config_lock); 82109aed1621SDavid Hollister iport->pwp->config_restart_time = ddi_get_lbolt() + 82119aed1621SDavid Hollister drv_usectohz(PMCS_REDISCOVERY_DELAY); 82129aed1621SDavid Hollister if (rediscover) { 82139aed1621SDavid Hollister iport->pwp->config_restart = B_TRUE; 82149aed1621SDavid Hollister } else if (iport->pwp->config_restart == B_TRUE) { 82159aed1621SDavid Hollister /* 82169aed1621SDavid Hollister * If we aren't asking for rediscovery because of this PHY, 82179aed1621SDavid Hollister * check to see if we're already asking for it on behalf of 82189aed1621SDavid Hollister * some other PHY. If so, we'll want to return TRUE, so reset 82199aed1621SDavid Hollister * "rediscover" here. 82209aed1621SDavid Hollister */ 82219aed1621SDavid Hollister rediscover = B_TRUE; 82229aed1621SDavid Hollister } 82239aed1621SDavid Hollister 82249aed1621SDavid Hollister mutex_exit(&iport->pwp->config_lock); 82259aed1621SDavid Hollister 82269aed1621SDavid Hollister return (rediscover); 82279aed1621SDavid Hollister } 82289aed1621SDavid Hollister 82299aed1621SDavid Hollister void 82309aed1621SDavid Hollister pmcs_status_disposition(pmcs_phy_t *phyp, uint32_t status) 82319aed1621SDavid Hollister { 82329aed1621SDavid Hollister ASSERT(phyp); 82339aed1621SDavid Hollister ASSERT(!mutex_owned(&phyp->phy_lock)); 82349aed1621SDavid Hollister 82359aed1621SDavid Hollister if (phyp == NULL) { 82369aed1621SDavid Hollister return; 82379aed1621SDavid Hollister } 82389aed1621SDavid Hollister 82399aed1621SDavid Hollister pmcs_lock_phy(phyp); 82409aed1621SDavid Hollister 82419aed1621SDavid Hollister /* 82429aed1621SDavid Hollister * XXX: Do we need to call this function from an SSP_EVENT? 82439aed1621SDavid Hollister */ 82449aed1621SDavid Hollister 82459aed1621SDavid Hollister switch (status) { 82469aed1621SDavid Hollister case PMCOUT_STATUS_NO_DEVICE: 82479aed1621SDavid Hollister case PMCOUT_STATUS_ERROR_HW_TIMEOUT: 82489aed1621SDavid Hollister case PMCOUT_STATUS_XFER_ERR_BREAK: 82499aed1621SDavid Hollister case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY: 82509aed1621SDavid Hollister case PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED: 82519aed1621SDavid Hollister case PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION: 82529aed1621SDavid Hollister case PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK: 82539aed1621SDavid Hollister case PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION: 82549aed1621SDavid Hollister case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: 82559aed1621SDavid Hollister case PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY: 82569aed1621SDavid Hollister case PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION: 82579aed1621SDavid Hollister case PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_ERROR: 82589aed1621SDavid Hollister case PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED: 82599aed1621SDavid Hollister case PMCOUT_STATUS_XFER_ERROR_RX_FRAME: 82609aed1621SDavid Hollister case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT: 82619aed1621SDavid Hollister case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE: 82629aed1621SDavid Hollister case PMCOUT_STATUS_IO_PORT_IN_RESET: 82639aed1621SDavid Hollister case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL: 82649aed1621SDavid Hollister case PMCOUT_STATUS_IO_DS_IN_RECOVERY: 82659aed1621SDavid Hollister case PMCOUT_STATUS_IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY: 82669aed1621SDavid Hollister pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG, phyp, phyp->target, 82679aed1621SDavid Hollister "%s: status = 0x%x for " SAS_ADDR_FMT ", reenumerate", 82689aed1621SDavid Hollister __func__, status, SAS_ADDR_PRT(phyp->sas_address)); 82699aed1621SDavid Hollister phyp->reenumerate = 1; 82709aed1621SDavid Hollister break; 82719aed1621SDavid Hollister 82729aed1621SDavid Hollister default: 82739aed1621SDavid Hollister pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG, phyp, phyp->target, 82749aed1621SDavid Hollister "%s: status = 0x%x for " SAS_ADDR_FMT ", no reenumeration", 82759aed1621SDavid Hollister __func__, status, SAS_ADDR_PRT(phyp->sas_address)); 82769aed1621SDavid Hollister break; 82779aed1621SDavid Hollister } 82789aed1621SDavid Hollister 82799aed1621SDavid Hollister pmcs_unlock_phy(phyp); 82809aed1621SDavid Hollister } 82819aed1621SDavid Hollister 82829aed1621SDavid Hollister /* 82839aed1621SDavid Hollister * Add the list of PHYs pointed to by phyp to the dead_phys_list 82849aed1621SDavid Hollister * 82859aed1621SDavid Hollister * Called with all PHYs in the list locked 82869aed1621SDavid Hollister */ 82879aed1621SDavid Hollister static void 82889aed1621SDavid Hollister pmcs_add_dead_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp) 82899aed1621SDavid Hollister { 82909aed1621SDavid Hollister mutex_enter(&pwp->dead_phylist_lock); 82919aed1621SDavid Hollister while (phyp) { 82929aed1621SDavid Hollister pmcs_phy_t *nxt = phyp->sibling; 82939aed1621SDavid Hollister ASSERT(phyp->dead); 82949aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, NULL, 82959aed1621SDavid Hollister "%s: dead PHY 0x%p (%s) (ref_count %d)", __func__, 82969aed1621SDavid Hollister (void *)phyp, phyp->path, phyp->ref_count); 82979aed1621SDavid Hollister /* 82989aed1621SDavid Hollister * Put this PHY on the dead PHY list for the watchdog to 82999aed1621SDavid Hollister * clean up after any outstanding work has completed. 83009aed1621SDavid Hollister */ 83019aed1621SDavid Hollister phyp->dead_next = pwp->dead_phys; 83029aed1621SDavid Hollister pwp->dead_phys = phyp; 83039aed1621SDavid Hollister pmcs_unlock_phy(phyp); 83049aed1621SDavid Hollister phyp = nxt; 83059aed1621SDavid Hollister } 83069aed1621SDavid Hollister mutex_exit(&pwp->dead_phylist_lock); 83079aed1621SDavid Hollister } 83082ac4abe8SDavid Hollister 83092ac4abe8SDavid Hollister static void 83102ac4abe8SDavid Hollister pmcs_get_fw_version(pmcs_hw_t *pwp) 83112ac4abe8SDavid Hollister { 83122ac4abe8SDavid Hollister uint32_t ila_len, ver_hi, ver_lo; 83132ac4abe8SDavid Hollister uint8_t ila_ver_string[9], img_flag; 83142ac4abe8SDavid Hollister char uc, *ucp = &uc; 83152ac4abe8SDavid Hollister unsigned long ila_ver; 83162ac4abe8SDavid Hollister uint64_t ver_hilo; 83172ac4abe8SDavid Hollister 83182ac4abe8SDavid Hollister /* Firmware version is easy. */ 83192ac4abe8SDavid Hollister pwp->fw = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_FW); 83202ac4abe8SDavid Hollister 83212ac4abe8SDavid Hollister /* 83222ac4abe8SDavid Hollister * Get the image size (2nd to last dword) 83232ac4abe8SDavid Hollister * NOTE: The GSM registers are mapped little-endian, but the data 83242ac4abe8SDavid Hollister * on the flash is actually big-endian, so we need to swap these values 83252ac4abe8SDavid Hollister * regardless of which platform we're on. 83262ac4abe8SDavid Hollister */ 83272ac4abe8SDavid Hollister ila_len = BSWAP_32(pmcs_rd_gsm_reg(pwp, GSM_FLASH_BASE_UPPER, 83282ac4abe8SDavid Hollister GSM_FLASH_BASE + GSM_SM_BLKSZ - (2 << 2))); 83292ac4abe8SDavid Hollister if (ila_len > 65535) { 83302ac4abe8SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 83312ac4abe8SDavid Hollister "%s: Invalid ILA image size (0x%x)?", __func__, ila_len); 83322ac4abe8SDavid Hollister return; 83332ac4abe8SDavid Hollister } 83342ac4abe8SDavid Hollister 83352ac4abe8SDavid Hollister /* 83362ac4abe8SDavid Hollister * The numeric version is at ila_len - PMCS_ILA_VER_OFFSET 83372ac4abe8SDavid Hollister */ 83382ac4abe8SDavid Hollister ver_hi = BSWAP_32(pmcs_rd_gsm_reg(pwp, GSM_FLASH_BASE_UPPER, 83392ac4abe8SDavid Hollister GSM_FLASH_BASE + ila_len - PMCS_ILA_VER_OFFSET)); 83402ac4abe8SDavid Hollister ver_lo = BSWAP_32(pmcs_rd_gsm_reg(pwp, GSM_FLASH_BASE_UPPER, 83412ac4abe8SDavid Hollister GSM_FLASH_BASE + ila_len - PMCS_ILA_VER_OFFSET + 4)); 83422ac4abe8SDavid Hollister ver_hilo = BE_64(((uint64_t)ver_hi << 32) | ver_lo); 83432ac4abe8SDavid Hollister bcopy((const void *)&ver_hilo, &ila_ver_string[0], 8); 83442ac4abe8SDavid Hollister ila_ver_string[8] = '\0'; 83452ac4abe8SDavid Hollister 83462ac4abe8SDavid Hollister (void) ddi_strtoul((const char *)ila_ver_string, &ucp, 16, &ila_ver); 83472ac4abe8SDavid Hollister pwp->ila_ver = (int)(ila_ver & 0xffffffff); 83482ac4abe8SDavid Hollister 83492ac4abe8SDavid Hollister img_flag = (BSWAP_32(pmcs_rd_gsm_reg(pwp, GSM_FLASH_BASE_UPPER, 83502ac4abe8SDavid Hollister GSM_FLASH_IMG_FLAGS)) & 0xff000000) >> 24; 83512ac4abe8SDavid Hollister if (img_flag & PMCS_IMG_FLAG_A) { 83522ac4abe8SDavid Hollister pwp->fw_active_img = 1; 83532ac4abe8SDavid Hollister } else { 83542ac4abe8SDavid Hollister pwp->fw_active_img = 0; 83552ac4abe8SDavid Hollister } 83562ac4abe8SDavid Hollister } 8357