14c06356dh/*
24c06356dh * CDDL HEADER START
34c06356dh *
44c06356dh * The contents of this file are subject to the terms of the
54c06356dh * Common Development and Distribution License (the "License").
64c06356dh * You may not use this file except in compliance with the License.
74c06356dh *
84c06356dh * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94c06356dh * or http://www.opensolaris.org/os/licensing.
104c06356dh * See the License for the specific language governing permissions
114c06356dh * and limitations under the License.
124c06356dh *
134c06356dh * When distributing Covered Code, include this CDDL HEADER in each
144c06356dh * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154c06356dh * If applicable, add the following below this CDDL HEADER, with the
164c06356dh * fields enclosed by brackets "[]" replaced with your own identifying
174c06356dh * information: Portions Copyright [yyyy] [name of copyright owner]
184c06356dh *
194c06356dh * CDDL HEADER END
20658280bDavid Hollister */
21658280bDavid Hollister/*
22658280bDavid Hollister * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
234c06356dh */
244c06356dh
254c06356dh/*
264c06356dh * This file contains various support routines.
274c06356dh */
284c06356dh
294c06356dh#include <sys/scsi/adapters/pmcs/pmcs.h>
304c06356dh
314c06356dh/*
324c06356dh * Local static data
334c06356dh */
3460aabb4Chris Hornestatic int tgtmap_stable_usec = MICROSEC;	/* 1 second */
3560aabb4Chris Hornestatic int tgtmap_csync_usec = 10 * MICROSEC;	/* 10 seconds */
364c06356dh
374c06356dh/*
384c06356dh * SAS Topology Configuration
394c06356dh */
404c06356dhstatic void pmcs_new_tport(pmcs_hw_t *, pmcs_phy_t *);
414c06356dhstatic void pmcs_configure_expander(pmcs_hw_t *, pmcs_phy_t *, pmcs_iport_t *);
424c06356dh
4373a3eccDavid Hollisterstatic void pmcs_check_expanders(pmcs_hw_t *, pmcs_phy_t *);
444c06356dhstatic void pmcs_check_expander(pmcs_hw_t *, pmcs_phy_t *);
454c06356dhstatic void pmcs_clear_expander(pmcs_hw_t *, pmcs_phy_t *, int);
464c06356dh
474c06356dhstatic int pmcs_expander_get_nphy(pmcs_hw_t *, pmcs_phy_t *);
484c06356dhstatic int pmcs_expander_content_discover(pmcs_hw_t *, pmcs_phy_t *,
494c06356dh    pmcs_phy_t *);
504c06356dh
514c06356dhstatic int pmcs_smp_function_result(pmcs_hw_t *, smp_response_frame_t *);
523be32c0Jesse Butlerstatic void pmcs_flush_nonio_cmds(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt);
534c06356dhstatic boolean_t pmcs_validate_devid(pmcs_phy_t *, pmcs_phy_t *, uint32_t);
544c06356dhstatic void pmcs_clear_phys(pmcs_hw_t *, pmcs_phy_t *);
554c06356dhstatic int pmcs_configure_new_devices(pmcs_hw_t *, pmcs_phy_t *);
56601c90fSrikanth, Ramanastatic void pmcs_begin_observations(pmcs_hw_t *);
570b53804Reedstatic void pmcs_flush_observations(pmcs_hw_t *);
584c06356dhstatic boolean_t pmcs_report_observations(pmcs_hw_t *);
594c06356dhstatic boolean_t pmcs_report_iport_observations(pmcs_hw_t *, pmcs_iport_t *,
604c06356dh    pmcs_phy_t *);
614c06356dhstatic pmcs_phy_t *pmcs_find_phy_needing_work(pmcs_hw_t *, pmcs_phy_t *);
624c06356dhstatic int pmcs_kill_devices(pmcs_hw_t *, pmcs_phy_t *);
634c06356dhstatic void pmcs_lock_phy_impl(pmcs_phy_t *, int);
644c06356dhstatic void pmcs_unlock_phy_impl(pmcs_phy_t *, int);
654c06356dhstatic pmcs_phy_t *pmcs_clone_phy(pmcs_phy_t *);
664c06356dhstatic boolean_t pmcs_configure_phy(pmcs_hw_t *, pmcs_phy_t *);
674c06356dhstatic void pmcs_reap_dead_phy(pmcs_phy_t *);
684c06356dhstatic pmcs_iport_t *pmcs_get_iport_by_ua(pmcs_hw_t *, char *);
694c06356dhstatic boolean_t pmcs_phy_target_match(pmcs_phy_t *);
709aed162David Hollisterstatic void pmcs_iport_active(pmcs_iport_t *);
719aed162David Hollisterstatic void pmcs_tgtmap_activate_cb(void *, char *, scsi_tgtmap_tgt_type_t,
729aed162David Hollister    void **);
739aed162David Hollisterstatic boolean_t pmcs_tgtmap_deactivate_cb(void *, char *,
749aed162David Hollister    scsi_tgtmap_tgt_type_t, void *, scsi_tgtmap_deact_rsn_t);
759aed162David Hollisterstatic void pmcs_add_dead_phys(pmcs_hw_t *, pmcs_phy_t *);
762ac4abeDavid Hollisterstatic void pmcs_get_fw_version(pmcs_hw_t *);
771f81b46David Hollisterstatic int pmcs_get_time_stamp(pmcs_hw_t *, uint64_t *, hrtime_t *);
784c06356dh
794c06356dh/*
804c06356dh * Often used strings
814c06356dh */
824c06356dhconst char pmcs_nowrk[] = "%s: unable to get work structure";
834c06356dhconst char pmcs_nomsg[] = "%s: unable to get Inbound Message entry";
846745c55Jesse Butlerconst char pmcs_timeo[] = "%s: command timed out";
854c06356dh
864c06356dhextern const ddi_dma_attr_t pmcs_dattr;
871f81b46David Hollisterextern kmutex_t pmcs_trace_lock;
884c06356dh
894c06356dh/*
904c06356dh * Some Initial setup steps.
914c06356dh */
924c06356dh
934c06356dhint
944c06356dhpmcs_setup(pmcs_hw_t *pwp)
954c06356dh{
964c06356dh	uint32_t barval = pwp->mpibar;
974c06356dh	uint32_t i, scratch, regbar, regoff, barbar, baroff;
984c06356dh	uint32_t new_ioq_depth, ferr = 0;
994c06356dh
1004c06356dh	/*
1014c06356dh	 * Check current state. If we're not at READY state,
1024c06356dh	 * we can't go further.
1034c06356dh	 */
1044c06356dh	scratch = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1);
1054c06356dh	if ((scratch & PMCS_MSGU_AAP_STATE_MASK) == PMCS_MSGU_AAP_STATE_ERROR) {
106c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
107c3bc407dh		    "%s: AAP Error State (0x%x)",
1084c06356dh		    __func__, pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
1094c06356dh		    PMCS_MSGU_AAP_ERROR_MASK);
1104c06356dh		pmcs_fm_ereport(pwp, DDI_FM_DEVICE_INVAL_STATE);
1114c06356dh		ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
1124c06356dh		return (-1);
1134c06356dh	}
1144c06356dh	if ((scratch & PMCS_MSGU_AAP_STATE_MASK) != PMCS_MSGU_AAP_STATE_READY) {
115c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1164c06356dh		    "%s: AAP unit not ready (state 0x%x)",
1174c06356dh		    __func__, scratch & PMCS_MSGU_AAP_STATE_MASK);
1184c06356dh		pmcs_fm_ereport(pwp, DDI_FM_DEVICE_INVAL_STATE);
1194c06356dh		ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
1204c06356dh		return (-1);
1214c06356dh	}
1224c06356dh
1234c06356dh	/*
1244c06356dh	 * Read the offset from the Message Unit scratchpad 0 register.
1254c06356dh	 * This allows us to read the MPI Configuration table.
1264c06356dh	 *
1274c06356dh	 * Check its signature for validity.
1284c06356dh	 */
1294c06356dh	baroff = barval;
1304c06356dh	barbar = barval >> PMCS_MSGU_MPI_BAR_SHIFT;
1314c06356dh	baroff &= PMCS_MSGU_MPI_OFFSET_MASK;
1324c06356dh
1334c06356dh	regoff = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH0);
1344c06356dh	regbar = regoff >> PMCS_MSGU_MPI_BAR_SHIFT;
1354c06356dh	regoff &= PMCS_MSGU_MPI_OFFSET_MASK;
1364c06356dh
1374c06356dh	if (regoff > baroff) {
138c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
139c3bc407dh		    "%s: bad MPI Table Length (register offset=0x%08x, "
140c3bc407dh		    "passed offset=0x%08x)", __func__, regoff, baroff);
1414c06356dh		return (-1);
1424c06356dh	}
1434c06356dh	if (regbar != barbar) {
144c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
145c3bc407dh		    "%s: bad MPI BAR (register BAROFF=0x%08x, "
146c3bc407dh		    "passed BAROFF=0x%08x)", __func__, regbar, barbar);
1474c06356dh		return (-1);
1484c06356dh	}
1494c06356dh	pwp->mpi_offset = regoff;
1504c06356dh	if (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_AS) != PMCS_SIGNATURE) {
151c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1524c06356dh		    "%s: Bad MPI Configuration Table Signature 0x%x", __func__,
1534c06356dh		    pmcs_rd_mpi_tbl(pwp, PMCS_MPI_AS));
1544c06356dh		return (-1);
1554c06356dh	}
1564c06356dh
1574c06356dh	if (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IR) != PMCS_MPI_REVISION1) {
158c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1594c06356dh		    "%s: Bad MPI Configuration Revision 0x%x", __func__,
1604c06356dh		    pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IR));
1614c06356dh		return (-1);
1624c06356dh	}
1634c06356dh
1644c06356dh	/*
1654c06356dh	 * Generate offsets for the General System, Inbound Queue Configuration
1664c06356dh	 * and Outbound Queue configuration tables. This way the macros to
1674c06356dh	 * access those tables will work correctly.
1684c06356dh	 */
1694c06356dh	pwp->mpi_gst_offset =
1704c06356dh	    pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_GSTO);
1714c06356dh	pwp->mpi_iqc_offset =
1724c06356dh	    pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IQCTO);
1734c06356dh	pwp->mpi_oqc_offset =
1744c06356dh	    pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_OQCTO);
1754c06356dh
1762ac4abeDavid Hollister	pmcs_get_fw_version(pwp);
1774c06356dh
1784c06356dh	pwp->max_cmd = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_MOIO);
1794c06356dh	pwp->max_dev = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO0) >> 16;
1804c06356dh
1814c06356dh	pwp->max_iq = PMCS_MNIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1));
1824c06356dh	pwp->max_oq = PMCS_MNOQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1));
1834c06356dh	pwp->nphy = PMCS_NPHY(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1));
1844c06356dh	if (pwp->max_iq <= PMCS_NIQ) {
185c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
186c3bc407dh		    "%s: not enough Inbound Queues supported "
187c3bc407dh		    "(need %d, max_oq=%d)", __func__, pwp->max_iq, PMCS_NIQ);
1884c06356dh		return (-1);
1894c06356dh	}
1904c06356dh	if (pwp->max_oq <= PMCS_NOQ) {
191c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
192c3bc407dh		    "%s: not enough Outbound Queues supported "
193c3bc407dh		    "(need %d, max_oq=%d)", __func__, pwp->max_oq, PMCS_NOQ);
1944c06356dh		return (-1);
1954c06356dh	}
1964c06356dh	if (pwp->nphy == 0) {
197c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
198c3bc407dh		    "%s: zero phys reported", __func__);
1994c06356dh		return (-1);
2004c06356dh	}
2014c06356dh	if (PMCS_HPIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1))) {
2024c06356dh		pwp->hipri_queue = (1 << PMCS_IQ_OTHER);
2034c06356dh	}
2044c06356dh
2054c06356dh
2064c06356dh	for (i = 0; i < pwp->nphy; i++) {
2074c06356dh		PMCS_MPI_EVQSET(pwp, PMCS_OQ_EVENTS, i);
2084c06356dh		PMCS_MPI_NCQSET(pwp, PMCS_OQ_EVENTS, i);
2094c06356dh	}
2104c06356dh
2114c06356dh	pmcs_wr_mpi_tbl(pwp, PMCS_MPI_INFO2,
2124c06356dh	    (PMCS_OQ_EVENTS << GENERAL_EVENT_OQ_SHIFT) |
2134c06356dh	    (PMCS_OQ_EVENTS << DEVICE_HANDLE_REMOVED_SHIFT));
2144c06356dh
2154c06356dh	/*
2164c06356dh	 * Verify that ioq_depth is valid (> 0 and not so high that it
2174c06356dh	 * would cause us to overrun the chip with commands).
2184c06356dh	 */
2194c06356dh	if (pwp->ioq_depth == 0) {
220c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2214c06356dh		    "%s: I/O queue depth set to 0. Setting to %d",
2224c06356dh		    __func__, PMCS_NQENTRY);
2234c06356dh		pwp->ioq_depth = PMCS_NQENTRY;
2244c06356dh	}
2254c06356dh
2264c06356dh	if (pwp->ioq_depth < PMCS_MIN_NQENTRY) {
227c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2284c06356dh		    "%s: I/O queue depth set too low (%d). Setting to %d",
2294c06356dh		    __func__, pwp->ioq_depth, PMCS_MIN_NQENTRY);
2304c06356dh		pwp->ioq_depth = PMCS_MIN_NQENTRY;
2314c06356dh	}
2324c06356dh
2334c06356dh	if (pwp->ioq_depth > (pwp->max_cmd / (PMCS_IO_IQ_MASK + 1))) {
2344c06356dh		new_ioq_depth = pwp->max_cmd / (PMCS_IO_IQ_MASK + 1);
235c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2364c06356dh		    "%s: I/O queue depth set too high (%d). Setting to %d",
2374c06356dh		    __func__, pwp->ioq_depth, new_ioq_depth);
2384c06356dh		pwp->ioq_depth = new_ioq_depth;
2394c06356dh	}
2404c06356dh
2414c06356dh	/*
2424c06356dh	 * Allocate consistent memory for OQs and IQs.
2434c06356dh	 */
2444c06356dh	pwp->iqp_dma_attr = pwp->oqp_dma_attr = pmcs_dattr;
2454c06356dh	pwp->iqp_dma_attr.dma_attr_align =
2464c06356dh	    pwp->oqp_dma_attr.dma_attr_align = PMCS_QENTRY_SIZE;
2474c06356dh
2484c06356dh	/*
2494c06356dh	 * The Rev C chip has the ability to do PIO to or from consistent
2504c06356dh	 * memory anywhere in a 64 bit address space, but the firmware is
2514c06356dh	 * not presently set up to do so.
2524c06356dh	 */
2534c06356dh	pwp->iqp_dma_attr.dma_attr_addr_hi =
2544c06356dh	    pwp->oqp_dma_attr.dma_attr_addr_hi = 0x000000FFFFFFFFFFull;
2554c06356dh
2564c06356dh	for (i = 0; i < PMCS_NIQ; i++) {
2574c06356dh		if (pmcs_dma_setup(pwp, &pwp->iqp_dma_attr,
2584c06356dh		    &pwp->iqp_acchdls[i],
2594c06356dh		    &pwp->iqp_handles[i], PMCS_QENTRY_SIZE * pwp->ioq_depth,
2604c06356dh		    (caddr_t *)&pwp->iqp[i], &pwp->iqaddr[i]) == B_FALSE) {
261c3bc407dh			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2624c06356dh			    "Failed to setup DMA for iqp[%d]", i);
2634c06356dh			return (-1);
2644c06356dh		}
2654c06356dh		bzero(pwp->iqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
2664c06356dh	}
2674c06356dh
2684c06356dh	for (i = 0; i < PMCS_NOQ; i++) {
2694c06356dh		if (pmcs_dma_setup(pwp, &pwp->oqp_dma_attr,
2704c06356dh		    &pwp->oqp_acchdls[i],
2714c06356dh		    &pwp->oqp_handles[i], PMCS_QENTRY_SIZE * pwp->ioq_depth,
2724c06356dh		    (caddr_t *)&pwp->oqp[i], &pwp->oqaddr[i]) == B_FALSE) {
273c3bc407dh			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2744c06356dh			    "Failed to setup DMA for oqp[%d]", i);
2754c06356dh			return (-1);
2764c06356dh		}
2774c06356dh		bzero(pwp->oqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
2784c06356dh	}
2794c06356dh
2804c06356dh	/*
2814c06356dh	 * Install the IQ and OQ addresses (and null out the rest).
2824c06356dh	 */
2834c06356dh	for (i = 0; i < pwp->max_iq; i++) {
2844c06356dh		pwp->iqpi_offset[i] = pmcs_rd_iqc_tbl(pwp, PMCS_IQPIOFFX(i));
2854c06356dh		if (i < PMCS_NIQ) {
2864c06356dh			if (i != PMCS_IQ_OTHER) {
2874c06356dh				pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i),
2884c06356dh				    pwp->ioq_depth | (PMCS_QENTRY_SIZE << 16));
2894c06356dh			} else {
2904c06356dh				pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i),
2914c06356dh				    (1 << 30) | pwp->ioq_depth |
2924c06356dh				    (PMCS_QENTRY_SIZE << 16));
2934c06356dh			}
2944c06356dh			pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i),
2954c06356dh			    DWORD1(pwp->iqaddr[i]));
2964c06356dh			pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i),
2974c06356dh			    DWORD0(pwp->iqaddr[i]));
2984c06356dh			pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i),
2994c06356dh			    DWORD1(pwp->ciaddr+IQ_OFFSET(i)));
3004c06356dh			pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i),
3014c06356dh			    DWORD0(pwp->ciaddr+IQ_OFFSET(i)));
3024c06356dh		} else {
3034c06356dh			pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 0);
3044c06356dh			pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 0);
3054c06356dh			pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 0);
3064c06356dh			pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 0);
3074c06356dh			pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 0);
3084c06356dh		}
3094c06356dh	}
3104c06356dh
3114c06356dh	for (i = 0; i < pwp->max_oq; i++) {
3124c06356dh		pwp->oqci_offset[i] = pmcs_rd_oqc_tbl(pwp, PMCS_OQCIOFFX(i));
3134c06356dh		if (i < PMCS_NOQ) {
3144c06356dh			pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), pwp->ioq_depth |
3154c06356dh			    (PMCS_QENTRY_SIZE << 16) | OQIEX);
3164c06356dh			pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i),
3174c06356dh			    DWORD1(pwp->oqaddr[i]));
3184c06356dh			pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i),
3194c06356dh			    DWORD0(pwp->oqaddr[i]));
3204c06356dh			pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i),
3214c06356dh			    DWORD1(pwp->ciaddr+OQ_OFFSET(i)));
3224c06356dh			pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i),
3234c06356dh			    DWORD0(pwp->ciaddr+OQ_OFFSET(i)));
3244c06356dh			pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i),
3254c06356dh			    pwp->oqvec[i] << 24);
3264c06356dh			pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0);
3274c06356dh		} else {
3284c06356dh			pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), 0);
3294c06356dh			pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 0);
3304c06356dh			pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 0);
3314c06356dh			pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 0);
3324c06356dh			pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 0);
3334c06356dh			pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 0);
3344c06356dh			pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0);
3354c06356dh		}
3364c06356dh	}
3374c06356dh
3384c06356dh	/*
3394c06356dh	 * Set up logging, if defined.
3404c06356dh	 */
3414c06356dh	if (pwp->fwlog) {
3424c06356dh		uint64_t logdma = pwp->fwaddr;
3434c06356dh		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBAH, DWORD1(logdma));
3444c06356dh		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBAL, DWORD0(logdma));
3454c06356dh		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBS, PMCS_FWLOG_SIZE >> 1);
3464c06356dh		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELSEV, pwp->fwlog);
3474c06356dh		logdma += (PMCS_FWLOG_SIZE >> 1);
3484c06356dh		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBAH, DWORD1(logdma));
3494c06356dh		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBAL, DWORD0(logdma));
3504c06356dh		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBS, PMCS_FWLOG_SIZE >> 1);
3514c06356dh		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELSEV, pwp->fwlog);
3524c06356dh	}
3534c06356dh
3544c06356dh	/*
3554c06356dh	 * Interrupt vectors, outbound queues, and odb_auto_clear
3564c06356dh	 *
3574c06356dh	 * MSI/MSI-X:
3584c06356dh	 * If we got 4 interrupt vectors, we'll assign one to each outbound
3594c06356dh	 * queue as well as the fatal interrupt, and auto clear can be set
3604c06356dh	 * for each.
3614c06356dh	 *
3624c06356dh	 * If we only got 2 vectors, one will be used for I/O completions
3634c06356dh	 * and the other for the other two vectors.  In this case, auto_
3644c06356dh	 * clear can only be set for I/Os, which is fine.  The fatal
3654c06356dh	 * interrupt will be mapped to the PMCS_FATAL_INTERRUPT bit, which
3664c06356dh	 * is not an interrupt vector.
3674c06356dh	 *
3684c06356dh	 * MSI/MSI-X/INT-X:
3694c06356dh	 * If we only got 1 interrupt vector, auto_clear must be set to 0,
3704c06356dh	 * and again the fatal interrupt will be mapped to the
3714c06356dh	 * PMCS_FATAL_INTERRUPT bit (again, not an interrupt vector).
3724c06356dh	 */
3734c06356dh
3744c06356dh	switch (pwp->int_type) {
3754c06356dh	case PMCS_INT_MSIX:
3764c06356dh	case PMCS_INT_MSI:
3774c06356dh		switch (pwp->intr_cnt) {
3784c06356dh		case 1:
3794c06356dh			pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
3804c06356dh			    (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT));
3814c06356dh			pwp->odb_auto_clear = 0;
3824c06356dh			break;
3834c06356dh		case 2:
3844c06356dh			pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
3854c06356dh			    (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT));
3864c06356dh			pwp->odb_auto_clear = (1 << PMCS_FATAL_INTERRUPT) |
3874c06356dh			    (1 << PMCS_MSIX_IODONE);
3884c06356dh			break;
3894c06356dh		case 4:
3904c06356dh			pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
3914c06356dh			    (PMCS_MSIX_FATAL << PMCS_FERIV_SHIFT));
3924c06356dh			pwp->odb_auto_clear = (1 << PMCS_MSIX_FATAL) |
3934c06356dh			    (1 << PMCS_MSIX_GENERAL) | (1 << PMCS_MSIX_IODONE) |
3944c06356dh			    (1 << PMCS_MSIX_EVENTS);
3954c06356dh			break;
3964c06356dh		}
3974c06356dh		break;
3984c06356dh
3994c06356dh	case PMCS_INT_FIXED:
4004c06356dh		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR,
4014c06356dh		    PMCS_FERRIE | (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT));
4024c06356dh		pwp->odb_auto_clear = 0;
4034c06356dh		break;
4044c06356dh	}
4054c06356dh
4064c06356dh	/*
407658280bDavid Hollister	 * If the open retry interval is non-zero, set it.
408658280bDavid Hollister	 */
409658280bDavid Hollister	if (pwp->open_retry_interval != 0) {
410658280bDavid Hollister		int phynum;
411658280bDavid Hollister
412658280bDavid Hollister		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
413658280bDavid Hollister		    "%s: Setting open retry interval to %d usecs", __func__,
414658280bDavid Hollister		    pwp->open_retry_interval);
415658280bDavid Hollister		for (phynum = 0; phynum < pwp->nphy; phynum ++) {
416658280bDavid Hollister			pmcs_wr_gsm_reg(pwp, OPEN_RETRY_INTERVAL(phynum),
417658280bDavid Hollister			    pwp->open_retry_interval);
418658280bDavid Hollister		}
419658280bDavid Hollister	}
420658280bDavid Hollister
421658280bDavid Hollister	/*
4224c06356dh	 * Enable Interrupt Reassertion
4234c06356dh	 * Default Delay 1000us
4244c06356dh	 */
4254c06356dh	ferr = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_FERR);
4264c06356dh	if ((ferr & PMCS_MPI_IRAE) == 0) {
4274c06356dh		ferr &= ~(PMCS_MPI_IRAU | PMCS_MPI_IRAD_MASK);
4284c06356dh		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, ferr | PMCS_MPI_IRAE);
4294c06356dh	}
4304c06356dh
4314c06356dh	pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR, pwp->odb_auto_clear);
4324c06356dh	pwp->mpi_table_setup = 1;
4334c06356dh	return (0);
4344c06356dh}
4354c06356dh
4364c06356dh/*
4374c06356dh * Start the Message Passing protocol with the PMC chip.
4384c06356dh */
4394c06356dhint
4404c06356dhpmcs_start_mpi(pmcs_hw_t *pwp)
4414c06356dh{
4424c06356dh	int i;
4434c06356dh
4444c06356dh	pmcs_wr_msgunit(pwp, PMCS_MSGU_IBDB, PMCS_MSGU_IBDB_MPIINI);
4454c06356dh	for (i = 0; i < 1000; i++) {
4464c06356dh		if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) &
4474c06356dh		    PMCS_MSGU_IBDB_MPIINI) == 0) {
4484c06356dh			break;
4494c06356dh		}
4504c06356dh		drv_usecwait(1000);
4514c06356dh	}
4524c06356dh	if (pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & PMCS_MSGU_IBDB_MPIINI) {
4534c06356dh		return (-1);
4544c06356dh	}
4554c06356dh	drv_usecwait(500000);
4564c06356dh
4574c06356dh	/*
4584c06356dh	 * Check to make sure we got to INIT state.
4594c06356dh	 */
4604c06356dh	if (PMCS_MPI_S(pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE)) !=
4614c06356dh	    PMCS_MPI_STATE_INIT) {
462c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
463c3bc407dh		    "%s: MPI launch failed (GST 0x%x DBCLR 0x%x)", __func__,
4644c06356dh		    pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE),
4654c06356dh		    pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB_CLEAR));
4664c06356dh		return (-1);
4674c06356dh	}
4684c06356dh	return (0);
4694c06356dh}
4704c06356dh
4714c06356dh/*
4724c06356dh * Stop the Message Passing protocol with the PMC chip.
4734c06356dh */
4744c06356dhint
4754c06356dhpmcs_stop_mpi(pmcs_hw_t *pwp)
4764c06356dh{
4774c06356dh	int i;
4784c06356dh
4794c06356dh	for (i = 0; i < pwp->max_iq; i++) {
4804c06356dh		pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 0);
4814c06356dh		pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 0);
4824c06356dh		pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 0);
4834c06356dh		pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 0);
4844c06356dh		pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 0);
4854c06356dh	}
4864c06356dh	for (i = 0; i < pwp->max_oq; i++) {
4874c06356dh		pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), 0);
4884c06356dh		pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 0);
4894c06356dh		pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 0);
4904c06356dh		pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 0);
4914c06356dh		pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 0);
4924c06356dh		pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 0);
4934c06356dh		pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0);
4944c06356dh	}
4954c06356dh	pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, 0);
4964c06356dh	pmcs_wr_msgunit(pwp, PMCS_MSGU_IBDB, PMCS_MSGU_IBDB_MPICTU);
4974c06356dh	for (i = 0; i < 2000; i++) {
4984c06356dh		if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) &
4994c06356dh		    PMCS_MSGU_IBDB_MPICTU) == 0) {
5004c06356dh			break;
5014c06356dh		}
5024c06356dh		drv_usecwait(1000);
5034c06356dh	}
5044c06356dh	if (pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & PMCS_MSGU_IBDB_MPICTU) {
505c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
506c3bc407dh		    "%s: MPI stop failed", __func__);
5074c06356dh		return (-1);
5084c06356dh	}
5094c06356dh	return (0);
5104c06356dh}
5114c06356dh
5124c06356dh/*
5134c06356dh * Do a sequence of ECHO messages to test for MPI functionality,
5144c06356dh * all inbound and outbound queue functionality and interrupts.
5154c06356dh */
5164c06356dhint
5174c06356dhpmcs_echo_test(pmcs_hw_t *pwp)
5184c06356dh{
5194c06356dh	echo_test_t fred;
5204c06356dh	struct pmcwork *pwrk;
5214c06356dh	uint32_t *msg, count;
5224c06356dh	int iqe = 0, iqo = 0, result, rval = 0;
5234c06356dh	int iterations;
5244c06356dh	hrtime_t echo_start, echo_end, echo_total;
5254c06356dh
5264c06356dh	ASSERT(pwp->max_cmd > 0);
5274c06356dh
5284c06356dh	/*
5294c06356dh	 * We want iterations to be max_cmd * 3 to ensure that we run the
5304c06356dh	 * echo test enough times to iterate through every inbound queue
5314c06356dh	 * at least twice.
5324c06356dh	 */
5334c06356dh	iterations = pwp->max_cmd * 3;
5344c06356dh
5354c06356dh	echo_total = 0;
5364c06356dh	count = 0;
5374c06356dh
5384c06356dh	while (count < iterations) {
5394c06356dh		pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
5404c06356dh		if (pwrk == NULL) {
541c3bc407dh			pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
542c3bc407dh			    pmcs_nowrk, __func__);
5434c06356dh			rval = -1;
5444c06356dh			break;
5454c06356dh		}
5464c06356dh
5474c06356dh		mutex_enter(&pwp->iqp_lock[iqe]);
5484c06356dh		msg = GET_IQ_ENTRY(pwp, iqe);
5494c06356dh		if (msg == NULL) {
5504c06356dh			mutex_exit(&pwp->iqp_lock[iqe]);
5514c06356dh			pmcs_pwork(pwp, pwrk);
552c3bc407dh			pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
553c3bc407dh			    pmcs_nomsg, __func__);
5544c06356dh			rval = -1;
5554c06356dh			break;
5564c06356dh		}
5574c06356dh
5584c06356dh		bzero(msg, PMCS_QENTRY_SIZE);
5594c06356dh
5604c06356dh		if (iqe == PMCS_IQ_OTHER) {
5614c06356dh			/* This is on the high priority queue */
5624c06356dh			msg[0] = LE_32(PMCS_HIPRI(pwp, iqo, PMCIN_ECHO));
5634c06356dh		} else {
5644c06356dh			msg[0] = LE_32(PMCS_IOMB_IN_SAS(iqo, PMCIN_ECHO));
5654c06356dh		}
5664c06356dh		msg[1] = LE_32(pwrk->htag);
5674c06356dh		fred.signature = 0xdeadbeef;
5684c06356dh		fred.count = count;
5694c06356dh		fred.ptr = &count;
5704c06356dh		(void) memcpy(&msg[2], &fred, sizeof (fred));
5714c06356dh		pwrk->state = PMCS_WORK_STATE_ONCHIP;
5724c06356dh
5734c06356dh		INC_IQ_ENTRY(pwp, iqe);
5744c06356dh
5754c06356dh		echo_start = gethrtime();
5764c06356dh		DTRACE_PROBE2(pmcs__echo__test__wait__start,
5774c06356dh		    hrtime_t, echo_start, uint32_t, pwrk->htag);
5784c06356dh
5794c06356dh		if (++iqe == PMCS_NIQ) {
5804c06356dh			iqe = 0;
5814c06356dh		}
5824c06356dh		if (++iqo == PMCS_NOQ) {
5834c06356dh			iqo = 0;
5844c06356dh		}
5854c06356dh
5864c06356dh		WAIT_FOR(pwrk, 250, result);
5873be32c0Jesse Butler		pmcs_pwork(pwp, pwrk);
5884c06356dh
5894c06356dh		echo_end = gethrtime();
5904c06356dh		DTRACE_PROBE2(pmcs__echo__test__wait__end,
5914c06356dh		    hrtime_t, echo_end, int, result);
5924c06356dh		echo_total += (echo_end - echo_start);
5934c06356dh
5944c06356dh		if (result) {
595c3bc407dh			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
5964c06356dh			    "%s: command timed out on echo test #%d",
5974c06356dh			    __func__, count);
5984c06356dh			rval = -1;
5994c06356dh			break;
6004c06356dh		}
6014c06356dh	}
6024c06356dh
6034c06356dh	/*
6044c06356dh	 * The intr_threshold is adjusted by PMCS_INTR_THRESHOLD in order to
6054c06356dh	 * remove the overhead of things like the delay in getting signaled
6064c06356dh	 * for completion.
6074c06356dh	 */
6084c06356dh	if (echo_total != 0) {
6094c06356dh		pwp->io_intr_coal.intr_latency =
6104c06356dh		    (echo_total / iterations) / 2;
6114c06356dh		pwp->io_intr_coal.intr_threshold =
6124c06356dh		    PMCS_INTR_THRESHOLD(PMCS_QUANTUM_TIME_USECS * 1000 /
6134c06356dh		    pwp->io_intr_coal.intr_latency);
6144c06356dh	}
6154c06356dh
6164c06356dh	return (rval);
6174c06356dh}
6184c06356dh
6194c06356dh/*
6204c06356dh * Start the (real) phys
6214c06356dh */
6224c06356dhint
6234c06356dhpmcs_start_phy(pmcs_hw_t *pwp, int phynum, int linkmode, int speed)
6244c06356dh{
6254c06356dh	int result;
6264c06356dh	uint32_t *msg;
6274c06356dh	struct pmcwork *pwrk;
6284c06356dh	pmcs_phy_t *pptr;
6294c06356dh	sas_identify_af_t sap;
6304c06356dh
6314c06356dh	mutex_enter(&pwp->lock);
6324c06356dh	pptr = pwp->root_phys + phynum;
6334c06356dh	if (pptr == NULL) {
6344c06356dh		mutex_exit(&pwp->lock);
635c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
636c3bc407dh		    "%s: cannot find port %d", __func__, phynum);
6374c06356dh		return (0);
6384c06356dh	}
6394c06356dh
6404c06356dh	pmcs_lock_phy(pptr);
6414c06356dh	mutex_exit(&pwp->lock);
6424c06356dh
6434c06356dh	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
6444c06356dh	if (pwrk == NULL) {
6454c06356dh		pmcs_unlock_phy(pptr);
646c3bc407dh		pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__);
6474c06356dh		return (-1);
6484c06356dh	}
6494c06356dh
6504c06356dh	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
6514c06356dh	msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
6524c06356dh
6534c06356dh	if (msg == NULL) {
6544c06356dh		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
6554c06356dh		pmcs_unlock_phy(pptr);
6564c06356dh		pmcs_pwork(pwp, pwrk);
657c3bc407dh		pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__);
6584c06356dh		return (-1);
6594c06356dh	}
6604c06356dh	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_PHY_START));
6614c06356dh	msg[1] = LE_32(pwrk->htag);
6624c06356dh	msg[2] = LE_32(linkmode | speed | phynum);
6634c06356dh	bzero(&sap, sizeof (sap));
6644c06356dh	sap.device_type = SAS_IF_DTYPE_ENDPOINT;
6654c06356dh	sap.ssp_ini_port = 1;
6664c06356dh
6674c06356dh	if (pwp->separate_ports) {
6684c06356dh		pmcs_wwn2barray(pwp->sas_wwns[phynum], sap.sas_address);
6694c06356dh	} else {
6704c06356dh		pmcs_wwn2barray(pwp->sas_wwns[0], sap.sas_address);
6714c06356dh	}
6724c06356dh
6734c06356dh	ASSERT(phynum < SAS2_PHYNUM_MAX);
6744c06356dh	sap.phy_identifier = phynum & SAS2_PHYNUM_MASK;
6754c06356dh	(void) memcpy(&msg[3], &sap, sizeof (sas_identify_af_t));
6764c06356dh	pwrk->state = PMCS_WORK_STATE_ONCHIP;
6774c06356dh	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
6784c06356dh
6794c06356dh	pptr->state.prog_min_rate = (lowbit((ulong_t)speed) - 1);
6804c06356dh	pptr->state.prog_max_rate = (highbit((ulong_t)speed) - 1);
6814c06356dh	pptr->state.hw_min_rate = PMCS_HW_MIN_LINK_RATE;
6824c06356dh	pptr->state.hw_max_rate = PMCS_HW_MAX_LINK_RATE;
6834c06356dh
6844c06356dh	pmcs_unlock_phy(pptr);
6854c06356dh	WAIT_FOR(pwrk, 1000, result);
6864c06356dh	pmcs_pwork(pwp, pwrk);
6874c06356dh
6884c06356dh	if (result) {
6896745c55Jesse Butler		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
6904c06356dh	} else {
6914c06356dh		mutex_enter(&pwp->lock);
6924c06356dh		pwp->phys_started |= (1 << phynum);
6934c06356dh		mutex_exit(&pwp->lock);
6944c06356dh	}
6954c06356dh
6964c06356dh	return (0);
6974c06356dh}
6984c06356dh
6994c06356dhint
7004c06356dhpmcs_start_phys(pmcs_hw_t *pwp)
7014c06356dh{
7021f81b46David Hollister	int i, rval;
7034c06356dh
7044c06356dh	for (i = 0; i < pwp->nphy; i++) {
7054c06356dh		if ((pwp->phyid_block_mask & (1 << i)) == 0) {
7064c06356dh			if (pmcs_start_phy(pwp, i,
7074c06356dh			    (pwp->phymode << PHY_MODE_SHIFT),
7084c06356dh			    pwp->physpeed << PHY_LINK_SHIFT)) {
7094c06356dh				return (-1);
7104c06356dh			}
7114c06356dh			if (pmcs_clear_diag_counters(pwp, i)) {
712c3bc407dh				pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
713c3bc407dh				    "%s: failed to reset counters on PHY (%d)",
714c3bc407dh				    __func__, i);
7154c06356dh			}
7164c06356dh		}
7174c06356dh	}
7181f81b46David Hollister
7191f81b46David Hollister	rval = pmcs_get_time_stamp(pwp, &pwp->fw_timestamp, &pwp->hrtimestamp);
7201f81b46David Hollister	if (rval) {
7211f81b46David Hollister		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
7221f81b46David Hollister		    "%s: Failed to obtain firmware timestamp", __func__);
7231f81b46David Hollister	} else {
7241f81b46David Hollister		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
7251f81b46David Hollister		    "Firmware timestamp: 0x%" PRIx64, pwp->fw_timestamp);
7261f81b46David Hollister	}
7271f81b46David Hollister
7284c06356dh	return (0);
7294c06356dh}
7304c06356dh
7314c06356dh/*
7324c06356dh * Called with PHY locked
7334c06356dh */
7344c06356dhint
7354c06356dhpmcs_reset_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint8_t type)
7364c06356dh{
7374c06356dh	uint32_t *msg;
7384c06356dh	uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2];
7394c06356dh	const char *mbar;
7404c06356dh	uint32_t amt;
7414c06356dh	uint32_t pdevid;
7424c06356dh	uint32_t stsoff;
7434c06356dh	uint32_t status;
7444c06356dh	int result, level, phynum;
7454c06356dh	struct pmcwork *pwrk;
7463be32c0Jesse Butler	pmcs_iport_t *iport;
7474c06356dh	uint32_t htag;
7484c06356dh
7494c06356dh	ASSERT(mutex_owned(&pptr->phy_lock));
7504c06356dh
7514c06356dh	bzero(iomb, PMCS_QENTRY_SIZE);
7524c06356dh	phynum = pptr->phynum;
7534c06356dh	level = pptr->level;
7544c06356dh	if (level > 0) {
7554c06356dh		pdevid = pptr->parent->device_id;
756601c90fSrikanth, Ramana	} else if ((level == 0) && (pptr->dtype == EXPANDER)) {
757601c90fSrikanth, Ramana		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, pptr->target,
758601c90fSrikanth, Ramana		    "%s: Not resetting HBA PHY @ %s", __func__, pptr->path);
759601c90fSrikanth, Ramana		return (0);
7604c06356dh	}
7614c06356dh
7629aed162David Hollister	if (!pptr->iport || !pptr->valid_device_id) {
7639aed162David Hollister		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, pptr->target,
7649aed162David Hollister		    "%s: Can't reach PHY %s", __func__, pptr->path);
7659aed162David Hollister		return (0);
7669aed162David Hollister	}
7679aed162David Hollister
7684c06356dh	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
7694c06356dh
7704c06356dh	if (pwrk == NULL) {
771c3bc407dh		pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__);
7724c06356dh		return (ENOMEM);
7734c06356dh	}
7744c06356dh
7754c06356dh	pwrk->arg = iomb;
7764c06356dh
7774c06356dh	/*
7784c06356dh	 * If level > 0, we need to issue an SMP_REQUEST with a PHY_CONTROL
7794c06356dh	 * function to do either a link reset or hard reset.  If level == 0,
7804c06356dh	 * then we do a LOCAL_PHY_CONTROL IOMB to do link/hard reset to the
7814c06356dh	 * root (local) PHY
7824c06356dh	 */
7834c06356dh	if (level) {
7844c06356dh		stsoff = 2;
7854c06356dh		iomb[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
7864c06356dh		    PMCIN_SMP_REQUEST));
7874c06356dh		iomb[1] = LE_32(pwrk->htag);
7884c06356dh		iomb[2] = LE_32(pdevid);
7894c06356dh		iomb[3] = LE_32(40 << SMP_REQUEST_LENGTH_SHIFT);
7904c06356dh		/*
7914c06356dh		 * Send SMP PHY CONTROL/HARD or LINK RESET
7924c06356dh		 */
7934c06356dh		iomb[4] = BE_32(0x40910000);
7944c06356dh		iomb[5] = 0;
7954c06356dh
7964c06356dh		if (type == PMCS_PHYOP_HARD_RESET) {
7974c06356dh			mbar = "SMP PHY CONTROL/HARD RESET";
798c80dec5David Hollister			iomb[6] = BE_32((phynum << 16) |
799c80dec5David Hollister			    (PMCS_PHYOP_HARD_RESET << 8));
8004c06356dh		} else {
8014c06356dh			mbar = "SMP PHY CONTROL/LINK RESET";
802c80dec5David Hollister			iomb[6] = BE_32((phynum << 16) |
803c80dec5David Hollister			    (PMCS_PHYOP_LINK_RESET << 8));
8044c06356dh		}
805c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
8064c06356dh		    "%s: sending %s to %s for phy 0x%x",
8074c06356dh		    __func__, mbar, pptr->parent->path, pptr->phynum);
8084c06356dh		amt = 7;
8094c06356dh	} else {
8104c06356dh		/*
8114c06356dh		 * Unlike most other Outbound messages, status for
8124c06356dh		 * a local phy operation is in DWORD 3.
8134c06356dh		 */
8144c06356dh		stsoff = 3;
8154c06356dh		iomb[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
8164c06356dh		    PMCIN_LOCAL_PHY_CONTROL));
8174c06356dh		iomb[1] = LE_32(pwrk->htag);
8184c06356dh		if (type == PMCS_PHYOP_LINK_RESET) {
8194c06356dh			mbar = "LOCAL PHY LINK RESET";
8204c06356dh			iomb[2] = LE_32((PMCS_PHYOP_LINK_RESET << 8) | phynum);
8214c06356dh		} else {
8224c06356dh			mbar = "LOCAL PHY HARD RESET";
8234c06356dh			iomb[2] = LE_32((PMCS_PHYOP_HARD_RESET << 8) | phynum);
8244c06356dh		}
825c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
8264c06356dh		    "%s: sending %s to %s", __func__, mbar, pptr->path);
8274c06356dh		amt = 3;
8284c06356dh	}
8294c06356dh
8304c06356dh	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
8314c06356dh	msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
8324c06356dh	if (msg == NULL) {
8334c06356dh		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
8344c06356dh		pmcs_pwork(pwp, pwrk);
835c3bc407dh		pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__);
8364c06356dh		return (ENOMEM);
8374c06356dh	}
8384c06356dh	COPY_MESSAGE(msg, iomb, amt);
8394c06356dh	htag = pwrk->htag;
8406745c55Jesse Butler
8413be32c0Jesse Butler	pmcs_hold_iport(pptr->iport);
8423be32c0Jesse Butler	iport = pptr->iport;
8433be32c0Jesse Butler	pmcs_smp_acquire(iport);
8444c06356dh	pwrk->state = PMCS_WORK_STATE_ONCHIP;
8454c06356dh	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
8464c06356dh	pmcs_unlock_phy(pptr);
8474c06356dh	WAIT_FOR(pwrk, 1000, result);
8484c06356dh	pmcs_pwork(pwp, pwrk);
849827ab34Jesse Butler	pmcs_smp_release(iport);
850827ab34Jesse Butler	pmcs_rele_iport(iport);
851601c90fSrikanth, Ramana	pmcs_lock_phy(pptr);
8524c06356dh	if (result) {
8536745c55Jesse Butler		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
8544c06356dh
8554c06356dh		if (pmcs_abort(pwp, pptr, htag, 0, 0)) {
856c3bc407dh			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
8574c06356dh			    "%s: Unable to issue SMP abort for htag 0x%08x",
8584c06356dh			    __func__, htag);
8594c06356dh		} else {
860c3bc407dh			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
8614c06356dh			    "%s: Issuing SMP ABORT for htag 0x%08x",
8624c06356dh			    __func__, htag);
8634c06356dh		}
8644c06356dh		return (EIO);
8654c06356dh	}
8664c06356dh	status = LE_32(iomb[stsoff]);
8674c06356dh
8684c06356dh	if (status != PMCOUT_STATUS_OK) {
8694c06356dh		char buf[32];
8704c06356dh		const char *es =  pmcs_status_str(status);
8714c06356dh		if (es == NULL) {
8724c06356dh			(void) snprintf(buf, sizeof (buf), "Status 0x%x",
8734c06356dh			    status);
8744c06356dh			es = buf;
8754c06356dh		}
876c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
8774c06356dh		    "%s: %s action returned %s for %s", __func__, mbar, es,
8784c06356dh		    pptr->path);
879601c90fSrikanth, Ramana		return (status);
8804c06356dh	}
8814c06356dh
8824c06356dh	return (0);
8834c06356dh}
8844c06356dh
8854c06356dh/*
8864c06356dh * Stop the (real) phys.  No PHY or softstate locks are required as this only
8874c06356dh * happens during detach.
8884c06356dh */
8894c06356dhvoid
8904c06356dhpmcs_stop_phy(pmcs_hw_t *pwp, int phynum)
8914c06356dh{
8924c06356dh	int result;
8934c06356dh	pmcs_phy_t *pptr;
8944c06356dh	uint32_t *msg;
8954c06356dh	struct pmcwork *pwrk;
8964c06356dh
8974c06356dh	pptr =  pwp->root_phys + phynum;
8984c06356dh	if (pptr == NULL) {
899c3bc407dh		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
9004c06356dh		    "%s: unable to find port %d", __func__, phynum);
9014c06356dh		return;
9024c06356dh	}
9034c06356dh
9044c06356dh	if (pwp->phys_started & (1 << phynum)) {
9054c06356dh		pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
9064c06356dh
9074c06356dh		if (pwrk == NULL) {
908c3bc407dh			pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL,
909c3bc407dh			    pmcs_nowrk, __func__);
9104c06356dh			return;
9114c06356dh		}
9124c06356dh
9134c06356dh		mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
9144c06356dh		msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
9154c06356