110696SDavid.Hollister@Sun.COM /*
210696SDavid.Hollister@Sun.COM * CDDL HEADER START
310696SDavid.Hollister@Sun.COM *
410696SDavid.Hollister@Sun.COM * The contents of this file are subject to the terms of the
510696SDavid.Hollister@Sun.COM * Common Development and Distribution License (the "License").
610696SDavid.Hollister@Sun.COM * You may not use this file except in compliance with the License.
710696SDavid.Hollister@Sun.COM *
810696SDavid.Hollister@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910696SDavid.Hollister@Sun.COM * or http://www.opensolaris.org/os/licensing.
1010696SDavid.Hollister@Sun.COM * See the License for the specific language governing permissions
1110696SDavid.Hollister@Sun.COM * and limitations under the License.
1210696SDavid.Hollister@Sun.COM *
1310696SDavid.Hollister@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each
1410696SDavid.Hollister@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510696SDavid.Hollister@Sun.COM * If applicable, add the following below this CDDL HEADER, with the
1610696SDavid.Hollister@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying
1710696SDavid.Hollister@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner]
1810696SDavid.Hollister@Sun.COM *
1910696SDavid.Hollister@Sun.COM * CDDL HEADER END
2012060SDavid.Hollister@Sun.COM */
2112060SDavid.Hollister@Sun.COM /*
2212060SDavid.Hollister@Sun.COM * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2310696SDavid.Hollister@Sun.COM */
2410696SDavid.Hollister@Sun.COM
2510696SDavid.Hollister@Sun.COM /*
2610696SDavid.Hollister@Sun.COM * This file contains various support routines.
2710696SDavid.Hollister@Sun.COM */
2810696SDavid.Hollister@Sun.COM
2910696SDavid.Hollister@Sun.COM #include <sys/scsi/adapters/pmcs/pmcs.h>
3010696SDavid.Hollister@Sun.COM
3110696SDavid.Hollister@Sun.COM /*
3210696SDavid.Hollister@Sun.COM * Local static data
3310696SDavid.Hollister@Sun.COM */
3412207SChris.Horne@Sun.COM static int tgtmap_stable_usec = MICROSEC; /* 1 second */
3512207SChris.Horne@Sun.COM static int tgtmap_csync_usec = 10 * MICROSEC; /* 10 seconds */
3610696SDavid.Hollister@Sun.COM
3710696SDavid.Hollister@Sun.COM /*
3810696SDavid.Hollister@Sun.COM * SAS Topology Configuration
3910696SDavid.Hollister@Sun.COM */
4010696SDavid.Hollister@Sun.COM static void pmcs_new_tport(pmcs_hw_t *, pmcs_phy_t *);
4110696SDavid.Hollister@Sun.COM static void pmcs_configure_expander(pmcs_hw_t *, pmcs_phy_t *, pmcs_iport_t *);
4210696SDavid.Hollister@Sun.COM
4311501SDavid.Hollister@Sun.COM static void pmcs_check_expanders(pmcs_hw_t *, pmcs_phy_t *);
4410696SDavid.Hollister@Sun.COM static void pmcs_check_expander(pmcs_hw_t *, pmcs_phy_t *);
4510696SDavid.Hollister@Sun.COM static void pmcs_clear_expander(pmcs_hw_t *, pmcs_phy_t *, int);
4610696SDavid.Hollister@Sun.COM
4710696SDavid.Hollister@Sun.COM static int pmcs_expander_get_nphy(pmcs_hw_t *, pmcs_phy_t *);
4810696SDavid.Hollister@Sun.COM static int pmcs_expander_content_discover(pmcs_hw_t *, pmcs_phy_t *,
4910696SDavid.Hollister@Sun.COM pmcs_phy_t *);
5010696SDavid.Hollister@Sun.COM
5110696SDavid.Hollister@Sun.COM static int pmcs_smp_function_result(pmcs_hw_t *, smp_response_frame_t *);
5212462Sjesse.butler@oracle.com static void pmcs_flush_nonio_cmds(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt);
5310696SDavid.Hollister@Sun.COM static boolean_t pmcs_validate_devid(pmcs_phy_t *, pmcs_phy_t *, uint32_t);
5410696SDavid.Hollister@Sun.COM static void pmcs_clear_phys(pmcs_hw_t *, pmcs_phy_t *);
5510696SDavid.Hollister@Sun.COM static int pmcs_configure_new_devices(pmcs_hw_t *, pmcs_phy_t *);
5611347SRamana.Srikanth@Sun.COM static void pmcs_begin_observations(pmcs_hw_t *);
5712000SReed.Liu@Sun.COM static void pmcs_flush_observations(pmcs_hw_t *);
5810696SDavid.Hollister@Sun.COM static boolean_t pmcs_report_observations(pmcs_hw_t *);
5910696SDavid.Hollister@Sun.COM static boolean_t pmcs_report_iport_observations(pmcs_hw_t *, pmcs_iport_t *,
6010696SDavid.Hollister@Sun.COM pmcs_phy_t *);
6110696SDavid.Hollister@Sun.COM static pmcs_phy_t *pmcs_find_phy_needing_work(pmcs_hw_t *, pmcs_phy_t *);
6210696SDavid.Hollister@Sun.COM static int pmcs_kill_devices(pmcs_hw_t *, pmcs_phy_t *);
6310696SDavid.Hollister@Sun.COM static void pmcs_lock_phy_impl(pmcs_phy_t *, int);
6410696SDavid.Hollister@Sun.COM static void pmcs_unlock_phy_impl(pmcs_phy_t *, int);
6510696SDavid.Hollister@Sun.COM static pmcs_phy_t *pmcs_clone_phy(pmcs_phy_t *);
6610696SDavid.Hollister@Sun.COM static boolean_t pmcs_configure_phy(pmcs_hw_t *, pmcs_phy_t *);
6710696SDavid.Hollister@Sun.COM static void pmcs_reap_dead_phy(pmcs_phy_t *);
6810696SDavid.Hollister@Sun.COM static pmcs_iport_t *pmcs_get_iport_by_ua(pmcs_hw_t *, char *);
6910696SDavid.Hollister@Sun.COM static boolean_t pmcs_phy_target_match(pmcs_phy_t *);
7011601SDavid.Hollister@Sun.COM static void pmcs_iport_active(pmcs_iport_t *);
7111601SDavid.Hollister@Sun.COM static void pmcs_tgtmap_activate_cb(void *, char *, scsi_tgtmap_tgt_type_t,
7211601SDavid.Hollister@Sun.COM void **);
7311601SDavid.Hollister@Sun.COM static boolean_t pmcs_tgtmap_deactivate_cb(void *, char *,
7411601SDavid.Hollister@Sun.COM scsi_tgtmap_tgt_type_t, void *, scsi_tgtmap_deact_rsn_t);
7511601SDavid.Hollister@Sun.COM static void pmcs_add_dead_phys(pmcs_hw_t *, pmcs_phy_t *);
7611980SDavid.Hollister@Sun.COM static void pmcs_get_fw_version(pmcs_hw_t *);
7712120SDavid.Hollister@Sun.COM static int pmcs_get_time_stamp(pmcs_hw_t *, uint64_t *, hrtime_t *);
7810696SDavid.Hollister@Sun.COM
7910696SDavid.Hollister@Sun.COM /*
8010696SDavid.Hollister@Sun.COM * Often used strings
8110696SDavid.Hollister@Sun.COM */
8210696SDavid.Hollister@Sun.COM const char pmcs_nowrk[] = "%s: unable to get work structure";
8310696SDavid.Hollister@Sun.COM const char pmcs_nomsg[] = "%s: unable to get Inbound Message entry";
8411267SJesse.Butler@Sun.COM const char pmcs_timeo[] = "%s: command timed out";
8510696SDavid.Hollister@Sun.COM
8610696SDavid.Hollister@Sun.COM extern const ddi_dma_attr_t pmcs_dattr;
8712120SDavid.Hollister@Sun.COM extern kmutex_t pmcs_trace_lock;
8810696SDavid.Hollister@Sun.COM
8910696SDavid.Hollister@Sun.COM /*
9010696SDavid.Hollister@Sun.COM * Some Initial setup steps.
9110696SDavid.Hollister@Sun.COM */
9210696SDavid.Hollister@Sun.COM
9310696SDavid.Hollister@Sun.COM int
pmcs_setup(pmcs_hw_t * pwp)9410696SDavid.Hollister@Sun.COM pmcs_setup(pmcs_hw_t *pwp)
9510696SDavid.Hollister@Sun.COM {
9610696SDavid.Hollister@Sun.COM uint32_t barval = pwp->mpibar;
9710696SDavid.Hollister@Sun.COM uint32_t i, scratch, regbar, regoff, barbar, baroff;
9810696SDavid.Hollister@Sun.COM uint32_t new_ioq_depth, ferr = 0;
9910696SDavid.Hollister@Sun.COM
10010696SDavid.Hollister@Sun.COM /*
10110696SDavid.Hollister@Sun.COM * Check current state. If we're not at READY state,
10210696SDavid.Hollister@Sun.COM * we can't go further.
10310696SDavid.Hollister@Sun.COM */
10410696SDavid.Hollister@Sun.COM scratch = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1);
10510696SDavid.Hollister@Sun.COM if ((scratch & PMCS_MSGU_AAP_STATE_MASK) == PMCS_MSGU_AAP_STATE_ERROR) {
10611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
10711048SDavid.Hollister@Sun.COM "%s: AAP Error State (0x%x)",
10810696SDavid.Hollister@Sun.COM __func__, pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
10910696SDavid.Hollister@Sun.COM PMCS_MSGU_AAP_ERROR_MASK);
11010696SDavid.Hollister@Sun.COM pmcs_fm_ereport(pwp, DDI_FM_DEVICE_INVAL_STATE);
11110696SDavid.Hollister@Sun.COM ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
11210696SDavid.Hollister@Sun.COM return (-1);
11310696SDavid.Hollister@Sun.COM }
11410696SDavid.Hollister@Sun.COM if ((scratch & PMCS_MSGU_AAP_STATE_MASK) != PMCS_MSGU_AAP_STATE_READY) {
11511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
11610696SDavid.Hollister@Sun.COM "%s: AAP unit not ready (state 0x%x)",
11710696SDavid.Hollister@Sun.COM __func__, scratch & PMCS_MSGU_AAP_STATE_MASK);
11810696SDavid.Hollister@Sun.COM pmcs_fm_ereport(pwp, DDI_FM_DEVICE_INVAL_STATE);
11910696SDavid.Hollister@Sun.COM ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
12010696SDavid.Hollister@Sun.COM return (-1);
12110696SDavid.Hollister@Sun.COM }
12210696SDavid.Hollister@Sun.COM
12310696SDavid.Hollister@Sun.COM /*
12410696SDavid.Hollister@Sun.COM * Read the offset from the Message Unit scratchpad 0 register.
12510696SDavid.Hollister@Sun.COM * This allows us to read the MPI Configuration table.
12610696SDavid.Hollister@Sun.COM *
12710696SDavid.Hollister@Sun.COM * Check its signature for validity.
12810696SDavid.Hollister@Sun.COM */
12910696SDavid.Hollister@Sun.COM baroff = barval;
13010696SDavid.Hollister@Sun.COM barbar = barval >> PMCS_MSGU_MPI_BAR_SHIFT;
13110696SDavid.Hollister@Sun.COM baroff &= PMCS_MSGU_MPI_OFFSET_MASK;
13210696SDavid.Hollister@Sun.COM
13310696SDavid.Hollister@Sun.COM regoff = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH0);
13410696SDavid.Hollister@Sun.COM regbar = regoff >> PMCS_MSGU_MPI_BAR_SHIFT;
13510696SDavid.Hollister@Sun.COM regoff &= PMCS_MSGU_MPI_OFFSET_MASK;
13610696SDavid.Hollister@Sun.COM
13710696SDavid.Hollister@Sun.COM if (regoff > baroff) {
13811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
13911048SDavid.Hollister@Sun.COM "%s: bad MPI Table Length (register offset=0x%08x, "
14011048SDavid.Hollister@Sun.COM "passed offset=0x%08x)", __func__, regoff, baroff);
14110696SDavid.Hollister@Sun.COM return (-1);
14210696SDavid.Hollister@Sun.COM }
14310696SDavid.Hollister@Sun.COM if (regbar != barbar) {
14411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
14511048SDavid.Hollister@Sun.COM "%s: bad MPI BAR (register BAROFF=0x%08x, "
14611048SDavid.Hollister@Sun.COM "passed BAROFF=0x%08x)", __func__, regbar, barbar);
14710696SDavid.Hollister@Sun.COM return (-1);
14810696SDavid.Hollister@Sun.COM }
14910696SDavid.Hollister@Sun.COM pwp->mpi_offset = regoff;
15010696SDavid.Hollister@Sun.COM if (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_AS) != PMCS_SIGNATURE) {
15111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
15210696SDavid.Hollister@Sun.COM "%s: Bad MPI Configuration Table Signature 0x%x", __func__,
15310696SDavid.Hollister@Sun.COM pmcs_rd_mpi_tbl(pwp, PMCS_MPI_AS));
15410696SDavid.Hollister@Sun.COM return (-1);
15510696SDavid.Hollister@Sun.COM }
15610696SDavid.Hollister@Sun.COM
15710696SDavid.Hollister@Sun.COM if (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IR) != PMCS_MPI_REVISION1) {
15811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
15910696SDavid.Hollister@Sun.COM "%s: Bad MPI Configuration Revision 0x%x", __func__,
16010696SDavid.Hollister@Sun.COM pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IR));
16110696SDavid.Hollister@Sun.COM return (-1);
16210696SDavid.Hollister@Sun.COM }
16310696SDavid.Hollister@Sun.COM
16410696SDavid.Hollister@Sun.COM /*
16510696SDavid.Hollister@Sun.COM * Generate offsets for the General System, Inbound Queue Configuration
16610696SDavid.Hollister@Sun.COM * and Outbound Queue configuration tables. This way the macros to
16710696SDavid.Hollister@Sun.COM * access those tables will work correctly.
16810696SDavid.Hollister@Sun.COM */
16910696SDavid.Hollister@Sun.COM pwp->mpi_gst_offset =
17010696SDavid.Hollister@Sun.COM pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_GSTO);
17110696SDavid.Hollister@Sun.COM pwp->mpi_iqc_offset =
17210696SDavid.Hollister@Sun.COM pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IQCTO);
17310696SDavid.Hollister@Sun.COM pwp->mpi_oqc_offset =
17410696SDavid.Hollister@Sun.COM pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_OQCTO);
17510696SDavid.Hollister@Sun.COM
17611980SDavid.Hollister@Sun.COM pmcs_get_fw_version(pwp);
17710696SDavid.Hollister@Sun.COM
17810696SDavid.Hollister@Sun.COM pwp->max_cmd = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_MOIO);
17910696SDavid.Hollister@Sun.COM pwp->max_dev = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO0) >> 16;
18010696SDavid.Hollister@Sun.COM
18110696SDavid.Hollister@Sun.COM pwp->max_iq = PMCS_MNIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1));
18210696SDavid.Hollister@Sun.COM pwp->max_oq = PMCS_MNOQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1));
18310696SDavid.Hollister@Sun.COM pwp->nphy = PMCS_NPHY(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1));
18410696SDavid.Hollister@Sun.COM if (pwp->max_iq <= PMCS_NIQ) {
18511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
18611048SDavid.Hollister@Sun.COM "%s: not enough Inbound Queues supported "
18711048SDavid.Hollister@Sun.COM "(need %d, max_oq=%d)", __func__, pwp->max_iq, PMCS_NIQ);
18810696SDavid.Hollister@Sun.COM return (-1);
18910696SDavid.Hollister@Sun.COM }
19010696SDavid.Hollister@Sun.COM if (pwp->max_oq <= PMCS_NOQ) {
19111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
19211048SDavid.Hollister@Sun.COM "%s: not enough Outbound Queues supported "
19311048SDavid.Hollister@Sun.COM "(need %d, max_oq=%d)", __func__, pwp->max_oq, PMCS_NOQ);
19410696SDavid.Hollister@Sun.COM return (-1);
19510696SDavid.Hollister@Sun.COM }
19610696SDavid.Hollister@Sun.COM if (pwp->nphy == 0) {
19711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
19811048SDavid.Hollister@Sun.COM "%s: zero phys reported", __func__);
19910696SDavid.Hollister@Sun.COM return (-1);
20010696SDavid.Hollister@Sun.COM }
20110696SDavid.Hollister@Sun.COM if (PMCS_HPIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1))) {
20210696SDavid.Hollister@Sun.COM pwp->hipri_queue = (1 << PMCS_IQ_OTHER);
20310696SDavid.Hollister@Sun.COM }
20410696SDavid.Hollister@Sun.COM
20510696SDavid.Hollister@Sun.COM
20610696SDavid.Hollister@Sun.COM for (i = 0; i < pwp->nphy; i++) {
20710696SDavid.Hollister@Sun.COM PMCS_MPI_EVQSET(pwp, PMCS_OQ_EVENTS, i);
20810696SDavid.Hollister@Sun.COM PMCS_MPI_NCQSET(pwp, PMCS_OQ_EVENTS, i);
20910696SDavid.Hollister@Sun.COM }
21010696SDavid.Hollister@Sun.COM
21110696SDavid.Hollister@Sun.COM pmcs_wr_mpi_tbl(pwp, PMCS_MPI_INFO2,
21210696SDavid.Hollister@Sun.COM (PMCS_OQ_EVENTS << GENERAL_EVENT_OQ_SHIFT) |
21310696SDavid.Hollister@Sun.COM (PMCS_OQ_EVENTS << DEVICE_HANDLE_REMOVED_SHIFT));
21410696SDavid.Hollister@Sun.COM
21510696SDavid.Hollister@Sun.COM /*
21610696SDavid.Hollister@Sun.COM * Verify that ioq_depth is valid (> 0 and not so high that it
21710696SDavid.Hollister@Sun.COM * would cause us to overrun the chip with commands).
21810696SDavid.Hollister@Sun.COM */
21910696SDavid.Hollister@Sun.COM if (pwp->ioq_depth == 0) {
22011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
22110696SDavid.Hollister@Sun.COM "%s: I/O queue depth set to 0. Setting to %d",
22210696SDavid.Hollister@Sun.COM __func__, PMCS_NQENTRY);
22310696SDavid.Hollister@Sun.COM pwp->ioq_depth = PMCS_NQENTRY;
22410696SDavid.Hollister@Sun.COM }
22510696SDavid.Hollister@Sun.COM
22610696SDavid.Hollister@Sun.COM if (pwp->ioq_depth < PMCS_MIN_NQENTRY) {
22711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
22810696SDavid.Hollister@Sun.COM "%s: I/O queue depth set too low (%d). Setting to %d",
22910696SDavid.Hollister@Sun.COM __func__, pwp->ioq_depth, PMCS_MIN_NQENTRY);
23010696SDavid.Hollister@Sun.COM pwp->ioq_depth = PMCS_MIN_NQENTRY;
23110696SDavid.Hollister@Sun.COM }
23210696SDavid.Hollister@Sun.COM
23310696SDavid.Hollister@Sun.COM if (pwp->ioq_depth > (pwp->max_cmd / (PMCS_IO_IQ_MASK + 1))) {
23410696SDavid.Hollister@Sun.COM new_ioq_depth = pwp->max_cmd / (PMCS_IO_IQ_MASK + 1);
23511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
23610696SDavid.Hollister@Sun.COM "%s: I/O queue depth set too high (%d). Setting to %d",
23710696SDavid.Hollister@Sun.COM __func__, pwp->ioq_depth, new_ioq_depth);
23810696SDavid.Hollister@Sun.COM pwp->ioq_depth = new_ioq_depth;
23910696SDavid.Hollister@Sun.COM }
24010696SDavid.Hollister@Sun.COM
24110696SDavid.Hollister@Sun.COM /*
24210696SDavid.Hollister@Sun.COM * Allocate consistent memory for OQs and IQs.
24310696SDavid.Hollister@Sun.COM */
24410696SDavid.Hollister@Sun.COM pwp->iqp_dma_attr = pwp->oqp_dma_attr = pmcs_dattr;
24510696SDavid.Hollister@Sun.COM pwp->iqp_dma_attr.dma_attr_align =
24610696SDavid.Hollister@Sun.COM pwp->oqp_dma_attr.dma_attr_align = PMCS_QENTRY_SIZE;
24710696SDavid.Hollister@Sun.COM
24810696SDavid.Hollister@Sun.COM /*
24910696SDavid.Hollister@Sun.COM * The Rev C chip has the ability to do PIO to or from consistent
25010696SDavid.Hollister@Sun.COM * memory anywhere in a 64 bit address space, but the firmware is
25110696SDavid.Hollister@Sun.COM * not presently set up to do so.
25210696SDavid.Hollister@Sun.COM */
25310696SDavid.Hollister@Sun.COM pwp->iqp_dma_attr.dma_attr_addr_hi =
25410696SDavid.Hollister@Sun.COM pwp->oqp_dma_attr.dma_attr_addr_hi = 0x000000FFFFFFFFFFull;
25510696SDavid.Hollister@Sun.COM
25610696SDavid.Hollister@Sun.COM for (i = 0; i < PMCS_NIQ; i++) {
25710696SDavid.Hollister@Sun.COM if (pmcs_dma_setup(pwp, &pwp->iqp_dma_attr,
25810696SDavid.Hollister@Sun.COM &pwp->iqp_acchdls[i],
25910696SDavid.Hollister@Sun.COM &pwp->iqp_handles[i], PMCS_QENTRY_SIZE * pwp->ioq_depth,
26010696SDavid.Hollister@Sun.COM (caddr_t *)&pwp->iqp[i], &pwp->iqaddr[i]) == B_FALSE) {
26111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
26210696SDavid.Hollister@Sun.COM "Failed to setup DMA for iqp[%d]", i);
26310696SDavid.Hollister@Sun.COM return (-1);
26410696SDavid.Hollister@Sun.COM }
26510696SDavid.Hollister@Sun.COM bzero(pwp->iqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
26610696SDavid.Hollister@Sun.COM }
26710696SDavid.Hollister@Sun.COM
26810696SDavid.Hollister@Sun.COM for (i = 0; i < PMCS_NOQ; i++) {
26910696SDavid.Hollister@Sun.COM if (pmcs_dma_setup(pwp, &pwp->oqp_dma_attr,
27010696SDavid.Hollister@Sun.COM &pwp->oqp_acchdls[i],
27110696SDavid.Hollister@Sun.COM &pwp->oqp_handles[i], PMCS_QENTRY_SIZE * pwp->ioq_depth,
27210696SDavid.Hollister@Sun.COM (caddr_t *)&pwp->oqp[i], &pwp->oqaddr[i]) == B_FALSE) {
27311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
27410696SDavid.Hollister@Sun.COM "Failed to setup DMA for oqp[%d]", i);
27510696SDavid.Hollister@Sun.COM return (-1);
27610696SDavid.Hollister@Sun.COM }
27710696SDavid.Hollister@Sun.COM bzero(pwp->oqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
27810696SDavid.Hollister@Sun.COM }
27910696SDavid.Hollister@Sun.COM
28010696SDavid.Hollister@Sun.COM /*
28110696SDavid.Hollister@Sun.COM * Install the IQ and OQ addresses (and null out the rest).
28210696SDavid.Hollister@Sun.COM */
28310696SDavid.Hollister@Sun.COM for (i = 0; i < pwp->max_iq; i++) {
28410696SDavid.Hollister@Sun.COM pwp->iqpi_offset[i] = pmcs_rd_iqc_tbl(pwp, PMCS_IQPIOFFX(i));
28510696SDavid.Hollister@Sun.COM if (i < PMCS_NIQ) {
28610696SDavid.Hollister@Sun.COM if (i != PMCS_IQ_OTHER) {
28710696SDavid.Hollister@Sun.COM pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i),
28810696SDavid.Hollister@Sun.COM pwp->ioq_depth | (PMCS_QENTRY_SIZE << 16));
28910696SDavid.Hollister@Sun.COM } else {
29010696SDavid.Hollister@Sun.COM pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i),
29110696SDavid.Hollister@Sun.COM (1 << 30) | pwp->ioq_depth |
29210696SDavid.Hollister@Sun.COM (PMCS_QENTRY_SIZE << 16));
29310696SDavid.Hollister@Sun.COM }
29410696SDavid.Hollister@Sun.COM pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i),
29510696SDavid.Hollister@Sun.COM DWORD1(pwp->iqaddr[i]));
29610696SDavid.Hollister@Sun.COM pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i),
29710696SDavid.Hollister@Sun.COM DWORD0(pwp->iqaddr[i]));
29810696SDavid.Hollister@Sun.COM pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i),
29910696SDavid.Hollister@Sun.COM DWORD1(pwp->ciaddr+IQ_OFFSET(i)));
30010696SDavid.Hollister@Sun.COM pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i),
30110696SDavid.Hollister@Sun.COM DWORD0(pwp->ciaddr+IQ_OFFSET(i)));
30210696SDavid.Hollister@Sun.COM } else {
30310696SDavid.Hollister@Sun.COM pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 0);
30410696SDavid.Hollister@Sun.COM pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 0);
30510696SDavid.Hollister@Sun.COM pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 0);
30610696SDavid.Hollister@Sun.COM pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 0);
30710696SDavid.Hollister@Sun.COM pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 0);
30810696SDavid.Hollister@Sun.COM }
30910696SDavid.Hollister@Sun.COM }
31010696SDavid.Hollister@Sun.COM
31110696SDavid.Hollister@Sun.COM for (i = 0; i < pwp->max_oq; i++) {
31210696SDavid.Hollister@Sun.COM pwp->oqci_offset[i] = pmcs_rd_oqc_tbl(pwp, PMCS_OQCIOFFX(i));
31310696SDavid.Hollister@Sun.COM if (i < PMCS_NOQ) {
31410696SDavid.Hollister@Sun.COM pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), pwp->ioq_depth |
31510696SDavid.Hollister@Sun.COM (PMCS_QENTRY_SIZE << 16) | OQIEX);
31610696SDavid.Hollister@Sun.COM pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i),
31710696SDavid.Hollister@Sun.COM DWORD1(pwp->oqaddr[i]));
31810696SDavid.Hollister@Sun.COM pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i),
31910696SDavid.Hollister@Sun.COM DWORD0(pwp->oqaddr[i]));
32010696SDavid.Hollister@Sun.COM pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i),
32110696SDavid.Hollister@Sun.COM DWORD1(pwp->ciaddr+OQ_OFFSET(i)));
32210696SDavid.Hollister@Sun.COM pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i),
32310696SDavid.Hollister@Sun.COM DWORD0(pwp->ciaddr+OQ_OFFSET(i)));
32410696SDavid.Hollister@Sun.COM pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i),
32510696SDavid.Hollister@Sun.COM pwp->oqvec[i] << 24);
32610696SDavid.Hollister@Sun.COM pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0);
32710696SDavid.Hollister@Sun.COM } else {
32810696SDavid.Hollister@Sun.COM pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), 0);
32910696SDavid.Hollister@Sun.COM pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 0);
33010696SDavid.Hollister@Sun.COM pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 0);
33110696SDavid.Hollister@Sun.COM pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 0);
33210696SDavid.Hollister@Sun.COM pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 0);
33310696SDavid.Hollister@Sun.COM pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 0);
33410696SDavid.Hollister@Sun.COM pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0);
33510696SDavid.Hollister@Sun.COM }
33610696SDavid.Hollister@Sun.COM }
33710696SDavid.Hollister@Sun.COM
33810696SDavid.Hollister@Sun.COM /*
33910696SDavid.Hollister@Sun.COM * Set up logging, if defined.
34010696SDavid.Hollister@Sun.COM */
34110696SDavid.Hollister@Sun.COM if (pwp->fwlog) {
34210696SDavid.Hollister@Sun.COM uint64_t logdma = pwp->fwaddr;
34310696SDavid.Hollister@Sun.COM pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBAH, DWORD1(logdma));
34410696SDavid.Hollister@Sun.COM pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBAL, DWORD0(logdma));
34510696SDavid.Hollister@Sun.COM pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBS, PMCS_FWLOG_SIZE >> 1);
34610696SDavid.Hollister@Sun.COM pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELSEV, pwp->fwlog);
34710696SDavid.Hollister@Sun.COM logdma += (PMCS_FWLOG_SIZE >> 1);
34810696SDavid.Hollister@Sun.COM pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBAH, DWORD1(logdma));
34910696SDavid.Hollister@Sun.COM pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBAL, DWORD0(logdma));
35010696SDavid.Hollister@Sun.COM pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBS, PMCS_FWLOG_SIZE >> 1);
35110696SDavid.Hollister@Sun.COM pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELSEV, pwp->fwlog);
35210696SDavid.Hollister@Sun.COM }
35310696SDavid.Hollister@Sun.COM
35410696SDavid.Hollister@Sun.COM /*
35510696SDavid.Hollister@Sun.COM * Interrupt vectors, outbound queues, and odb_auto_clear
35610696SDavid.Hollister@Sun.COM *
35710696SDavid.Hollister@Sun.COM * MSI/MSI-X:
35810696SDavid.Hollister@Sun.COM * If we got 4 interrupt vectors, we'll assign one to each outbound
35910696SDavid.Hollister@Sun.COM * queue as well as the fatal interrupt, and auto clear can be set
36010696SDavid.Hollister@Sun.COM * for each.
36110696SDavid.Hollister@Sun.COM *
36210696SDavid.Hollister@Sun.COM * If we only got 2 vectors, one will be used for I/O completions
36310696SDavid.Hollister@Sun.COM * and the other for the other two vectors. In this case, auto_
36410696SDavid.Hollister@Sun.COM * clear can only be set for I/Os, which is fine. The fatal
36510696SDavid.Hollister@Sun.COM * interrupt will be mapped to the PMCS_FATAL_INTERRUPT bit, which
36610696SDavid.Hollister@Sun.COM * is not an interrupt vector.
36710696SDavid.Hollister@Sun.COM *
36810696SDavid.Hollister@Sun.COM * MSI/MSI-X/INT-X:
36910696SDavid.Hollister@Sun.COM * If we only got 1 interrupt vector, auto_clear must be set to 0,
37010696SDavid.Hollister@Sun.COM * and again the fatal interrupt will be mapped to the
37110696SDavid.Hollister@Sun.COM * PMCS_FATAL_INTERRUPT bit (again, not an interrupt vector).
37210696SDavid.Hollister@Sun.COM */
37310696SDavid.Hollister@Sun.COM
37410696SDavid.Hollister@Sun.COM switch (pwp->int_type) {
37510696SDavid.Hollister@Sun.COM case PMCS_INT_MSIX:
37610696SDavid.Hollister@Sun.COM case PMCS_INT_MSI:
37710696SDavid.Hollister@Sun.COM switch (pwp->intr_cnt) {
37810696SDavid.Hollister@Sun.COM case 1:
37910696SDavid.Hollister@Sun.COM pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
38010696SDavid.Hollister@Sun.COM (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT));
38110696SDavid.Hollister@Sun.COM pwp->odb_auto_clear = 0;
38210696SDavid.Hollister@Sun.COM break;
38310696SDavid.Hollister@Sun.COM case 2:
38410696SDavid.Hollister@Sun.COM pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
38510696SDavid.Hollister@Sun.COM (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT));
38610696SDavid.Hollister@Sun.COM pwp->odb_auto_clear = (1 << PMCS_FATAL_INTERRUPT) |
38710696SDavid.Hollister@Sun.COM (1 << PMCS_MSIX_IODONE);
38810696SDavid.Hollister@Sun.COM break;
38910696SDavid.Hollister@Sun.COM case 4:
39010696SDavid.Hollister@Sun.COM pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
39110696SDavid.Hollister@Sun.COM (PMCS_MSIX_FATAL << PMCS_FERIV_SHIFT));
39210696SDavid.Hollister@Sun.COM pwp->odb_auto_clear = (1 << PMCS_MSIX_FATAL) |
39310696SDavid.Hollister@Sun.COM (1 << PMCS_MSIX_GENERAL) | (1 << PMCS_MSIX_IODONE) |
39410696SDavid.Hollister@Sun.COM (1 << PMCS_MSIX_EVENTS);
39510696SDavid.Hollister@Sun.COM break;
39610696SDavid.Hollister@Sun.COM }
39710696SDavid.Hollister@Sun.COM break;
39810696SDavid.Hollister@Sun.COM
39910696SDavid.Hollister@Sun.COM case PMCS_INT_FIXED:
40010696SDavid.Hollister@Sun.COM pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR,
40110696SDavid.Hollister@Sun.COM PMCS_FERRIE | (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT));
40210696SDavid.Hollister@Sun.COM pwp->odb_auto_clear = 0;
40310696SDavid.Hollister@Sun.COM break;
40410696SDavid.Hollister@Sun.COM }
40510696SDavid.Hollister@Sun.COM
40610696SDavid.Hollister@Sun.COM /*
40712060SDavid.Hollister@Sun.COM * If the open retry interval is non-zero, set it.
40812060SDavid.Hollister@Sun.COM */
40912060SDavid.Hollister@Sun.COM if (pwp->open_retry_interval != 0) {
41012060SDavid.Hollister@Sun.COM int phynum;
41112060SDavid.Hollister@Sun.COM
41212060SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
41312060SDavid.Hollister@Sun.COM "%s: Setting open retry interval to %d usecs", __func__,
41412060SDavid.Hollister@Sun.COM pwp->open_retry_interval);
41512060SDavid.Hollister@Sun.COM for (phynum = 0; phynum < pwp->nphy; phynum ++) {
41612060SDavid.Hollister@Sun.COM pmcs_wr_gsm_reg(pwp, OPEN_RETRY_INTERVAL(phynum),
41712060SDavid.Hollister@Sun.COM pwp->open_retry_interval);
41812060SDavid.Hollister@Sun.COM }
41912060SDavid.Hollister@Sun.COM }
42012060SDavid.Hollister@Sun.COM
42112060SDavid.Hollister@Sun.COM /*
42210696SDavid.Hollister@Sun.COM * Enable Interrupt Reassertion
42310696SDavid.Hollister@Sun.COM * Default Delay 1000us
42410696SDavid.Hollister@Sun.COM */
42510696SDavid.Hollister@Sun.COM ferr = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_FERR);
42610696SDavid.Hollister@Sun.COM if ((ferr & PMCS_MPI_IRAE) == 0) {
42710696SDavid.Hollister@Sun.COM ferr &= ~(PMCS_MPI_IRAU | PMCS_MPI_IRAD_MASK);
42810696SDavid.Hollister@Sun.COM pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, ferr | PMCS_MPI_IRAE);
42910696SDavid.Hollister@Sun.COM }
43010696SDavid.Hollister@Sun.COM
43110696SDavid.Hollister@Sun.COM pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR, pwp->odb_auto_clear);
43210696SDavid.Hollister@Sun.COM pwp->mpi_table_setup = 1;
43310696SDavid.Hollister@Sun.COM return (0);
43410696SDavid.Hollister@Sun.COM }
43510696SDavid.Hollister@Sun.COM
43610696SDavid.Hollister@Sun.COM /*
43710696SDavid.Hollister@Sun.COM * Start the Message Passing protocol with the PMC chip.
43810696SDavid.Hollister@Sun.COM */
43910696SDavid.Hollister@Sun.COM int
pmcs_start_mpi(pmcs_hw_t * pwp)44010696SDavid.Hollister@Sun.COM pmcs_start_mpi(pmcs_hw_t *pwp)
44110696SDavid.Hollister@Sun.COM {
44210696SDavid.Hollister@Sun.COM int i;
44310696SDavid.Hollister@Sun.COM
44410696SDavid.Hollister@Sun.COM pmcs_wr_msgunit(pwp, PMCS_MSGU_IBDB, PMCS_MSGU_IBDB_MPIINI);
44510696SDavid.Hollister@Sun.COM for (i = 0; i < 1000; i++) {
44610696SDavid.Hollister@Sun.COM if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) &
44710696SDavid.Hollister@Sun.COM PMCS_MSGU_IBDB_MPIINI) == 0) {
44810696SDavid.Hollister@Sun.COM break;
44910696SDavid.Hollister@Sun.COM }
45010696SDavid.Hollister@Sun.COM drv_usecwait(1000);
45110696SDavid.Hollister@Sun.COM }
45210696SDavid.Hollister@Sun.COM if (pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & PMCS_MSGU_IBDB_MPIINI) {
45310696SDavid.Hollister@Sun.COM return (-1);
45410696SDavid.Hollister@Sun.COM }
45510696SDavid.Hollister@Sun.COM drv_usecwait(500000);
45610696SDavid.Hollister@Sun.COM
45710696SDavid.Hollister@Sun.COM /*
45810696SDavid.Hollister@Sun.COM * Check to make sure we got to INIT state.
45910696SDavid.Hollister@Sun.COM */
46010696SDavid.Hollister@Sun.COM if (PMCS_MPI_S(pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE)) !=
46110696SDavid.Hollister@Sun.COM PMCS_MPI_STATE_INIT) {
46211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
46311048SDavid.Hollister@Sun.COM "%s: MPI launch failed (GST 0x%x DBCLR 0x%x)", __func__,
46410696SDavid.Hollister@Sun.COM pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE),
46510696SDavid.Hollister@Sun.COM pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB_CLEAR));
46610696SDavid.Hollister@Sun.COM return (-1);
46710696SDavid.Hollister@Sun.COM }
46810696SDavid.Hollister@Sun.COM return (0);
46910696SDavid.Hollister@Sun.COM }
47010696SDavid.Hollister@Sun.COM
47110696SDavid.Hollister@Sun.COM /*
47210696SDavid.Hollister@Sun.COM * Stop the Message Passing protocol with the PMC chip.
47310696SDavid.Hollister@Sun.COM */
47410696SDavid.Hollister@Sun.COM int
pmcs_stop_mpi(pmcs_hw_t * pwp)47510696SDavid.Hollister@Sun.COM pmcs_stop_mpi(pmcs_hw_t *pwp)
47610696SDavid.Hollister@Sun.COM {
47710696SDavid.Hollister@Sun.COM int i;
47810696SDavid.Hollister@Sun.COM
47910696SDavid.Hollister@Sun.COM for (i = 0; i < pwp->max_iq; i++) {
48010696SDavid.Hollister@Sun.COM pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 0);
48110696SDavid.Hollister@Sun.COM pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 0);
48210696SDavid.Hollister@Sun.COM pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 0);
48310696SDavid.Hollister@Sun.COM pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 0);
48410696SDavid.Hollister@Sun.COM pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 0);
48510696SDavid.Hollister@Sun.COM }
48610696SDavid.Hollister@Sun.COM for (i = 0; i < pwp->max_oq; i++) {
48710696SDavid.Hollister@Sun.COM pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), 0);
48810696SDavid.Hollister@Sun.COM pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 0);
48910696SDavid.Hollister@Sun.COM pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 0);
49010696SDavid.Hollister@Sun.COM pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 0);
49110696SDavid.Hollister@Sun.COM pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 0);
49210696SDavid.Hollister@Sun.COM pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 0);
49310696SDavid.Hollister@Sun.COM pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0);
49410696SDavid.Hollister@Sun.COM }
49510696SDavid.Hollister@Sun.COM pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, 0);
49610696SDavid.Hollister@Sun.COM pmcs_wr_msgunit(pwp, PMCS_MSGU_IBDB, PMCS_MSGU_IBDB_MPICTU);
49710696SDavid.Hollister@Sun.COM for (i = 0; i < 2000; i++) {
49810696SDavid.Hollister@Sun.COM if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) &
49910696SDavid.Hollister@Sun.COM PMCS_MSGU_IBDB_MPICTU) == 0) {
50010696SDavid.Hollister@Sun.COM break;
50110696SDavid.Hollister@Sun.COM }
50210696SDavid.Hollister@Sun.COM drv_usecwait(1000);
50310696SDavid.Hollister@Sun.COM }
50410696SDavid.Hollister@Sun.COM if (pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & PMCS_MSGU_IBDB_MPICTU) {
50511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
50611048SDavid.Hollister@Sun.COM "%s: MPI stop failed", __func__);
50710696SDavid.Hollister@Sun.COM return (-1);
50810696SDavid.Hollister@Sun.COM }
50910696SDavid.Hollister@Sun.COM return (0);
51010696SDavid.Hollister@Sun.COM }
51110696SDavid.Hollister@Sun.COM
51210696SDavid.Hollister@Sun.COM /*
51310696SDavid.Hollister@Sun.COM * Do a sequence of ECHO messages to test for MPI functionality,
51410696SDavid.Hollister@Sun.COM * all inbound and outbound queue functionality and interrupts.
51510696SDavid.Hollister@Sun.COM */
51610696SDavid.Hollister@Sun.COM int
pmcs_echo_test(pmcs_hw_t * pwp)51710696SDavid.Hollister@Sun.COM pmcs_echo_test(pmcs_hw_t *pwp)
51810696SDavid.Hollister@Sun.COM {
51910696SDavid.Hollister@Sun.COM echo_test_t fred;
52010696SDavid.Hollister@Sun.COM struct pmcwork *pwrk;
52110696SDavid.Hollister@Sun.COM uint32_t *msg, count;
52210696SDavid.Hollister@Sun.COM int iqe = 0, iqo = 0, result, rval = 0;
52310696SDavid.Hollister@Sun.COM int iterations;
52410696SDavid.Hollister@Sun.COM hrtime_t echo_start, echo_end, echo_total;
52510696SDavid.Hollister@Sun.COM
52610696SDavid.Hollister@Sun.COM ASSERT(pwp->max_cmd > 0);
52710696SDavid.Hollister@Sun.COM
52810696SDavid.Hollister@Sun.COM /*
52910696SDavid.Hollister@Sun.COM * We want iterations to be max_cmd * 3 to ensure that we run the
53010696SDavid.Hollister@Sun.COM * echo test enough times to iterate through every inbound queue
53110696SDavid.Hollister@Sun.COM * at least twice.
53210696SDavid.Hollister@Sun.COM */
53310696SDavid.Hollister@Sun.COM iterations = pwp->max_cmd * 3;
53410696SDavid.Hollister@Sun.COM
53510696SDavid.Hollister@Sun.COM echo_total = 0;
53610696SDavid.Hollister@Sun.COM count = 0;
53710696SDavid.Hollister@Sun.COM
53810696SDavid.Hollister@Sun.COM while (count < iterations) {
53910696SDavid.Hollister@Sun.COM pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
54010696SDavid.Hollister@Sun.COM if (pwrk == NULL) {
54111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
54211048SDavid.Hollister@Sun.COM pmcs_nowrk, __func__);
54310696SDavid.Hollister@Sun.COM rval = -1;
54410696SDavid.Hollister@Sun.COM break;
54510696SDavid.Hollister@Sun.COM }
54610696SDavid.Hollister@Sun.COM
54710696SDavid.Hollister@Sun.COM mutex_enter(&pwp->iqp_lock[iqe]);
54810696SDavid.Hollister@Sun.COM msg = GET_IQ_ENTRY(pwp, iqe);
54910696SDavid.Hollister@Sun.COM if (msg == NULL) {
55010696SDavid.Hollister@Sun.COM mutex_exit(&pwp->iqp_lock[iqe]);
55110696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
55211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
55311048SDavid.Hollister@Sun.COM pmcs_nomsg, __func__);
55410696SDavid.Hollister@Sun.COM rval = -1;
55510696SDavid.Hollister@Sun.COM break;
55610696SDavid.Hollister@Sun.COM }
55710696SDavid.Hollister@Sun.COM
55810696SDavid.Hollister@Sun.COM bzero(msg, PMCS_QENTRY_SIZE);
55910696SDavid.Hollister@Sun.COM
56010696SDavid.Hollister@Sun.COM if (iqe == PMCS_IQ_OTHER) {
56110696SDavid.Hollister@Sun.COM /* This is on the high priority queue */
56210696SDavid.Hollister@Sun.COM msg[0] = LE_32(PMCS_HIPRI(pwp, iqo, PMCIN_ECHO));
56310696SDavid.Hollister@Sun.COM } else {
56410696SDavid.Hollister@Sun.COM msg[0] = LE_32(PMCS_IOMB_IN_SAS(iqo, PMCIN_ECHO));
56510696SDavid.Hollister@Sun.COM }
56610696SDavid.Hollister@Sun.COM msg[1] = LE_32(pwrk->htag);
56710696SDavid.Hollister@Sun.COM fred.signature = 0xdeadbeef;
56810696SDavid.Hollister@Sun.COM fred.count = count;
56910696SDavid.Hollister@Sun.COM fred.ptr = &count;
57010696SDavid.Hollister@Sun.COM (void) memcpy(&msg[2], &fred, sizeof (fred));
57110696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_ONCHIP;
57210696SDavid.Hollister@Sun.COM
57310696SDavid.Hollister@Sun.COM INC_IQ_ENTRY(pwp, iqe);
57410696SDavid.Hollister@Sun.COM
57510696SDavid.Hollister@Sun.COM echo_start = gethrtime();
57610696SDavid.Hollister@Sun.COM DTRACE_PROBE2(pmcs__echo__test__wait__start,
57710696SDavid.Hollister@Sun.COM hrtime_t, echo_start, uint32_t, pwrk->htag);
57810696SDavid.Hollister@Sun.COM
57910696SDavid.Hollister@Sun.COM if (++iqe == PMCS_NIQ) {
58010696SDavid.Hollister@Sun.COM iqe = 0;
58110696SDavid.Hollister@Sun.COM }
58210696SDavid.Hollister@Sun.COM if (++iqo == PMCS_NOQ) {
58310696SDavid.Hollister@Sun.COM iqo = 0;
58410696SDavid.Hollister@Sun.COM }
58510696SDavid.Hollister@Sun.COM
58610696SDavid.Hollister@Sun.COM WAIT_FOR(pwrk, 250, result);
58712462Sjesse.butler@oracle.com pmcs_pwork(pwp, pwrk);
58810696SDavid.Hollister@Sun.COM
58910696SDavid.Hollister@Sun.COM echo_end = gethrtime();
59010696SDavid.Hollister@Sun.COM DTRACE_PROBE2(pmcs__echo__test__wait__end,
59110696SDavid.Hollister@Sun.COM hrtime_t, echo_end, int, result);
59210696SDavid.Hollister@Sun.COM echo_total += (echo_end - echo_start);
59310696SDavid.Hollister@Sun.COM
59410696SDavid.Hollister@Sun.COM if (result) {
59511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
59610696SDavid.Hollister@Sun.COM "%s: command timed out on echo test #%d",
59710696SDavid.Hollister@Sun.COM __func__, count);
59810696SDavid.Hollister@Sun.COM rval = -1;
59910696SDavid.Hollister@Sun.COM break;
60010696SDavid.Hollister@Sun.COM }
60110696SDavid.Hollister@Sun.COM }
60210696SDavid.Hollister@Sun.COM
60310696SDavid.Hollister@Sun.COM /*
60410696SDavid.Hollister@Sun.COM * The intr_threshold is adjusted by PMCS_INTR_THRESHOLD in order to
60510696SDavid.Hollister@Sun.COM * remove the overhead of things like the delay in getting signaled
60610696SDavid.Hollister@Sun.COM * for completion.
60710696SDavid.Hollister@Sun.COM */
60810696SDavid.Hollister@Sun.COM if (echo_total != 0) {
60910696SDavid.Hollister@Sun.COM pwp->io_intr_coal.intr_latency =
61010696SDavid.Hollister@Sun.COM (echo_total / iterations) / 2;
61110696SDavid.Hollister@Sun.COM pwp->io_intr_coal.intr_threshold =
61210696SDavid.Hollister@Sun.COM PMCS_INTR_THRESHOLD(PMCS_QUANTUM_TIME_USECS * 1000 /
61310696SDavid.Hollister@Sun.COM pwp->io_intr_coal.intr_latency);
61410696SDavid.Hollister@Sun.COM }
61510696SDavid.Hollister@Sun.COM
61610696SDavid.Hollister@Sun.COM return (rval);
61710696SDavid.Hollister@Sun.COM }
61810696SDavid.Hollister@Sun.COM
61910696SDavid.Hollister@Sun.COM /*
62010696SDavid.Hollister@Sun.COM * Start the (real) phys
62110696SDavid.Hollister@Sun.COM */
62210696SDavid.Hollister@Sun.COM int
pmcs_start_phy(pmcs_hw_t * pwp,int phynum,int linkmode,int speed)62310696SDavid.Hollister@Sun.COM pmcs_start_phy(pmcs_hw_t *pwp, int phynum, int linkmode, int speed)
62410696SDavid.Hollister@Sun.COM {
62510696SDavid.Hollister@Sun.COM int result;
62610696SDavid.Hollister@Sun.COM uint32_t *msg;
62710696SDavid.Hollister@Sun.COM struct pmcwork *pwrk;
62810696SDavid.Hollister@Sun.COM pmcs_phy_t *pptr;
62910696SDavid.Hollister@Sun.COM sas_identify_af_t sap;
63010696SDavid.Hollister@Sun.COM
63110696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
63210696SDavid.Hollister@Sun.COM pptr = pwp->root_phys + phynum;
63310696SDavid.Hollister@Sun.COM if (pptr == NULL) {
63410696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
63511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
63611048SDavid.Hollister@Sun.COM "%s: cannot find port %d", __func__, phynum);
63710696SDavid.Hollister@Sun.COM return (0);
63810696SDavid.Hollister@Sun.COM }
63910696SDavid.Hollister@Sun.COM
64010696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
64110696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
64210696SDavid.Hollister@Sun.COM
64310696SDavid.Hollister@Sun.COM pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
64410696SDavid.Hollister@Sun.COM if (pwrk == NULL) {
64510696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
64611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__);
64710696SDavid.Hollister@Sun.COM return (-1);
64810696SDavid.Hollister@Sun.COM }
64910696SDavid.Hollister@Sun.COM
65010696SDavid.Hollister@Sun.COM mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
65110696SDavid.Hollister@Sun.COM msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
65210696SDavid.Hollister@Sun.COM
65310696SDavid.Hollister@Sun.COM if (msg == NULL) {
65410696SDavid.Hollister@Sun.COM mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
65510696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
65610696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
65711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__);
65810696SDavid.Hollister@Sun.COM return (-1);
65910696SDavid.Hollister@Sun.COM }
66010696SDavid.Hollister@Sun.COM msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_PHY_START));
66110696SDavid.Hollister@Sun.COM msg[1] = LE_32(pwrk->htag);
66210696SDavid.Hollister@Sun.COM msg[2] = LE_32(linkmode | speed | phynum);
66310696SDavid.Hollister@Sun.COM bzero(&sap, sizeof (sap));
66410696SDavid.Hollister@Sun.COM sap.device_type = SAS_IF_DTYPE_ENDPOINT;
66510696SDavid.Hollister@Sun.COM sap.ssp_ini_port = 1;
66610696SDavid.Hollister@Sun.COM
66710696SDavid.Hollister@Sun.COM if (pwp->separate_ports) {
66810696SDavid.Hollister@Sun.COM pmcs_wwn2barray(pwp->sas_wwns[phynum], sap.sas_address);
66910696SDavid.Hollister@Sun.COM } else {
67010696SDavid.Hollister@Sun.COM pmcs_wwn2barray(pwp->sas_wwns[0], sap.sas_address);
67110696SDavid.Hollister@Sun.COM }
67210696SDavid.Hollister@Sun.COM
67310696SDavid.Hollister@Sun.COM ASSERT(phynum < SAS2_PHYNUM_MAX);
67410696SDavid.Hollister@Sun.COM sap.phy_identifier = phynum & SAS2_PHYNUM_MASK;
67510696SDavid.Hollister@Sun.COM (void) memcpy(&msg[3], &sap, sizeof (sas_identify_af_t));
67610696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_ONCHIP;
67710696SDavid.Hollister@Sun.COM INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
67810696SDavid.Hollister@Sun.COM
67910696SDavid.Hollister@Sun.COM pptr->state.prog_min_rate = (lowbit((ulong_t)speed) - 1);
68010696SDavid.Hollister@Sun.COM pptr->state.prog_max_rate = (highbit((ulong_t)speed) - 1);
68110696SDavid.Hollister@Sun.COM pptr->state.hw_min_rate = PMCS_HW_MIN_LINK_RATE;
68210696SDavid.Hollister@Sun.COM pptr->state.hw_max_rate = PMCS_HW_MAX_LINK_RATE;
68310696SDavid.Hollister@Sun.COM
68410696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
68510696SDavid.Hollister@Sun.COM WAIT_FOR(pwrk, 1000, result);
68610696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
68710696SDavid.Hollister@Sun.COM
68810696SDavid.Hollister@Sun.COM if (result) {
68911267SJesse.Butler@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
69010696SDavid.Hollister@Sun.COM } else {
69110696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
69210696SDavid.Hollister@Sun.COM pwp->phys_started |= (1 << phynum);
69310696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
69410696SDavid.Hollister@Sun.COM }
69510696SDavid.Hollister@Sun.COM
69610696SDavid.Hollister@Sun.COM return (0);
69710696SDavid.Hollister@Sun.COM }
69810696SDavid.Hollister@Sun.COM
69910696SDavid.Hollister@Sun.COM int
pmcs_start_phys(pmcs_hw_t * pwp)70010696SDavid.Hollister@Sun.COM pmcs_start_phys(pmcs_hw_t *pwp)
70110696SDavid.Hollister@Sun.COM {
70212120SDavid.Hollister@Sun.COM int i, rval;
70310696SDavid.Hollister@Sun.COM
70410696SDavid.Hollister@Sun.COM for (i = 0; i < pwp->nphy; i++) {
70510696SDavid.Hollister@Sun.COM if ((pwp->phyid_block_mask & (1 << i)) == 0) {
70610696SDavid.Hollister@Sun.COM if (pmcs_start_phy(pwp, i,
70710696SDavid.Hollister@Sun.COM (pwp->phymode << PHY_MODE_SHIFT),
70810696SDavid.Hollister@Sun.COM pwp->physpeed << PHY_LINK_SHIFT)) {
70910696SDavid.Hollister@Sun.COM return (-1);
71010696SDavid.Hollister@Sun.COM }
71110696SDavid.Hollister@Sun.COM if (pmcs_clear_diag_counters(pwp, i)) {
71211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
71311048SDavid.Hollister@Sun.COM "%s: failed to reset counters on PHY (%d)",
71411048SDavid.Hollister@Sun.COM __func__, i);
71510696SDavid.Hollister@Sun.COM }
71610696SDavid.Hollister@Sun.COM }
71710696SDavid.Hollister@Sun.COM }
71812120SDavid.Hollister@Sun.COM
71912120SDavid.Hollister@Sun.COM rval = pmcs_get_time_stamp(pwp, &pwp->fw_timestamp, &pwp->hrtimestamp);
72012120SDavid.Hollister@Sun.COM if (rval) {
72112120SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
72212120SDavid.Hollister@Sun.COM "%s: Failed to obtain firmware timestamp", __func__);
72312120SDavid.Hollister@Sun.COM } else {
72412120SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
72512120SDavid.Hollister@Sun.COM "Firmware timestamp: 0x%" PRIx64, pwp->fw_timestamp);
72612120SDavid.Hollister@Sun.COM }
72712120SDavid.Hollister@Sun.COM
72810696SDavid.Hollister@Sun.COM return (0);
72910696SDavid.Hollister@Sun.COM }
73010696SDavid.Hollister@Sun.COM
73110696SDavid.Hollister@Sun.COM /*
73210696SDavid.Hollister@Sun.COM * Called with PHY locked
73310696SDavid.Hollister@Sun.COM */
73410696SDavid.Hollister@Sun.COM int
pmcs_reset_phy(pmcs_hw_t * pwp,pmcs_phy_t * pptr,uint8_t type)73510696SDavid.Hollister@Sun.COM pmcs_reset_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint8_t type)
73610696SDavid.Hollister@Sun.COM {
73710696SDavid.Hollister@Sun.COM uint32_t *msg;
73810696SDavid.Hollister@Sun.COM uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2];
73910696SDavid.Hollister@Sun.COM const char *mbar;
74010696SDavid.Hollister@Sun.COM uint32_t amt;
74110696SDavid.Hollister@Sun.COM uint32_t pdevid;
74210696SDavid.Hollister@Sun.COM uint32_t stsoff;
74310696SDavid.Hollister@Sun.COM uint32_t status;
74410696SDavid.Hollister@Sun.COM int result, level, phynum;
74510696SDavid.Hollister@Sun.COM struct pmcwork *pwrk;
74612462Sjesse.butler@oracle.com pmcs_iport_t *iport;
74710696SDavid.Hollister@Sun.COM uint32_t htag;
74810696SDavid.Hollister@Sun.COM
74910696SDavid.Hollister@Sun.COM ASSERT(mutex_owned(&pptr->phy_lock));
75010696SDavid.Hollister@Sun.COM
75110696SDavid.Hollister@Sun.COM bzero(iomb, PMCS_QENTRY_SIZE);
75210696SDavid.Hollister@Sun.COM phynum = pptr->phynum;
75310696SDavid.Hollister@Sun.COM level = pptr->level;
75410696SDavid.Hollister@Sun.COM if (level > 0) {
75510696SDavid.Hollister@Sun.COM pdevid = pptr->parent->device_id;
75611347SRamana.Srikanth@Sun.COM } else if ((level == 0) && (pptr->dtype == EXPANDER)) {
75711347SRamana.Srikanth@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, pptr->target,
75811347SRamana.Srikanth@Sun.COM "%s: Not resetting HBA PHY @ %s", __func__, pptr->path);
75911347SRamana.Srikanth@Sun.COM return (0);
76010696SDavid.Hollister@Sun.COM }
76110696SDavid.Hollister@Sun.COM
76211601SDavid.Hollister@Sun.COM if (!pptr->iport || !pptr->valid_device_id) {
76311601SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, pptr->target,
76411601SDavid.Hollister@Sun.COM "%s: Can't reach PHY %s", __func__, pptr->path);
76511601SDavid.Hollister@Sun.COM return (0);
76611601SDavid.Hollister@Sun.COM }
76711601SDavid.Hollister@Sun.COM
76810696SDavid.Hollister@Sun.COM pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
76910696SDavid.Hollister@Sun.COM
77010696SDavid.Hollister@Sun.COM if (pwrk == NULL) {
77111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__);
77210696SDavid.Hollister@Sun.COM return (ENOMEM);
77310696SDavid.Hollister@Sun.COM }
77410696SDavid.Hollister@Sun.COM
77510696SDavid.Hollister@Sun.COM pwrk->arg = iomb;
77610696SDavid.Hollister@Sun.COM
77710696SDavid.Hollister@Sun.COM /*
77810696SDavid.Hollister@Sun.COM * If level > 0, we need to issue an SMP_REQUEST with a PHY_CONTROL
77910696SDavid.Hollister@Sun.COM * function to do either a link reset or hard reset. If level == 0,
78010696SDavid.Hollister@Sun.COM * then we do a LOCAL_PHY_CONTROL IOMB to do link/hard reset to the
78110696SDavid.Hollister@Sun.COM * root (local) PHY
78210696SDavid.Hollister@Sun.COM */
78310696SDavid.Hollister@Sun.COM if (level) {
78410696SDavid.Hollister@Sun.COM stsoff = 2;
78510696SDavid.Hollister@Sun.COM iomb[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
78610696SDavid.Hollister@Sun.COM PMCIN_SMP_REQUEST));
78710696SDavid.Hollister@Sun.COM iomb[1] = LE_32(pwrk->htag);
78810696SDavid.Hollister@Sun.COM iomb[2] = LE_32(pdevid);
78910696SDavid.Hollister@Sun.COM iomb[3] = LE_32(40 << SMP_REQUEST_LENGTH_SHIFT);
79010696SDavid.Hollister@Sun.COM /*
79110696SDavid.Hollister@Sun.COM * Send SMP PHY CONTROL/HARD or LINK RESET
79210696SDavid.Hollister@Sun.COM */
79310696SDavid.Hollister@Sun.COM iomb[4] = BE_32(0x40910000);
79410696SDavid.Hollister@Sun.COM iomb[5] = 0;
79510696SDavid.Hollister@Sun.COM
79610696SDavid.Hollister@Sun.COM if (type == PMCS_PHYOP_HARD_RESET) {
79710696SDavid.Hollister@Sun.COM mbar = "SMP PHY CONTROL/HARD RESET";
79812443Sdavid.hollister@oracle.com iomb[6] = BE_32((phynum << 16) |
79912443Sdavid.hollister@oracle.com (PMCS_PHYOP_HARD_RESET << 8));
80010696SDavid.Hollister@Sun.COM } else {
80110696SDavid.Hollister@Sun.COM mbar = "SMP PHY CONTROL/LINK RESET";
80212443Sdavid.hollister@oracle.com iomb[6] = BE_32((phynum << 16) |
80312443Sdavid.hollister@oracle.com (PMCS_PHYOP_LINK_RESET << 8));
80410696SDavid.Hollister@Sun.COM }
80511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
80610696SDavid.Hollister@Sun.COM "%s: sending %s to %s for phy 0x%x",
80710696SDavid.Hollister@Sun.COM __func__, mbar, pptr->parent->path, pptr->phynum);
80810696SDavid.Hollister@Sun.COM amt = 7;
80910696SDavid.Hollister@Sun.COM } else {
81010696SDavid.Hollister@Sun.COM /*
81110696SDavid.Hollister@Sun.COM * Unlike most other Outbound messages, status for
81210696SDavid.Hollister@Sun.COM * a local phy operation is in DWORD 3.
81310696SDavid.Hollister@Sun.COM */
81410696SDavid.Hollister@Sun.COM stsoff = 3;
81510696SDavid.Hollister@Sun.COM iomb[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
81610696SDavid.Hollister@Sun.COM PMCIN_LOCAL_PHY_CONTROL));
81710696SDavid.Hollister@Sun.COM iomb[1] = LE_32(pwrk->htag);
81810696SDavid.Hollister@Sun.COM if (type == PMCS_PHYOP_LINK_RESET) {
81910696SDavid.Hollister@Sun.COM mbar = "LOCAL PHY LINK RESET";
82010696SDavid.Hollister@Sun.COM iomb[2] = LE_32((PMCS_PHYOP_LINK_RESET << 8) | phynum);
82110696SDavid.Hollister@Sun.COM } else {
82210696SDavid.Hollister@Sun.COM mbar = "LOCAL PHY HARD RESET";
82310696SDavid.Hollister@Sun.COM iomb[2] = LE_32((PMCS_PHYOP_HARD_RESET << 8) | phynum);
82410696SDavid.Hollister@Sun.COM }
82511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
82610696SDavid.Hollister@Sun.COM "%s: sending %s to %s", __func__, mbar, pptr->path);
82710696SDavid.Hollister@Sun.COM amt = 3;
82810696SDavid.Hollister@Sun.COM }
82910696SDavid.Hollister@Sun.COM
83010696SDavid.Hollister@Sun.COM mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
83110696SDavid.Hollister@Sun.COM msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
83210696SDavid.Hollister@Sun.COM if (msg == NULL) {
83310696SDavid.Hollister@Sun.COM mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
83410696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
83511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__);
83610696SDavid.Hollister@Sun.COM return (ENOMEM);
83710696SDavid.Hollister@Sun.COM }
83810696SDavid.Hollister@Sun.COM COPY_MESSAGE(msg, iomb, amt);
83910696SDavid.Hollister@Sun.COM htag = pwrk->htag;
84011267SJesse.Butler@Sun.COM
84112462Sjesse.butler@oracle.com pmcs_hold_iport(pptr->iport);
84212462Sjesse.butler@oracle.com iport = pptr->iport;
84312462Sjesse.butler@oracle.com pmcs_smp_acquire(iport);
84410696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_ONCHIP;
84510696SDavid.Hollister@Sun.COM INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
84610696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
84710696SDavid.Hollister@Sun.COM WAIT_FOR(pwrk, 1000, result);
84810696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
84913090Sjesse.butler@oracle.com pmcs_smp_release(iport);
85013090Sjesse.butler@oracle.com pmcs_rele_iport(iport);
85110696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
85210696SDavid.Hollister@Sun.COM if (result) {
85311267SJesse.Butler@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
85410696SDavid.Hollister@Sun.COM
85510696SDavid.Hollister@Sun.COM if (pmcs_abort(pwp, pptr, htag, 0, 0)) {
85611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
85710696SDavid.Hollister@Sun.COM "%s: Unable to issue SMP abort for htag 0x%08x",
85810696SDavid.Hollister@Sun.COM __func__, htag);
85910696SDavid.Hollister@Sun.COM } else {
86011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
86110696SDavid.Hollister@Sun.COM "%s: Issuing SMP ABORT for htag 0x%08x",
86210696SDavid.Hollister@Sun.COM __func__, htag);
86310696SDavid.Hollister@Sun.COM }
86410696SDavid.Hollister@Sun.COM return (EIO);
86510696SDavid.Hollister@Sun.COM }
86610696SDavid.Hollister@Sun.COM status = LE_32(iomb[stsoff]);
86710696SDavid.Hollister@Sun.COM
86810696SDavid.Hollister@Sun.COM if (status != PMCOUT_STATUS_OK) {
86910696SDavid.Hollister@Sun.COM char buf[32];
87010696SDavid.Hollister@Sun.COM const char *es = pmcs_status_str(status);
87110696SDavid.Hollister@Sun.COM if (es == NULL) {
87210696SDavid.Hollister@Sun.COM (void) snprintf(buf, sizeof (buf), "Status 0x%x",
87310696SDavid.Hollister@Sun.COM status);
87410696SDavid.Hollister@Sun.COM es = buf;
87510696SDavid.Hollister@Sun.COM }
87611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
87710696SDavid.Hollister@Sun.COM "%s: %s action returned %s for %s", __func__, mbar, es,
87810696SDavid.Hollister@Sun.COM pptr->path);
87911347SRamana.Srikanth@Sun.COM return (status);
88010696SDavid.Hollister@Sun.COM }
88110696SDavid.Hollister@Sun.COM
88210696SDavid.Hollister@Sun.COM return (0);
88310696SDavid.Hollister@Sun.COM }
88410696SDavid.Hollister@Sun.COM
88510696SDavid.Hollister@Sun.COM /*
88610696SDavid.Hollister@Sun.COM * Stop the (real) phys. No PHY or softstate locks are required as this only
88710696SDavid.Hollister@Sun.COM * happens during detach.
88810696SDavid.Hollister@Sun.COM */
88910696SDavid.Hollister@Sun.COM void
pmcs_stop_phy(pmcs_hw_t * pwp,int phynum)89010696SDavid.Hollister@Sun.COM pmcs_stop_phy(pmcs_hw_t *pwp, int phynum)
89110696SDavid.Hollister@Sun.COM {
89210696SDavid.Hollister@Sun.COM int result;
89310696SDavid.Hollister@Sun.COM pmcs_phy_t *pptr;
89410696SDavid.Hollister@Sun.COM uint32_t *msg;
89510696SDavid.Hollister@Sun.COM struct pmcwork *pwrk;
89610696SDavid.Hollister@Sun.COM
89710696SDavid.Hollister@Sun.COM pptr = pwp->root_phys + phynum;
89810696SDavid.Hollister@Sun.COM if (pptr == NULL) {
89911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
90010696SDavid.Hollister@Sun.COM "%s: unable to find port %d", __func__, phynum);
90110696SDavid.Hollister@Sun.COM return;
90210696SDavid.Hollister@Sun.COM }
90310696SDavid.Hollister@Sun.COM
90410696SDavid.Hollister@Sun.COM if (pwp->phys_started & (1 << phynum)) {
90510696SDavid.Hollister@Sun.COM pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
90610696SDavid.Hollister@Sun.COM
90710696SDavid.Hollister@Sun.COM if (pwrk == NULL) {
90811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL,
90911048SDavid.Hollister@Sun.COM pmcs_nowrk, __func__);
91010696SDavid.Hollister@Sun.COM return;
91110696SDavid.Hollister@Sun.COM }
91210696SDavid.Hollister@Sun.COM
91310696SDavid.Hollister@Sun.COM mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
91410696SDavid.Hollister@Sun.COM msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
91510696SDavid.Hollister@Sun.COM
91610696SDavid.Hollister@Sun.COM if (msg == NULL) {
91710696SDavid.Hollister@Sun.COM mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
91810696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
91911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL,
92011048SDavid.Hollister@Sun.COM pmcs_nomsg, __func__);
92110696SDavid.Hollister@Sun.COM return;
92210696SDavid.Hollister@Sun.COM }
92310696SDavid.Hollister@Sun.COM
92410696SDavid.Hollister@Sun.COM msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_PHY_STOP));
92510696SDavid.Hollister@Sun.COM msg[1] = LE_32(pwrk->htag);
92610696SDavid.Hollister@Sun.COM msg[2] = LE_32(phynum);
92710696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_ONCHIP;
92810696SDavid.Hollister@Sun.COM /*
92910696SDavid.Hollister@Sun.COM * Make this unconfigured now.
93010696SDavid.Hollister@Sun.COM */
93110696SDavid.Hollister@Sun.COM INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
93210696SDavid.Hollister@Sun.COM WAIT_FOR(pwrk, 1000, result);
93310696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
93410696SDavid.Hollister@Sun.COM if (result) {
93511267SJesse.Butler@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG,
93611048SDavid.Hollister@Sun.COM pptr, NULL, pmcs_timeo, __func__);
93710696SDavid.Hollister@Sun.COM }
93810696SDavid.Hollister@Sun.COM
93910696SDavid.Hollister@Sun.COM pwp->phys_started &= ~(1 << phynum);
94010696SDavid.Hollister@Sun.COM }
94110696SDavid.Hollister@Sun.COM
94210696SDavid.Hollister@Sun.COM pptr->configured = 0;
94310696SDavid.Hollister@Sun.COM }
94410696SDavid.Hollister@Sun.COM
94510696SDavid.Hollister@Sun.COM /*
94610696SDavid.Hollister@Sun.COM * No locks should be required as this is only called during detach
94710696SDavid.Hollister@Sun.COM */
94810696SDavid.Hollister@Sun.COM void
pmcs_stop_phys(pmcs_hw_t * pwp)94910696SDavid.Hollister@Sun.COM pmcs_stop_phys(pmcs_hw_t *pwp)
95010696SDavid.Hollister@Sun.COM {
95110696SDavid.Hollister@Sun.COM int i;
95210696SDavid.Hollister@Sun.COM for (i = 0; i < pwp->nphy; i++) {
95310696SDavid.Hollister@Sun.COM if ((pwp->phyid_block_mask & (1 << i)) == 0) {
95410696SDavid.Hollister@Sun.COM pmcs_stop_phy(pwp, i);
95510696SDavid.Hollister@Sun.COM }
95610696SDavid.Hollister@Sun.COM }
95710696SDavid.Hollister@Sun.COM }
95810696SDavid.Hollister@Sun.COM
95910696SDavid.Hollister@Sun.COM /*
96010696SDavid.Hollister@Sun.COM * Run SAS_DIAG_EXECUTE with cmd and cmd_desc passed.
96110696SDavid.Hollister@Sun.COM * ERR_CNT_RESET: return status of cmd
96210696SDavid.Hollister@Sun.COM * DIAG_REPORT_GET: return value of the counter
96310696SDavid.Hollister@Sun.COM */
96410696SDavid.Hollister@Sun.COM int
pmcs_sas_diag_execute(pmcs_hw_t * pwp,uint32_t cmd,uint32_t cmd_desc,uint8_t phynum)96510696SDavid.Hollister@Sun.COM pmcs_sas_diag_execute(pmcs_hw_t *pwp, uint32_t cmd, uint32_t cmd_desc,
96610696SDavid.Hollister@Sun.COM uint8_t phynum)
96710696SDavid.Hollister@Sun.COM {
96810696SDavid.Hollister@Sun.COM uint32_t htag, *ptr, status, msg[PMCS_MSG_SIZE << 1];
96910696SDavid.Hollister@Sun.COM int result;
97010696SDavid.Hollister@Sun.COM struct pmcwork *pwrk;
97110696SDavid.Hollister@Sun.COM
97210696SDavid.Hollister@Sun.COM pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
97310696SDavid.Hollister@Sun.COM if (pwrk == NULL) {
97411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nowrk, __func__);
97510696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
97610696SDavid.Hollister@Sun.COM }
97710696SDavid.Hollister@Sun.COM pwrk->arg = msg;
97810696SDavid.Hollister@Sun.COM htag = pwrk->htag;
97910696SDavid.Hollister@Sun.COM msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_SAS_DIAG_EXECUTE));
98010696SDavid.Hollister@Sun.COM msg[1] = LE_32(htag);
98110696SDavid.Hollister@Sun.COM msg[2] = LE_32((cmd << PMCS_DIAG_CMD_SHIFT) |
98210696SDavid.Hollister@Sun.COM (cmd_desc << PMCS_DIAG_CMD_DESC_SHIFT) | phynum);
98310696SDavid.Hollister@Sun.COM
98410696SDavid.Hollister@Sun.COM mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
98510696SDavid.Hollister@Sun.COM ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
98610696SDavid.Hollister@Sun.COM if (ptr == NULL) {
98710696SDavid.Hollister@Sun.COM mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
98810696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
98911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nomsg, __func__);
99010696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
99110696SDavid.Hollister@Sun.COM }
99210696SDavid.Hollister@Sun.COM COPY_MESSAGE(ptr, msg, 3);
99310696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_ONCHIP;
99410696SDavid.Hollister@Sun.COM INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
99510696SDavid.Hollister@Sun.COM
99610696SDavid.Hollister@Sun.COM WAIT_FOR(pwrk, 1000, result);
99710696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
99810696SDavid.Hollister@Sun.COM if (result) {
99910696SDavid.Hollister@Sun.COM pmcs_timed_out(pwp, htag, __func__);
100010696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
100110696SDavid.Hollister@Sun.COM }
100210696SDavid.Hollister@Sun.COM
100310696SDavid.Hollister@Sun.COM status = LE_32(msg[3]);
100410696SDavid.Hollister@Sun.COM
100510696SDavid.Hollister@Sun.COM /* Return for counter reset */
100610696SDavid.Hollister@Sun.COM if (cmd == PMCS_ERR_CNT_RESET)
100710696SDavid.Hollister@Sun.COM return (status);
100810696SDavid.Hollister@Sun.COM
100910696SDavid.Hollister@Sun.COM /* Return for counter value */
101010696SDavid.Hollister@Sun.COM if (status) {
101111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
101211048SDavid.Hollister@Sun.COM "%s: failed, status (0x%x)", __func__, status);
101310696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
101410696SDavid.Hollister@Sun.COM }
101510696SDavid.Hollister@Sun.COM return (LE_32(msg[4]));
101610696SDavid.Hollister@Sun.COM }
101710696SDavid.Hollister@Sun.COM
101810696SDavid.Hollister@Sun.COM /* Get the current value of the counter for desc on phynum and return it. */
101910696SDavid.Hollister@Sun.COM int
pmcs_get_diag_report(pmcs_hw_t * pwp,uint32_t desc,uint8_t phynum)102010696SDavid.Hollister@Sun.COM pmcs_get_diag_report(pmcs_hw_t *pwp, uint32_t desc, uint8_t phynum)
102110696SDavid.Hollister@Sun.COM {
102210696SDavid.Hollister@Sun.COM return (pmcs_sas_diag_execute(pwp, PMCS_DIAG_REPORT_GET, desc, phynum));
102310696SDavid.Hollister@Sun.COM }
102410696SDavid.Hollister@Sun.COM
102510696SDavid.Hollister@Sun.COM /* Clear all of the counters for phynum. Returns the status of the command. */
102610696SDavid.Hollister@Sun.COM int
pmcs_clear_diag_counters(pmcs_hw_t * pwp,uint8_t phynum)102710696SDavid.Hollister@Sun.COM pmcs_clear_diag_counters(pmcs_hw_t *pwp, uint8_t phynum)
102810696SDavid.Hollister@Sun.COM {
102910696SDavid.Hollister@Sun.COM uint32_t cmd = PMCS_ERR_CNT_RESET;
103010696SDavid.Hollister@Sun.COM uint32_t cmd_desc;
103110696SDavid.Hollister@Sun.COM
103210696SDavid.Hollister@Sun.COM cmd_desc = PMCS_INVALID_DWORD_CNT;
103310696SDavid.Hollister@Sun.COM if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
103410696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
103510696SDavid.Hollister@Sun.COM
103610696SDavid.Hollister@Sun.COM cmd_desc = PMCS_DISPARITY_ERR_CNT;
103710696SDavid.Hollister@Sun.COM if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
103810696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
103910696SDavid.Hollister@Sun.COM
104010696SDavid.Hollister@Sun.COM cmd_desc = PMCS_LOST_DWORD_SYNC_CNT;
104110696SDavid.Hollister@Sun.COM if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
104210696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
104310696SDavid.Hollister@Sun.COM
104410696SDavid.Hollister@Sun.COM cmd_desc = PMCS_RESET_FAILED_CNT;
104510696SDavid.Hollister@Sun.COM if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
104610696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
104710696SDavid.Hollister@Sun.COM
104810696SDavid.Hollister@Sun.COM return (DDI_SUCCESS);
104910696SDavid.Hollister@Sun.COM }
105010696SDavid.Hollister@Sun.COM
105110696SDavid.Hollister@Sun.COM /*
105210696SDavid.Hollister@Sun.COM * Get firmware timestamp
105310696SDavid.Hollister@Sun.COM */
105412120SDavid.Hollister@Sun.COM static int
pmcs_get_time_stamp(pmcs_hw_t * pwp,uint64_t * fw_ts,hrtime_t * sys_hr_ts)105512120SDavid.Hollister@Sun.COM pmcs_get_time_stamp(pmcs_hw_t *pwp, uint64_t *fw_ts, hrtime_t *sys_hr_ts)
105610696SDavid.Hollister@Sun.COM {
105710696SDavid.Hollister@Sun.COM uint32_t htag, *ptr, msg[PMCS_MSG_SIZE << 1];
105810696SDavid.Hollister@Sun.COM int result;
105910696SDavid.Hollister@Sun.COM struct pmcwork *pwrk;
106010696SDavid.Hollister@Sun.COM
106110696SDavid.Hollister@Sun.COM pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
106210696SDavid.Hollister@Sun.COM if (pwrk == NULL) {
106311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nowrk, __func__);
106410696SDavid.Hollister@Sun.COM return (-1);
106510696SDavid.Hollister@Sun.COM }
106610696SDavid.Hollister@Sun.COM pwrk->arg = msg;
106710696SDavid.Hollister@Sun.COM htag = pwrk->htag;
106810696SDavid.Hollister@Sun.COM msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_GET_TIME_STAMP));
106910696SDavid.Hollister@Sun.COM msg[1] = LE_32(pwrk->htag);
107010696SDavid.Hollister@Sun.COM
107110696SDavid.Hollister@Sun.COM mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
107210696SDavid.Hollister@Sun.COM ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
107310696SDavid.Hollister@Sun.COM if (ptr == NULL) {
107410696SDavid.Hollister@Sun.COM mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
107510696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
107611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, pmcs_nomsg, __func__);
107710696SDavid.Hollister@Sun.COM return (-1);
107810696SDavid.Hollister@Sun.COM }
107910696SDavid.Hollister@Sun.COM COPY_MESSAGE(ptr, msg, 2);
108010696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_ONCHIP;
108110696SDavid.Hollister@Sun.COM INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
108210696SDavid.Hollister@Sun.COM
108310696SDavid.Hollister@Sun.COM WAIT_FOR(pwrk, 1000, result);
108410696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
108510696SDavid.Hollister@Sun.COM if (result) {
108610696SDavid.Hollister@Sun.COM pmcs_timed_out(pwp, htag, __func__);
108710696SDavid.Hollister@Sun.COM return (-1);
108810696SDavid.Hollister@Sun.COM }
108912120SDavid.Hollister@Sun.COM
109012120SDavid.Hollister@Sun.COM mutex_enter(&pmcs_trace_lock);
109112120SDavid.Hollister@Sun.COM *sys_hr_ts = gethrtime();
109212120SDavid.Hollister@Sun.COM gethrestime(&pwp->sys_timestamp);
109312120SDavid.Hollister@Sun.COM *fw_ts = LE_32(msg[2]) | (((uint64_t)LE_32(msg[3])) << 32);
109412120SDavid.Hollister@Sun.COM mutex_exit(&pmcs_trace_lock);
109510696SDavid.Hollister@Sun.COM return (0);
109610696SDavid.Hollister@Sun.COM }
109710696SDavid.Hollister@Sun.COM
109810696SDavid.Hollister@Sun.COM /*
109910696SDavid.Hollister@Sun.COM * Dump all pertinent registers
110010696SDavid.Hollister@Sun.COM */
110110696SDavid.Hollister@Sun.COM
110210696SDavid.Hollister@Sun.COM void
pmcs_register_dump(pmcs_hw_t * pwp)110310696SDavid.Hollister@Sun.COM pmcs_register_dump(pmcs_hw_t *pwp)
110410696SDavid.Hollister@Sun.COM {
110510696SDavid.Hollister@Sun.COM int i;
110610696SDavid.Hollister@Sun.COM uint32_t val;
110710696SDavid.Hollister@Sun.COM
110811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "pmcs%d: Register dump start",
110910696SDavid.Hollister@Sun.COM ddi_get_instance(pwp->dip));
111011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
111110696SDavid.Hollister@Sun.COM "OBDB (intr): 0x%08x (mask): 0x%08x (clear): 0x%08x",
111210696SDavid.Hollister@Sun.COM pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB),
111310696SDavid.Hollister@Sun.COM pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB_MASK),
111410696SDavid.Hollister@Sun.COM pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR));
111511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH0: 0x%08x",
111610696SDavid.Hollister@Sun.COM pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH0));
111711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH1: 0x%08x",
111810696SDavid.Hollister@Sun.COM pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1));
111911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH2: 0x%08x",
112010696SDavid.Hollister@Sun.COM pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2));
112111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "SCRATCH3: 0x%08x",
112210696SDavid.Hollister@Sun.COM pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH3));
112310696SDavid.Hollister@Sun.COM for (i = 0; i < PMCS_NIQ; i++) {
112411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "IQ %d: CI %u PI %u",
112510696SDavid.Hollister@Sun.COM i, pmcs_rd_iqci(pwp, i), pmcs_rd_iqpi(pwp, i));
112610696SDavid.Hollister@Sun.COM }
112710696SDavid.Hollister@Sun.COM for (i = 0; i < PMCS_NOQ; i++) {
112811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "OQ %d: CI %u PI %u",
112910696SDavid.Hollister@Sun.COM i, pmcs_rd_oqci(pwp, i), pmcs_rd_oqpi(pwp, i));
113010696SDavid.Hollister@Sun.COM }
113110696SDavid.Hollister@Sun.COM val = pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE);
113211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
113310696SDavid.Hollister@Sun.COM "GST TABLE BASE: 0x%08x (STATE=0x%x QF=%d GSTLEN=%d HMI_ERR=0x%x)",
113410696SDavid.Hollister@Sun.COM val, PMCS_MPI_S(val), PMCS_QF(val), PMCS_GSTLEN(val) * 4,
113510696SDavid.Hollister@Sun.COM PMCS_HMI_ERR(val));
113611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE IQFRZ0: 0x%08x",
113710696SDavid.Hollister@Sun.COM pmcs_rd_gst_tbl(pwp, PMCS_GST_IQFRZ0));
113811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE IQFRZ1: 0x%08x",
113910696SDavid.Hollister@Sun.COM pmcs_rd_gst_tbl(pwp, PMCS_GST_IQFRZ1));
114011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE MSGU TICK: 0x%08x",
114110696SDavid.Hollister@Sun.COM pmcs_rd_gst_tbl(pwp, PMCS_GST_MSGU_TICK));
114211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "GST TABLE IOP TICK: 0x%08x",
114310696SDavid.Hollister@Sun.COM pmcs_rd_gst_tbl(pwp, PMCS_GST_IOP_TICK));
114410696SDavid.Hollister@Sun.COM for (i = 0; i < pwp->nphy; i++) {
114510696SDavid.Hollister@Sun.COM uint32_t rerrf, pinfo, started = 0, link = 0;
114610696SDavid.Hollister@Sun.COM pinfo = pmcs_rd_gst_tbl(pwp, PMCS_GST_PHY_INFO(i));
114710696SDavid.Hollister@Sun.COM if (pinfo & 1) {
114810696SDavid.Hollister@Sun.COM started = 1;
114910696SDavid.Hollister@Sun.COM link = pinfo & 2;
115010696SDavid.Hollister@Sun.COM }
115110696SDavid.Hollister@Sun.COM rerrf = pmcs_rd_gst_tbl(pwp, PMCS_GST_RERR_INFO(i));
115211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
115310696SDavid.Hollister@Sun.COM "GST TABLE PHY%d STARTED=%d LINK=%d RERR=0x%08x",
115410696SDavid.Hollister@Sun.COM i, started, link, rerrf);
115510696SDavid.Hollister@Sun.COM }
115611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "pmcs%d: Register dump end",
115710696SDavid.Hollister@Sun.COM ddi_get_instance(pwp->dip));
115810696SDavid.Hollister@Sun.COM }
115910696SDavid.Hollister@Sun.COM
116010696SDavid.Hollister@Sun.COM /*
116110696SDavid.Hollister@Sun.COM * Handle SATA Abort and other error processing
116210696SDavid.Hollister@Sun.COM */
116310696SDavid.Hollister@Sun.COM int
pmcs_abort_handler(pmcs_hw_t * pwp)116410696SDavid.Hollister@Sun.COM pmcs_abort_handler(pmcs_hw_t *pwp)
116510696SDavid.Hollister@Sun.COM {
116610696SDavid.Hollister@Sun.COM pmcs_phy_t *pptr, *pnext, *pnext_uplevel[PMCS_MAX_XPND];
116711347SRamana.Srikanth@Sun.COM pmcs_xscsi_t *tgt;
116810696SDavid.Hollister@Sun.COM int r, level = 0;
116910696SDavid.Hollister@Sun.COM
117011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s", __func__);
117110696SDavid.Hollister@Sun.COM
117210696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
117310696SDavid.Hollister@Sun.COM pptr = pwp->root_phys;
117410696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
117510696SDavid.Hollister@Sun.COM
117610696SDavid.Hollister@Sun.COM while (pptr) {
117710696SDavid.Hollister@Sun.COM /*
117810696SDavid.Hollister@Sun.COM * XXX: Need to make sure this doesn't happen
117910696SDavid.Hollister@Sun.COM * XXX: when non-NCQ commands are running.
118010696SDavid.Hollister@Sun.COM */
118110696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
118210696SDavid.Hollister@Sun.COM if (pptr->need_rl_ext) {
118310696SDavid.Hollister@Sun.COM ASSERT(pptr->dtype == SATA);
118410696SDavid.Hollister@Sun.COM if (pmcs_acquire_scratch(pwp, B_FALSE)) {
118510696SDavid.Hollister@Sun.COM goto next_phy;
118610696SDavid.Hollister@Sun.COM }
118710696SDavid.Hollister@Sun.COM r = pmcs_sata_abort_ncq(pwp, pptr);
118810696SDavid.Hollister@Sun.COM pmcs_release_scratch(pwp);
118910696SDavid.Hollister@Sun.COM if (r == ENOMEM) {
119010696SDavid.Hollister@Sun.COM goto next_phy;
119110696SDavid.Hollister@Sun.COM }
119210696SDavid.Hollister@Sun.COM if (r) {
119310696SDavid.Hollister@Sun.COM r = pmcs_reset_phy(pwp, pptr,
119410696SDavid.Hollister@Sun.COM PMCS_PHYOP_LINK_RESET);
119510696SDavid.Hollister@Sun.COM if (r == ENOMEM) {
119610696SDavid.Hollister@Sun.COM goto next_phy;
119710696SDavid.Hollister@Sun.COM }
119810696SDavid.Hollister@Sun.COM /* what if other failures happened? */
119910696SDavid.Hollister@Sun.COM pptr->abort_pending = 1;
120010696SDavid.Hollister@Sun.COM pptr->abort_sent = 0;
120110696SDavid.Hollister@Sun.COM }
120210696SDavid.Hollister@Sun.COM }
120310696SDavid.Hollister@Sun.COM if (pptr->abort_pending == 0 || pptr->abort_sent) {
120410696SDavid.Hollister@Sun.COM goto next_phy;
120510696SDavid.Hollister@Sun.COM }
120610696SDavid.Hollister@Sun.COM pptr->abort_pending = 0;
120710696SDavid.Hollister@Sun.COM if (pmcs_abort(pwp, pptr, pptr->device_id, 1, 1) == ENOMEM) {
120810696SDavid.Hollister@Sun.COM pptr->abort_pending = 1;
120910696SDavid.Hollister@Sun.COM goto next_phy;
121010696SDavid.Hollister@Sun.COM }
121110696SDavid.Hollister@Sun.COM pptr->abort_sent = 1;
121210696SDavid.Hollister@Sun.COM
121311347SRamana.Srikanth@Sun.COM /*
121411347SRamana.Srikanth@Sun.COM * If the iport is no longer active, flush the queues
121511347SRamana.Srikanth@Sun.COM */
121611347SRamana.Srikanth@Sun.COM if ((pptr->iport == NULL) ||
121711347SRamana.Srikanth@Sun.COM (pptr->iport->ua_state != UA_ACTIVE)) {
121811347SRamana.Srikanth@Sun.COM tgt = pptr->target;
121912462Sjesse.butler@oracle.com if (tgt != NULL) {
122011347SRamana.Srikanth@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, tgt,
122111347SRamana.Srikanth@Sun.COM "%s: Clearing target 0x%p, inactive iport",
122211347SRamana.Srikanth@Sun.COM __func__, (void *) tgt);
122311347SRamana.Srikanth@Sun.COM mutex_enter(&tgt->statlock);
122411347SRamana.Srikanth@Sun.COM pmcs_clear_xp(pwp, tgt);
122511347SRamana.Srikanth@Sun.COM mutex_exit(&tgt->statlock);
122611347SRamana.Srikanth@Sun.COM }
122711347SRamana.Srikanth@Sun.COM }
122811347SRamana.Srikanth@Sun.COM
122910696SDavid.Hollister@Sun.COM next_phy:
123010696SDavid.Hollister@Sun.COM if (pptr->children) {
123110696SDavid.Hollister@Sun.COM pnext = pptr->children;
123210696SDavid.Hollister@Sun.COM pnext_uplevel[level++] = pptr->sibling;
123310696SDavid.Hollister@Sun.COM } else {
123410696SDavid.Hollister@Sun.COM pnext = pptr->sibling;
123510696SDavid.Hollister@Sun.COM while ((pnext == NULL) && (level > 0)) {
123610696SDavid.Hollister@Sun.COM pnext = pnext_uplevel[--level];
123710696SDavid.Hollister@Sun.COM }
123810696SDavid.Hollister@Sun.COM }
123910696SDavid.Hollister@Sun.COM
124010696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
124110696SDavid.Hollister@Sun.COM pptr = pnext;
124210696SDavid.Hollister@Sun.COM }
124310696SDavid.Hollister@Sun.COM
124410696SDavid.Hollister@Sun.COM return (0);
124510696SDavid.Hollister@Sun.COM }
124610696SDavid.Hollister@Sun.COM
124710696SDavid.Hollister@Sun.COM /*
124810696SDavid.Hollister@Sun.COM * Register a device (get a device handle for it).
124910696SDavid.Hollister@Sun.COM * Called with PHY lock held.
125010696SDavid.Hollister@Sun.COM */
125110696SDavid.Hollister@Sun.COM int
pmcs_register_device(pmcs_hw_t * pwp,pmcs_phy_t * pptr)125210696SDavid.Hollister@Sun.COM pmcs_register_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
125310696SDavid.Hollister@Sun.COM {
125410696SDavid.Hollister@Sun.COM struct pmcwork *pwrk;
125510696SDavid.Hollister@Sun.COM int result = 0;
125610696SDavid.Hollister@Sun.COM uint32_t *msg;
125710696SDavid.Hollister@Sun.COM uint32_t tmp, status;
125810696SDavid.Hollister@Sun.COM uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2];
125910696SDavid.Hollister@Sun.COM
126010696SDavid.Hollister@Sun.COM mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
126110696SDavid.Hollister@Sun.COM msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
126210696SDavid.Hollister@Sun.COM
126310696SDavid.Hollister@Sun.COM if (msg == NULL ||
126410696SDavid.Hollister@Sun.COM (pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr)) == NULL) {
126510696SDavid.Hollister@Sun.COM mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
126610696SDavid.Hollister@Sun.COM result = ENOMEM;
126710696SDavid.Hollister@Sun.COM goto out;
126810696SDavid.Hollister@Sun.COM }
126910696SDavid.Hollister@Sun.COM
127010696SDavid.Hollister@Sun.COM pwrk->arg = iomb;
127110696SDavid.Hollister@Sun.COM pwrk->dtype = pptr->dtype;
127210696SDavid.Hollister@Sun.COM
127310696SDavid.Hollister@Sun.COM msg[1] = LE_32(pwrk->htag);
127410696SDavid.Hollister@Sun.COM msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_REGISTER_DEVICE));
127510696SDavid.Hollister@Sun.COM tmp = PMCS_DEVREG_TLR |
127610696SDavid.Hollister@Sun.COM (pptr->link_rate << PMCS_DEVREG_LINK_RATE_SHIFT);
127710696SDavid.Hollister@Sun.COM if (IS_ROOT_PHY(pptr)) {
127810696SDavid.Hollister@Sun.COM msg[2] = LE_32(pptr->portid |
127910696SDavid.Hollister@Sun.COM (pptr->phynum << PMCS_PHYID_SHIFT));
128010696SDavid.Hollister@Sun.COM } else {
128110696SDavid.Hollister@Sun.COM msg[2] = LE_32(pptr->portid);
128210696SDavid.Hollister@Sun.COM }
128310696SDavid.Hollister@Sun.COM if (pptr->dtype == SATA) {
128410696SDavid.Hollister@Sun.COM if (IS_ROOT_PHY(pptr)) {
128510696SDavid.Hollister@Sun.COM tmp |= PMCS_DEVREG_TYPE_SATA_DIRECT;
128610696SDavid.Hollister@Sun.COM } else {
128710696SDavid.Hollister@Sun.COM tmp |= PMCS_DEVREG_TYPE_SATA;
128810696SDavid.Hollister@Sun.COM }
128910696SDavid.Hollister@Sun.COM } else {
129010696SDavid.Hollister@Sun.COM tmp |= PMCS_DEVREG_TYPE_SAS;
129110696SDavid.Hollister@Sun.COM }
129210696SDavid.Hollister@Sun.COM msg[3] = LE_32(tmp);
129310696SDavid.Hollister@Sun.COM msg[4] = LE_32(PMCS_DEVREG_IT_NEXUS_TIMEOUT);
129410696SDavid.Hollister@Sun.COM (void) memcpy(&msg[5], pptr->sas_address, 8);
129510696SDavid.Hollister@Sun.COM
129610696SDavid.Hollister@Sun.COM CLEAN_MESSAGE(msg, 7);
129710696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_ONCHIP;
129810696SDavid.Hollister@Sun.COM INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
129910696SDavid.Hollister@Sun.COM
130010696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
130110696SDavid.Hollister@Sun.COM WAIT_FOR(pwrk, 250, result);
130212462Sjesse.butler@oracle.com pmcs_pwork(pwp, pwrk);
130310696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
130410696SDavid.Hollister@Sun.COM
130510696SDavid.Hollister@Sun.COM if (result) {
130611267SJesse.Butler@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
130710696SDavid.Hollister@Sun.COM result = ETIMEDOUT;
130810696SDavid.Hollister@Sun.COM goto out;
130910696SDavid.Hollister@Sun.COM }
131010696SDavid.Hollister@Sun.COM status = LE_32(iomb[2]);
131110696SDavid.Hollister@Sun.COM tmp = LE_32(iomb[3]);
131210696SDavid.Hollister@Sun.COM switch (status) {
131310696SDavid.Hollister@Sun.COM case PMCS_DEVREG_OK:
131410696SDavid.Hollister@Sun.COM case PMCS_DEVREG_DEVICE_ALREADY_REGISTERED:
131510696SDavid.Hollister@Sun.COM case PMCS_DEVREG_PHY_ALREADY_REGISTERED:
131610696SDavid.Hollister@Sun.COM if (pmcs_validate_devid(pwp->root_phys, pptr, tmp) == B_FALSE) {
131710696SDavid.Hollister@Sun.COM result = EEXIST;
131810696SDavid.Hollister@Sun.COM goto out;
131910696SDavid.Hollister@Sun.COM } else if (status != PMCS_DEVREG_OK) {
132010696SDavid.Hollister@Sun.COM if (tmp == 0xffffffff) { /* F/W bug */
132111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, pptr, NULL,
132210696SDavid.Hollister@Sun.COM "%s: phy %s already has bogus devid 0x%x",
132310696SDavid.Hollister@Sun.COM __func__, pptr->path, tmp);
132410696SDavid.Hollister@Sun.COM result = EIO;
132510696SDavid.Hollister@Sun.COM goto out;
132610696SDavid.Hollister@Sun.COM } else {
132711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, pptr, NULL,
132810696SDavid.Hollister@Sun.COM "%s: phy %s already has a device id 0x%x",
132910696SDavid.Hollister@Sun.COM __func__, pptr->path, tmp);
133010696SDavid.Hollister@Sun.COM }
133110696SDavid.Hollister@Sun.COM }
133210696SDavid.Hollister@Sun.COM break;
133310696SDavid.Hollister@Sun.COM default:
133411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
133511048SDavid.Hollister@Sun.COM "%s: status 0x%x when trying to register device %s",
133611048SDavid.Hollister@Sun.COM __func__, status, pptr->path);
133710696SDavid.Hollister@Sun.COM result = EIO;
133810696SDavid.Hollister@Sun.COM goto out;
133910696SDavid.Hollister@Sun.COM }
134010696SDavid.Hollister@Sun.COM pptr->device_id = tmp;
134110696SDavid.Hollister@Sun.COM pptr->valid_device_id = 1;
134211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "Phy %s/" SAS_ADDR_FMT
134310696SDavid.Hollister@Sun.COM " registered with device_id 0x%x (portid %d)", pptr->path,
134410696SDavid.Hollister@Sun.COM SAS_ADDR_PRT(pptr->sas_address), tmp, pptr->portid);
134510696SDavid.Hollister@Sun.COM out:
134610696SDavid.Hollister@Sun.COM return (result);
134710696SDavid.Hollister@Sun.COM }
134810696SDavid.Hollister@Sun.COM
134910696SDavid.Hollister@Sun.COM /*
135010696SDavid.Hollister@Sun.COM * Deregister a device (remove a device handle).
135110696SDavid.Hollister@Sun.COM * Called with PHY locked.
135210696SDavid.Hollister@Sun.COM */
135310696SDavid.Hollister@Sun.COM void
pmcs_deregister_device(pmcs_hw_t * pwp,pmcs_phy_t * pptr)135410696SDavid.Hollister@Sun.COM pmcs_deregister_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
135510696SDavid.Hollister@Sun.COM {
135610696SDavid.Hollister@Sun.COM struct pmcwork *pwrk;
135710696SDavid.Hollister@Sun.COM uint32_t msg[PMCS_MSG_SIZE], *ptr, status;
135810696SDavid.Hollister@Sun.COM uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2];
135910696SDavid.Hollister@Sun.COM int result;
136010696SDavid.Hollister@Sun.COM
136110696SDavid.Hollister@Sun.COM pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
136210696SDavid.Hollister@Sun.COM if (pwrk == NULL) {
136310696SDavid.Hollister@Sun.COM return;
136410696SDavid.Hollister@Sun.COM }
136510696SDavid.Hollister@Sun.COM
136610696SDavid.Hollister@Sun.COM pwrk->arg = iomb;
136710696SDavid.Hollister@Sun.COM pwrk->dtype = pptr->dtype;
136810696SDavid.Hollister@Sun.COM mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
136910696SDavid.Hollister@Sun.COM ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
137010696SDavid.Hollister@Sun.COM if (ptr == NULL) {
137110696SDavid.Hollister@Sun.COM mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
137210696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
137310696SDavid.Hollister@Sun.COM return;
137410696SDavid.Hollister@Sun.COM }
137510696SDavid.Hollister@Sun.COM msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
137610696SDavid.Hollister@Sun.COM PMCIN_DEREGISTER_DEVICE_HANDLE));
137710696SDavid.Hollister@Sun.COM msg[1] = LE_32(pwrk->htag);
137810696SDavid.Hollister@Sun.COM msg[2] = LE_32(pptr->device_id);
137910696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_ONCHIP;
138010696SDavid.Hollister@Sun.COM COPY_MESSAGE(ptr, msg, 3);
138110696SDavid.Hollister@Sun.COM INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
138210696SDavid.Hollister@Sun.COM
138310696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
138410696SDavid.Hollister@Sun.COM WAIT_FOR(pwrk, 250, result);
138510696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
138610696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
138710696SDavid.Hollister@Sun.COM
138810696SDavid.Hollister@Sun.COM if (result) {
138911267SJesse.Butler@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, pmcs_timeo, __func__);
139010696SDavid.Hollister@Sun.COM return;
139110696SDavid.Hollister@Sun.COM }
139210696SDavid.Hollister@Sun.COM status = LE_32(iomb[2]);
139310696SDavid.Hollister@Sun.COM if (status != PMCOUT_STATUS_OK) {
139411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
139511048SDavid.Hollister@Sun.COM "%s: status 0x%x when trying to deregister device %s",
139611048SDavid.Hollister@Sun.COM __func__, status, pptr->path);
139710696SDavid.Hollister@Sun.COM } else {
139811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
139911048SDavid.Hollister@Sun.COM "%s: device %s deregistered", __func__, pptr->path);
140012462Sjesse.butler@oracle.com }
140112462Sjesse.butler@oracle.com
140212462Sjesse.butler@oracle.com pptr->device_id = PMCS_INVALID_DEVICE_ID;
140312462Sjesse.butler@oracle.com pptr->configured = 0;
140412462Sjesse.butler@oracle.com pptr->deregister_wait = 0;
140512462Sjesse.butler@oracle.com pptr->valid_device_id = 0;
140610696SDavid.Hollister@Sun.COM }
140710696SDavid.Hollister@Sun.COM
140810696SDavid.Hollister@Sun.COM /*
140910696SDavid.Hollister@Sun.COM * Deregister all registered devices.
141010696SDavid.Hollister@Sun.COM */
141110696SDavid.Hollister@Sun.COM void
pmcs_deregister_devices(pmcs_hw_t * pwp,pmcs_phy_t * phyp)141210696SDavid.Hollister@Sun.COM pmcs_deregister_devices(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
141310696SDavid.Hollister@Sun.COM {
141410696SDavid.Hollister@Sun.COM /*
141510696SDavid.Hollister@Sun.COM * Start at the maximum level and walk back to level 0. This only
141610696SDavid.Hollister@Sun.COM * gets done during detach after all threads and timers have been
141712399SChris.Horne@Sun.COM * destroyed.
141810696SDavid.Hollister@Sun.COM */
141910696SDavid.Hollister@Sun.COM while (phyp) {
142010696SDavid.Hollister@Sun.COM if (phyp->children) {
142110696SDavid.Hollister@Sun.COM pmcs_deregister_devices(pwp, phyp->children);
142210696SDavid.Hollister@Sun.COM }
142312399SChris.Horne@Sun.COM pmcs_lock_phy(phyp);
142410696SDavid.Hollister@Sun.COM if (phyp->valid_device_id) {
142510696SDavid.Hollister@Sun.COM pmcs_deregister_device(pwp, phyp);
142610696SDavid.Hollister@Sun.COM }
142712399SChris.Horne@Sun.COM pmcs_unlock_phy(phyp);
142810696SDavid.Hollister@Sun.COM phyp = phyp->sibling;
142910696SDavid.Hollister@Sun.COM }
143010696SDavid.Hollister@Sun.COM }
143110696SDavid.Hollister@Sun.COM
143210696SDavid.Hollister@Sun.COM /*
143310696SDavid.Hollister@Sun.COM * Perform a 'soft' reset on the PMC chip
143410696SDavid.Hollister@Sun.COM */
143510696SDavid.Hollister@Sun.COM int
pmcs_soft_reset(pmcs_hw_t * pwp,boolean_t no_restart)143610696SDavid.Hollister@Sun.COM pmcs_soft_reset(pmcs_hw_t *pwp, boolean_t no_restart)
143710696SDavid.Hollister@Sun.COM {
143810696SDavid.Hollister@Sun.COM uint32_t s2, sfrbits, gsm, rapchk, wapchk, wdpchk, spc, tsmode;
143910696SDavid.Hollister@Sun.COM pmcs_phy_t *pptr;
144010696SDavid.Hollister@Sun.COM char *msg = NULL;
144110696SDavid.Hollister@Sun.COM int i;
144210696SDavid.Hollister@Sun.COM
144310696SDavid.Hollister@Sun.COM /*
144410696SDavid.Hollister@Sun.COM * Disable interrupts
144510696SDavid.Hollister@Sun.COM */
144610696SDavid.Hollister@Sun.COM pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
144710696SDavid.Hollister@Sun.COM pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
144810696SDavid.Hollister@Sun.COM
144911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "%s", __func__);
145010696SDavid.Hollister@Sun.COM
145110696SDavid.Hollister@Sun.COM if (pwp->locks_initted) {
145210696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
145310696SDavid.Hollister@Sun.COM }
145410696SDavid.Hollister@Sun.COM pwp->blocked = 1;
145510696SDavid.Hollister@Sun.COM
145610696SDavid.Hollister@Sun.COM /*
145711692SJesse.Butler@Sun.COM * Clear our softstate copies of the MSGU and IOP heartbeats.
145811692SJesse.Butler@Sun.COM */
145911692SJesse.Butler@Sun.COM pwp->last_msgu_tick = pwp->last_iop_tick = 0;
146011692SJesse.Butler@Sun.COM
146111692SJesse.Butler@Sun.COM /*
146210696SDavid.Hollister@Sun.COM * Step 1
146310696SDavid.Hollister@Sun.COM */
146410696SDavid.Hollister@Sun.COM s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2);
146510696SDavid.Hollister@Sun.COM if ((s2 & PMCS_MSGU_HOST_SOFT_RESET_READY) == 0) {
146610696SDavid.Hollister@Sun.COM pmcs_wr_gsm_reg(pwp, RB6_ACCESS, RB6_NMI_SIGNATURE);
146710696SDavid.Hollister@Sun.COM pmcs_wr_gsm_reg(pwp, RB6_ACCESS, RB6_NMI_SIGNATURE);
146810696SDavid.Hollister@Sun.COM for (i = 0; i < 100; i++) {
146910696SDavid.Hollister@Sun.COM s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) &
147010696SDavid.Hollister@Sun.COM PMCS_MSGU_HOST_SOFT_RESET_READY;
147110696SDavid.Hollister@Sun.COM if (s2) {
147210696SDavid.Hollister@Sun.COM break;
147310696SDavid.Hollister@Sun.COM }
147410696SDavid.Hollister@Sun.COM drv_usecwait(10000);
147510696SDavid.Hollister@Sun.COM }
147610696SDavid.Hollister@Sun.COM s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) &
147710696SDavid.Hollister@Sun.COM PMCS_MSGU_HOST_SOFT_RESET_READY;
147810696SDavid.Hollister@Sun.COM if (s2 == 0) {
147911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
148011048SDavid.Hollister@Sun.COM "%s: PMCS_MSGU_HOST_SOFT_RESET_READY never came "
148111048SDavid.Hollister@Sun.COM "ready", __func__);
148210696SDavid.Hollister@Sun.COM pmcs_register_dump(pwp);
148310696SDavid.Hollister@Sun.COM if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
148410696SDavid.Hollister@Sun.COM PMCS_MSGU_CPU_SOFT_RESET_READY) == 0 ||
148510696SDavid.Hollister@Sun.COM (pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) &
148610696SDavid.Hollister@Sun.COM PMCS_MSGU_CPU_SOFT_RESET_READY) == 0) {
148710696SDavid.Hollister@Sun.COM pwp->state = STATE_DEAD;
148810696SDavid.Hollister@Sun.COM pwp->blocked = 0;
148910696SDavid.Hollister@Sun.COM if (pwp->locks_initted) {
149010696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
149110696SDavid.Hollister@Sun.COM }
149210696SDavid.Hollister@Sun.COM return (-1);
149310696SDavid.Hollister@Sun.COM }
149410696SDavid.Hollister@Sun.COM }
149510696SDavid.Hollister@Sun.COM }
149610696SDavid.Hollister@Sun.COM
149710696SDavid.Hollister@Sun.COM /*
149810696SDavid.Hollister@Sun.COM * Step 2
149910696SDavid.Hollister@Sun.COM */
150010696SDavid.Hollister@Sun.COM pmcs_wr_gsm_reg(pwp, NMI_EN_VPE0_IOP, 0);
150110696SDavid.Hollister@Sun.COM drv_usecwait(10);
150210696SDavid.Hollister@Sun.COM pmcs_wr_gsm_reg(pwp, NMI_EN_VPE0_AAP1, 0);
150310696SDavid.Hollister@Sun.COM drv_usecwait(10);
150410696SDavid.Hollister@Sun.COM pmcs_wr_topunit(pwp, PMCS_EVENT_INT_ENABLE, 0);
150510696SDavid.Hollister@Sun.COM drv_usecwait(10);
150610696SDavid.Hollister@Sun.COM pmcs_wr_topunit(pwp, PMCS_EVENT_INT_STAT,
150710696SDavid.Hollister@Sun.COM pmcs_rd_topunit(pwp, PMCS_EVENT_INT_STAT));
150810696SDavid.Hollister@Sun.COM drv_usecwait(10);
150910696SDavid.Hollister@Sun.COM pmcs_wr_topunit(pwp, PMCS_ERROR_INT_ENABLE, 0);
151010696SDavid.Hollister@Sun.COM drv_usecwait(10);
151110696SDavid.Hollister@Sun.COM pmcs_wr_topunit(pwp, PMCS_ERROR_INT_STAT,
151210696SDavid.Hollister@Sun.COM pmcs_rd_topunit(pwp, PMCS_ERROR_INT_STAT));
151310696SDavid.Hollister@Sun.COM drv_usecwait(10);
151410696SDavid.Hollister@Sun.COM
151510696SDavid.Hollister@Sun.COM sfrbits = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
151610696SDavid.Hollister@Sun.COM PMCS_MSGU_AAP_SFR_PROGRESS;
151710696SDavid.Hollister@Sun.COM sfrbits ^= PMCS_MSGU_AAP_SFR_PROGRESS;
151811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "PMCS_MSGU_HOST_SCRATCH0 "
151911048SDavid.Hollister@Sun.COM "%08x -> %08x", pmcs_rd_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH0),
152011048SDavid.Hollister@Sun.COM HST_SFT_RESET_SIG);
152110696SDavid.Hollister@Sun.COM pmcs_wr_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH0, HST_SFT_RESET_SIG);
152210696SDavid.Hollister@Sun.COM
152310696SDavid.Hollister@Sun.COM /*
152410696SDavid.Hollister@Sun.COM * Step 3
152510696SDavid.Hollister@Sun.COM */
152611980SDavid.Hollister@Sun.COM gsm = pmcs_rd_gsm_reg(pwp, 0, GSM_CFG_AND_RESET);
152711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "GSM %08x -> %08x", gsm,
152810696SDavid.Hollister@Sun.COM gsm & ~PMCS_SOFT_RESET_BITS);
152910696SDavid.Hollister@Sun.COM pmcs_wr_gsm_reg(pwp, GSM_CFG_AND_RESET, gsm & ~PMCS_SOFT_RESET_BITS);
153010696SDavid.Hollister@Sun.COM
153110696SDavid.Hollister@Sun.COM /*
153210696SDavid.Hollister@Sun.COM * Step 4
153310696SDavid.Hollister@Sun.COM */
153411980SDavid.Hollister@Sun.COM rapchk = pmcs_rd_gsm_reg(pwp, 0, READ_ADR_PARITY_CHK_EN);
153511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "READ_ADR_PARITY_CHK_EN "
153611048SDavid.Hollister@Sun.COM "%08x -> %08x", rapchk, 0);
153710696SDavid.Hollister@Sun.COM pmcs_wr_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN, 0);
153811980SDavid.Hollister@Sun.COM wapchk = pmcs_rd_gsm_reg(pwp, 0, WRITE_ADR_PARITY_CHK_EN);
153911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_ADR_PARITY_CHK_EN "
154011048SDavid.Hollister@Sun.COM "%08x -> %08x", wapchk, 0);
154110696SDavid.Hollister@Sun.COM pmcs_wr_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN, 0);
154211980SDavid.Hollister@Sun.COM wdpchk = pmcs_rd_gsm_reg(pwp, 0, WRITE_DATA_PARITY_CHK_EN);
154311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_DATA_PARITY_CHK_EN "
154411048SDavid.Hollister@Sun.COM "%08x -> %08x", wdpchk, 0);
154510696SDavid.Hollister@Sun.COM pmcs_wr_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN, 0);
154610696SDavid.Hollister@Sun.COM
154710696SDavid.Hollister@Sun.COM /*
154810696SDavid.Hollister@Sun.COM * Step 5
154910696SDavid.Hollister@Sun.COM */
155010696SDavid.Hollister@Sun.COM drv_usecwait(100);
155110696SDavid.Hollister@Sun.COM
155210696SDavid.Hollister@Sun.COM /*
155310696SDavid.Hollister@Sun.COM * Step 5.5 (Temporary workaround for 1.07.xx Beta)
155410696SDavid.Hollister@Sun.COM */
155511980SDavid.Hollister@Sun.COM tsmode = pmcs_rd_gsm_reg(pwp, 0, PMCS_GPIO_TRISTATE_MODE_ADDR);
155611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "GPIO TSMODE %08x -> %08x",
155711048SDavid.Hollister@Sun.COM tsmode, tsmode & ~(PMCS_GPIO_TSMODE_BIT0|PMCS_GPIO_TSMODE_BIT1));
155810696SDavid.Hollister@Sun.COM pmcs_wr_gsm_reg(pwp, PMCS_GPIO_TRISTATE_MODE_ADDR,
155910696SDavid.Hollister@Sun.COM tsmode & ~(PMCS_GPIO_TSMODE_BIT0|PMCS_GPIO_TSMODE_BIT1));
156010696SDavid.Hollister@Sun.COM drv_usecwait(10);
156110696SDavid.Hollister@Sun.COM
156210696SDavid.Hollister@Sun.COM /*
156310696SDavid.Hollister@Sun.COM * Step 6
156410696SDavid.Hollister@Sun.COM */
156510696SDavid.Hollister@Sun.COM spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
156611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x",
156711048SDavid.Hollister@Sun.COM spc, spc & ~(PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
156810696SDavid.Hollister@Sun.COM pmcs_wr_topunit(pwp, PMCS_SPC_RESET,
156910696SDavid.Hollister@Sun.COM spc & ~(PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
157010696SDavid.Hollister@Sun.COM drv_usecwait(10);
157110696SDavid.Hollister@Sun.COM
157210696SDavid.Hollister@Sun.COM /*
157310696SDavid.Hollister@Sun.COM * Step 7
157410696SDavid.Hollister@Sun.COM */
157510696SDavid.Hollister@Sun.COM spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
157611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x",
157711048SDavid.Hollister@Sun.COM spc, spc & ~(BDMA_CORE_RSTB|OSSP_RSTB));
157810696SDavid.Hollister@Sun.COM pmcs_wr_topunit(pwp, PMCS_SPC_RESET, spc & ~(BDMA_CORE_RSTB|OSSP_RSTB));
157910696SDavid.Hollister@Sun.COM
158010696SDavid.Hollister@Sun.COM /*
158110696SDavid.Hollister@Sun.COM * Step 8
158210696SDavid.Hollister@Sun.COM */
158310696SDavid.Hollister@Sun.COM drv_usecwait(100);
158410696SDavid.Hollister@Sun.COM
158510696SDavid.Hollister@Sun.COM /*
158610696SDavid.Hollister@Sun.COM * Step 9
158710696SDavid.Hollister@Sun.COM */
158810696SDavid.Hollister@Sun.COM spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
158911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x",
159011048SDavid.Hollister@Sun.COM spc, spc | (BDMA_CORE_RSTB|OSSP_RSTB));
159110696SDavid.Hollister@Sun.COM pmcs_wr_topunit(pwp, PMCS_SPC_RESET, spc | (BDMA_CORE_RSTB|OSSP_RSTB));
159210696SDavid.Hollister@Sun.COM
159310696SDavid.Hollister@Sun.COM /*
159410696SDavid.Hollister@Sun.COM * Step 10
159510696SDavid.Hollister@Sun.COM */
159610696SDavid.Hollister@Sun.COM drv_usecwait(100);
159710696SDavid.Hollister@Sun.COM
159810696SDavid.Hollister@Sun.COM /*
159910696SDavid.Hollister@Sun.COM * Step 11
160010696SDavid.Hollister@Sun.COM */
160111980SDavid.Hollister@Sun.COM gsm = pmcs_rd_gsm_reg(pwp, 0, GSM_CFG_AND_RESET);
160211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "GSM %08x -> %08x", gsm,
160310696SDavid.Hollister@Sun.COM gsm | PMCS_SOFT_RESET_BITS);
160410696SDavid.Hollister@Sun.COM pmcs_wr_gsm_reg(pwp, GSM_CFG_AND_RESET, gsm | PMCS_SOFT_RESET_BITS);
160510696SDavid.Hollister@Sun.COM drv_usecwait(10);
160610696SDavid.Hollister@Sun.COM
160710696SDavid.Hollister@Sun.COM /*
160810696SDavid.Hollister@Sun.COM * Step 12
160910696SDavid.Hollister@Sun.COM */
161011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "READ_ADR_PARITY_CHK_EN "
161111980SDavid.Hollister@Sun.COM "%08x -> %08x", pmcs_rd_gsm_reg(pwp, 0, READ_ADR_PARITY_CHK_EN),
161211048SDavid.Hollister@Sun.COM rapchk);
161310696SDavid.Hollister@Sun.COM pmcs_wr_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN, rapchk);
161410696SDavid.Hollister@Sun.COM drv_usecwait(10);
161511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_ADR_PARITY_CHK_EN "
161611980SDavid.Hollister@Sun.COM "%08x -> %08x", pmcs_rd_gsm_reg(pwp, 0, WRITE_ADR_PARITY_CHK_EN),
161711048SDavid.Hollister@Sun.COM wapchk);
161810696SDavid.Hollister@Sun.COM pmcs_wr_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN, wapchk);
161910696SDavid.Hollister@Sun.COM drv_usecwait(10);
162011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "WRITE_DATA_PARITY_CHK_EN "
162111980SDavid.Hollister@Sun.COM "%08x -> %08x", pmcs_rd_gsm_reg(pwp, 0, WRITE_DATA_PARITY_CHK_EN),
162211048SDavid.Hollister@Sun.COM wapchk);
162310696SDavid.Hollister@Sun.COM pmcs_wr_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN, wdpchk);
162410696SDavid.Hollister@Sun.COM drv_usecwait(10);
162510696SDavid.Hollister@Sun.COM
162610696SDavid.Hollister@Sun.COM /*
162710696SDavid.Hollister@Sun.COM * Step 13
162810696SDavid.Hollister@Sun.COM */
162910696SDavid.Hollister@Sun.COM spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
163011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "SPC_RESET %08x -> %08x",
163111048SDavid.Hollister@Sun.COM spc, spc | (PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
163210696SDavid.Hollister@Sun.COM pmcs_wr_topunit(pwp, PMCS_SPC_RESET,
163310696SDavid.Hollister@Sun.COM spc | (PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
163410696SDavid.Hollister@Sun.COM
163510696SDavid.Hollister@Sun.COM /*
163610696SDavid.Hollister@Sun.COM * Step 14
163710696SDavid.Hollister@Sun.COM */
163810696SDavid.Hollister@Sun.COM drv_usecwait(100);
163910696SDavid.Hollister@Sun.COM
164010696SDavid.Hollister@Sun.COM /*
164110696SDavid.Hollister@Sun.COM * Step 15
164210696SDavid.Hollister@Sun.COM */
164310696SDavid.Hollister@Sun.COM for (spc = 0, i = 0; i < 1000; i++) {
164410696SDavid.Hollister@Sun.COM drv_usecwait(1000);
164510696SDavid.Hollister@Sun.COM spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1);
164610696SDavid.Hollister@Sun.COM if ((spc & PMCS_MSGU_AAP_SFR_PROGRESS) == sfrbits) {
164710696SDavid.Hollister@Sun.COM break;
164810696SDavid.Hollister@Sun.COM }
164910696SDavid.Hollister@Sun.COM }
165010696SDavid.Hollister@Sun.COM
165110696SDavid.Hollister@Sun.COM if ((spc & PMCS_MSGU_AAP_SFR_PROGRESS) != sfrbits) {
165211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
165310696SDavid.Hollister@Sun.COM "SFR didn't toggle (sfr 0x%x)", spc);
165410696SDavid.Hollister@Sun.COM pwp->state = STATE_DEAD;
165510696SDavid.Hollister@Sun.COM pwp->blocked = 0;
165610696SDavid.Hollister@Sun.COM if (pwp->locks_initted) {
165710696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
165810696SDavid.Hollister@Sun.COM }
165910696SDavid.Hollister@Sun.COM return (-1);
166010696SDavid.Hollister@Sun.COM }
166110696SDavid.Hollister@Sun.COM
166210696SDavid.Hollister@Sun.COM /*
166310696SDavid.Hollister@Sun.COM * Step 16
166410696SDavid.Hollister@Sun.COM */
166510696SDavid.Hollister@Sun.COM pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
166610696SDavid.Hollister@Sun.COM pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
166710696SDavid.Hollister@Sun.COM
166810696SDavid.Hollister@Sun.COM /*
166910696SDavid.Hollister@Sun.COM * Wait for up to 5 seconds for AAP state to come either ready or error.
167010696SDavid.Hollister@Sun.COM */
167110696SDavid.Hollister@Sun.COM for (i = 0; i < 50; i++) {
167210696SDavid.Hollister@Sun.COM spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
167310696SDavid.Hollister@Sun.COM PMCS_MSGU_AAP_STATE_MASK;
167410696SDavid.Hollister@Sun.COM if (spc == PMCS_MSGU_AAP_STATE_ERROR ||
167510696SDavid.Hollister@Sun.COM spc == PMCS_MSGU_AAP_STATE_READY) {
167610696SDavid.Hollister@Sun.COM break;
167710696SDavid.Hollister@Sun.COM }
167810696SDavid.Hollister@Sun.COM drv_usecwait(100000);
167910696SDavid.Hollister@Sun.COM }
168010696SDavid.Hollister@Sun.COM spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1);
168110696SDavid.Hollister@Sun.COM if ((spc & PMCS_MSGU_AAP_STATE_MASK) != PMCS_MSGU_AAP_STATE_READY) {
168211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
168310696SDavid.Hollister@Sun.COM "soft reset failed (state 0x%x)", spc);
168410696SDavid.Hollister@Sun.COM pwp->state = STATE_DEAD;
168510696SDavid.Hollister@Sun.COM pwp->blocked = 0;
168610696SDavid.Hollister@Sun.COM if (pwp->locks_initted) {
168710696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
168810696SDavid.Hollister@Sun.COM }
168910696SDavid.Hollister@Sun.COM return (-1);
169010696SDavid.Hollister@Sun.COM }
169110696SDavid.Hollister@Sun.COM
169211692SJesse.Butler@Sun.COM /* Clear the firmware log */
169311692SJesse.Butler@Sun.COM if (pwp->fwlogp) {
169411692SJesse.Butler@Sun.COM bzero(pwp->fwlogp, PMCS_FWLOG_SIZE);
169511692SJesse.Butler@Sun.COM }
169611692SJesse.Butler@Sun.COM
169711692SJesse.Butler@Sun.COM /* Reset our queue indices and entries */
169811692SJesse.Butler@Sun.COM bzero(pwp->shadow_iqpi, sizeof (pwp->shadow_iqpi));
169911692SJesse.Butler@Sun.COM bzero(pwp->last_iqci, sizeof (pwp->last_iqci));
170011832SJesse.Butler@Sun.COM bzero(pwp->last_htag, sizeof (pwp->last_htag));
170111692SJesse.Butler@Sun.COM for (i = 0; i < PMCS_NIQ; i++) {
170211692SJesse.Butler@Sun.COM if (pwp->iqp[i]) {
170311692SJesse.Butler@Sun.COM bzero(pwp->iqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
170411692SJesse.Butler@Sun.COM pmcs_wr_iqpi(pwp, i, 0);
170511692SJesse.Butler@Sun.COM pmcs_wr_iqci(pwp, i, 0);
170611692SJesse.Butler@Sun.COM }
170711692SJesse.Butler@Sun.COM }
170811692SJesse.Butler@Sun.COM for (i = 0; i < PMCS_NOQ; i++) {
170911692SJesse.Butler@Sun.COM if (pwp->oqp[i]) {
171011692SJesse.Butler@Sun.COM bzero(pwp->oqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
171111692SJesse.Butler@Sun.COM pmcs_wr_oqpi(pwp, i, 0);
171211692SJesse.Butler@Sun.COM pmcs_wr_oqci(pwp, i, 0);
171311692SJesse.Butler@Sun.COM }
171411692SJesse.Butler@Sun.COM
171511692SJesse.Butler@Sun.COM }
171610696SDavid.Hollister@Sun.COM
171710696SDavid.Hollister@Sun.COM if (pwp->state == STATE_DEAD || pwp->state == STATE_UNPROBING ||
171810696SDavid.Hollister@Sun.COM pwp->state == STATE_PROBING || pwp->locks_initted == 0) {
171910696SDavid.Hollister@Sun.COM pwp->blocked = 0;
172010696SDavid.Hollister@Sun.COM if (pwp->locks_initted) {
172110696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
172210696SDavid.Hollister@Sun.COM }
172310696SDavid.Hollister@Sun.COM return (0);
172410696SDavid.Hollister@Sun.COM }
172510696SDavid.Hollister@Sun.COM
172610696SDavid.Hollister@Sun.COM /*
172710696SDavid.Hollister@Sun.COM * Return at this point if we dont need to startup.
172810696SDavid.Hollister@Sun.COM */
172910696SDavid.Hollister@Sun.COM if (no_restart) {
173010696SDavid.Hollister@Sun.COM return (0);
173110696SDavid.Hollister@Sun.COM }
173210696SDavid.Hollister@Sun.COM
173310696SDavid.Hollister@Sun.COM ASSERT(pwp->locks_initted != 0);
173410696SDavid.Hollister@Sun.COM
173510696SDavid.Hollister@Sun.COM /*
173611692SJesse.Butler@Sun.COM * Flush the target queues and clear each target's PHY
173711692SJesse.Butler@Sun.COM */
173810696SDavid.Hollister@Sun.COM if (pwp->targets) {
173910696SDavid.Hollister@Sun.COM for (i = 0; i < pwp->max_dev; i++) {
174010696SDavid.Hollister@Sun.COM pmcs_xscsi_t *xp = pwp->targets[i];
174110696SDavid.Hollister@Sun.COM
174210696SDavid.Hollister@Sun.COM if (xp == NULL) {
174310696SDavid.Hollister@Sun.COM continue;
174410696SDavid.Hollister@Sun.COM }
174511692SJesse.Butler@Sun.COM
174610696SDavid.Hollister@Sun.COM mutex_enter(&xp->statlock);
174711692SJesse.Butler@Sun.COM pmcs_flush_target_queues(pwp, xp, PMCS_TGT_ALL_QUEUES);
174811692SJesse.Butler@Sun.COM xp->phy = NULL;
174910696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
175010696SDavid.Hollister@Sun.COM }
175110696SDavid.Hollister@Sun.COM }
175210696SDavid.Hollister@Sun.COM
175311692SJesse.Butler@Sun.COM /*
175411692SJesse.Butler@Sun.COM * Zero out the ports list, free non root phys, clear root phys
175511692SJesse.Butler@Sun.COM */
175611692SJesse.Butler@Sun.COM bzero(pwp->ports, sizeof (pwp->ports));
175711692SJesse.Butler@Sun.COM pmcs_free_all_phys(pwp, pwp->root_phys);
175811692SJesse.Butler@Sun.COM for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
175911692SJesse.Butler@Sun.COM pmcs_lock_phy(pptr);
176011692SJesse.Butler@Sun.COM pmcs_clear_phy(pwp, pptr);
176111692SJesse.Butler@Sun.COM pptr->target = NULL;
176211692SJesse.Butler@Sun.COM pmcs_unlock_phy(pptr);
176310696SDavid.Hollister@Sun.COM }
176410696SDavid.Hollister@Sun.COM
176510696SDavid.Hollister@Sun.COM /*
176610696SDavid.Hollister@Sun.COM * Restore Interrupt Mask
176710696SDavid.Hollister@Sun.COM */
176810696SDavid.Hollister@Sun.COM pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, pwp->intr_mask);
176910696SDavid.Hollister@Sun.COM pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
177010696SDavid.Hollister@Sun.COM
177110696SDavid.Hollister@Sun.COM pwp->mpi_table_setup = 0;
177210696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
177310696SDavid.Hollister@Sun.COM
177410696SDavid.Hollister@Sun.COM /*
177510696SDavid.Hollister@Sun.COM * Set up MPI again.
177610696SDavid.Hollister@Sun.COM */
177710696SDavid.Hollister@Sun.COM if (pmcs_setup(pwp)) {
177810696SDavid.Hollister@Sun.COM msg = "unable to setup MPI tables again";
177910696SDavid.Hollister@Sun.COM goto fail_restart;
178010696SDavid.Hollister@Sun.COM }
178110696SDavid.Hollister@Sun.COM pmcs_report_fwversion(pwp);
178210696SDavid.Hollister@Sun.COM
178310696SDavid.Hollister@Sun.COM /*
178410696SDavid.Hollister@Sun.COM * Restart MPI
178510696SDavid.Hollister@Sun.COM */
178610696SDavid.Hollister@Sun.COM if (pmcs_start_mpi(pwp)) {
178710696SDavid.Hollister@Sun.COM msg = "unable to restart MPI again";
178810696SDavid.Hollister@Sun.COM goto fail_restart;
178910696SDavid.Hollister@Sun.COM }
179010696SDavid.Hollister@Sun.COM
179110696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
179210696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
179310696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
179410696SDavid.Hollister@Sun.COM
179510696SDavid.Hollister@Sun.COM /*
179610696SDavid.Hollister@Sun.COM * Run any completions
179710696SDavid.Hollister@Sun.COM */
179810696SDavid.Hollister@Sun.COM PMCS_CQ_RUN(pwp);
179910696SDavid.Hollister@Sun.COM
180010696SDavid.Hollister@Sun.COM /*
180110696SDavid.Hollister@Sun.COM * Delay
180210696SDavid.Hollister@Sun.COM */
180310696SDavid.Hollister@Sun.COM drv_usecwait(1000000);
180410696SDavid.Hollister@Sun.COM return (0);
180510696SDavid.Hollister@Sun.COM
180610696SDavid.Hollister@Sun.COM fail_restart:
180710696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
180810696SDavid.Hollister@Sun.COM pwp->state = STATE_DEAD;
180910696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
181011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL,
181111048SDavid.Hollister@Sun.COM "%s: Failed: %s", __func__, msg);
181210696SDavid.Hollister@Sun.COM return (-1);
181310696SDavid.Hollister@Sun.COM }
181410696SDavid.Hollister@Sun.COM
181511692SJesse.Butler@Sun.COM
181611692SJesse.Butler@Sun.COM /*
181711692SJesse.Butler@Sun.COM * Perform a 'hot' reset, which will soft reset the chip and
181811692SJesse.Butler@Sun.COM * restore the state back to pre-reset context. Called with pwp
181911692SJesse.Butler@Sun.COM * lock held.
182011692SJesse.Butler@Sun.COM */
182111692SJesse.Butler@Sun.COM int
pmcs_hot_reset(pmcs_hw_t * pwp)182211692SJesse.Butler@Sun.COM pmcs_hot_reset(pmcs_hw_t *pwp)
182311692SJesse.Butler@Sun.COM {
182411692SJesse.Butler@Sun.COM pmcs_iport_t *iport;
182511692SJesse.Butler@Sun.COM
182611692SJesse.Butler@Sun.COM ASSERT(mutex_owned(&pwp->lock));
182711692SJesse.Butler@Sun.COM pwp->state = STATE_IN_RESET;
182811692SJesse.Butler@Sun.COM
182911692SJesse.Butler@Sun.COM /*
183011692SJesse.Butler@Sun.COM * For any iports on this HBA, report empty target sets and
183111692SJesse.Butler@Sun.COM * then tear them down.
183211692SJesse.Butler@Sun.COM */
183311692SJesse.Butler@Sun.COM rw_enter(&pwp->iports_lock, RW_READER);
183411692SJesse.Butler@Sun.COM for (iport = list_head(&pwp->iports); iport != NULL;
183511692SJesse.Butler@Sun.COM iport = list_next(&pwp->iports, iport)) {
183611692SJesse.Butler@Sun.COM mutex_enter(&iport->lock);
183711692SJesse.Butler@Sun.COM (void) scsi_hba_tgtmap_set_begin(iport->iss_tgtmap);
183811692SJesse.Butler@Sun.COM (void) scsi_hba_tgtmap_set_end(iport->iss_tgtmap, 0);
183911692SJesse.Butler@Sun.COM pmcs_iport_teardown_phys(iport);
184011692SJesse.Butler@Sun.COM mutex_exit(&iport->lock);
184111692SJesse.Butler@Sun.COM }
184211692SJesse.Butler@Sun.COM rw_exit(&pwp->iports_lock);
184311692SJesse.Butler@Sun.COM
184411692SJesse.Butler@Sun.COM /* Grab a register dump, in the event that reset fails */
184511692SJesse.Butler@Sun.COM pmcs_register_dump_int(pwp);
184611692SJesse.Butler@Sun.COM mutex_exit(&pwp->lock);
184711692SJesse.Butler@Sun.COM
184812078SJesse.Butler@Sun.COM /* Ensure discovery is not running before we proceed */
184912078SJesse.Butler@Sun.COM mutex_enter(&pwp->config_lock);
185012078SJesse.Butler@Sun.COM while (pwp->configuring) {
185112078SJesse.Butler@Sun.COM cv_wait(&pwp->config_cv, &pwp->config_lock);
185212078SJesse.Butler@Sun.COM }
185312078SJesse.Butler@Sun.COM mutex_exit(&pwp->config_lock);
185412078SJesse.Butler@Sun.COM
185511692SJesse.Butler@Sun.COM /* Issue soft reset and clean up related softstate */
185611692SJesse.Butler@Sun.COM if (pmcs_soft_reset(pwp, B_FALSE)) {
185711692SJesse.Butler@Sun.COM /*
185811692SJesse.Butler@Sun.COM * Disable interrupts, in case we got far enough along to
185911692SJesse.Butler@Sun.COM * enable them, then fire off ereport and service impact.
186011692SJesse.Butler@Sun.COM */
186111692SJesse.Butler@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
186211692SJesse.Butler@Sun.COM "%s: failed soft reset", __func__);
186311692SJesse.Butler@Sun.COM pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
186411692SJesse.Butler@Sun.COM pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
186511692SJesse.Butler@Sun.COM pmcs_fm_ereport(pwp, DDI_FM_DEVICE_NO_RESPONSE);
186611692SJesse.Butler@Sun.COM ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
186711692SJesse.Butler@Sun.COM mutex_enter(&pwp->lock);
186811692SJesse.Butler@Sun.COM pwp->state = STATE_DEAD;
186911692SJesse.Butler@Sun.COM return (DDI_FAILURE);
187011692SJesse.Butler@Sun.COM }
187111692SJesse.Butler@Sun.COM
187211692SJesse.Butler@Sun.COM mutex_enter(&pwp->lock);
187311692SJesse.Butler@Sun.COM pwp->state = STATE_RUNNING;
187411692SJesse.Butler@Sun.COM mutex_exit(&pwp->lock);
187511692SJesse.Butler@Sun.COM
187611692SJesse.Butler@Sun.COM /*
187711692SJesse.Butler@Sun.COM * Finally, restart the phys, which will bring the iports back
187811692SJesse.Butler@Sun.COM * up and eventually result in discovery running.
187911692SJesse.Butler@Sun.COM */
188011692SJesse.Butler@Sun.COM if (pmcs_start_phys(pwp)) {
188111692SJesse.Butler@Sun.COM /* We should be up and running now, so retry */
188211692SJesse.Butler@Sun.COM if (pmcs_start_phys(pwp)) {
188311692SJesse.Butler@Sun.COM /* Apparently unable to restart PHYs, fail */
188411692SJesse.Butler@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
188511692SJesse.Butler@Sun.COM "%s: failed to restart PHYs after soft reset",
188611692SJesse.Butler@Sun.COM __func__);
188711692SJesse.Butler@Sun.COM mutex_enter(&pwp->lock);
188811692SJesse.Butler@Sun.COM return (DDI_FAILURE);
188911692SJesse.Butler@Sun.COM }
189011692SJesse.Butler@Sun.COM }
189111692SJesse.Butler@Sun.COM
189211692SJesse.Butler@Sun.COM mutex_enter(&pwp->lock);
189311692SJesse.Butler@Sun.COM return (DDI_SUCCESS);
189411692SJesse.Butler@Sun.COM }
189511692SJesse.Butler@Sun.COM
189610696SDavid.Hollister@Sun.COM /*
189710696SDavid.Hollister@Sun.COM * Reset a device or a logical unit.
189810696SDavid.Hollister@Sun.COM */
189910696SDavid.Hollister@Sun.COM int
pmcs_reset_dev(pmcs_hw_t * pwp,pmcs_phy_t * pptr,uint64_t lun)190010696SDavid.Hollister@Sun.COM pmcs_reset_dev(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint64_t lun)
190110696SDavid.Hollister@Sun.COM {
190210696SDavid.Hollister@Sun.COM int rval = 0;
190310696SDavid.Hollister@Sun.COM
190410696SDavid.Hollister@Sun.COM if (pptr == NULL) {
190510696SDavid.Hollister@Sun.COM return (ENXIO);
190610696SDavid.Hollister@Sun.COM }
190710696SDavid.Hollister@Sun.COM
190810696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
190910696SDavid.Hollister@Sun.COM if (pptr->dtype == SAS) {
191010696SDavid.Hollister@Sun.COM /*
191110696SDavid.Hollister@Sun.COM * Some devices do not support SAS_I_T_NEXUS_RESET as
191210696SDavid.Hollister@Sun.COM * it is not a mandatory (in SAM4) task management
191310696SDavid.Hollister@Sun.COM * function, while LOGIC_UNIT_RESET is mandatory.
191410696SDavid.Hollister@Sun.COM *
191510696SDavid.Hollister@Sun.COM * The problem here is that we need to iterate over
191610696SDavid.Hollister@Sun.COM * all known LUNs to emulate the semantics of
191710696SDavid.Hollister@Sun.COM * "RESET_TARGET".
191810696SDavid.Hollister@Sun.COM *
191910696SDavid.Hollister@Sun.COM * XXX: FIX ME
192010696SDavid.Hollister@Sun.COM */
192110696SDavid.Hollister@Sun.COM if (lun == (uint64_t)-1) {
192210696SDavid.Hollister@Sun.COM lun = 0;
192310696SDavid.Hollister@Sun.COM }
192410696SDavid.Hollister@Sun.COM rval = pmcs_ssp_tmf(pwp, pptr, SAS_LOGICAL_UNIT_RESET, 0, lun,
192510696SDavid.Hollister@Sun.COM NULL);
192610696SDavid.Hollister@Sun.COM } else if (pptr->dtype == SATA) {
192710696SDavid.Hollister@Sun.COM if (lun != 0ull) {
192810696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
192910696SDavid.Hollister@Sun.COM return (EINVAL);
193010696SDavid.Hollister@Sun.COM }
193110696SDavid.Hollister@Sun.COM rval = pmcs_reset_phy(pwp, pptr, PMCS_PHYOP_LINK_RESET);
193210696SDavid.Hollister@Sun.COM } else {
193310696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
193411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
193510696SDavid.Hollister@Sun.COM "%s: cannot reset a SMP device yet (%s)",
193610696SDavid.Hollister@Sun.COM __func__, pptr->path);
193710696SDavid.Hollister@Sun.COM return (EINVAL);
193810696SDavid.Hollister@Sun.COM }
193910696SDavid.Hollister@Sun.COM
194010696SDavid.Hollister@Sun.COM /*
194110696SDavid.Hollister@Sun.COM * Now harvest any commands killed by this action
194210696SDavid.Hollister@Sun.COM * by issuing an ABORT for all commands on this device.
194310696SDavid.Hollister@Sun.COM *
194410696SDavid.Hollister@Sun.COM * We do this even if the the tmf or reset fails (in case there
194510696SDavid.Hollister@Sun.COM * are any dead commands around to be harvested *anyway*).
194610696SDavid.Hollister@Sun.COM * We don't have to await for the abort to complete.
194710696SDavid.Hollister@Sun.COM */
194810696SDavid.Hollister@Sun.COM if (pmcs_abort(pwp, pptr, 0, 1, 0)) {
194910696SDavid.Hollister@Sun.COM pptr->abort_pending = 1;
195010696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
195110696SDavid.Hollister@Sun.COM }
195210696SDavid.Hollister@Sun.COM
195310696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
195410696SDavid.Hollister@Sun.COM return (rval);
195510696SDavid.Hollister@Sun.COM }
195610696SDavid.Hollister@Sun.COM
195710696SDavid.Hollister@Sun.COM /*
195810696SDavid.Hollister@Sun.COM * Called with PHY locked.
195910696SDavid.Hollister@Sun.COM */
196010696SDavid.Hollister@Sun.COM static int
pmcs_get_device_handle(pmcs_hw_t * pwp,pmcs_phy_t * pptr)196110696SDavid.Hollister@Sun.COM pmcs_get_device_handle(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
196210696SDavid.Hollister@Sun.COM {
196310696SDavid.Hollister@Sun.COM if (pptr->valid_device_id == 0) {
196410696SDavid.Hollister@Sun.COM int result = pmcs_register_device(pwp, pptr);
196510696SDavid.Hollister@Sun.COM
196610696SDavid.Hollister@Sun.COM /*
196710696SDavid.Hollister@Sun.COM * If we changed while registering, punt
196810696SDavid.Hollister@Sun.COM */
196910696SDavid.Hollister@Sun.COM if (pptr->changed) {
197010696SDavid.Hollister@Sun.COM RESTART_DISCOVERY(pwp);
197110696SDavid.Hollister@Sun.COM return (-1);
197210696SDavid.Hollister@Sun.COM }
197310696SDavid.Hollister@Sun.COM
197410696SDavid.Hollister@Sun.COM /*
197510696SDavid.Hollister@Sun.COM * If we had a failure to register, check against errors.
197610696SDavid.Hollister@Sun.COM * An ENOMEM error means we just retry (temp resource shortage).
197710696SDavid.Hollister@Sun.COM */
197810696SDavid.Hollister@Sun.COM if (result == ENOMEM) {
197910696SDavid.Hollister@Sun.COM PHY_CHANGED(pwp, pptr);
198010696SDavid.Hollister@Sun.COM RESTART_DISCOVERY(pwp);
198110696SDavid.Hollister@Sun.COM return (-1);
198210696SDavid.Hollister@Sun.COM }
198310696SDavid.Hollister@Sun.COM
198410696SDavid.Hollister@Sun.COM /*
198510696SDavid.Hollister@Sun.COM * An ETIMEDOUT error means we retry (if our counter isn't
198610696SDavid.Hollister@Sun.COM * exhausted)
198710696SDavid.Hollister@Sun.COM */
198810696SDavid.Hollister@Sun.COM if (result == ETIMEDOUT) {
198910696SDavid.Hollister@Sun.COM if (ddi_get_lbolt() < pptr->config_stop) {
199010696SDavid.Hollister@Sun.COM PHY_CHANGED(pwp, pptr);
199110696SDavid.Hollister@Sun.COM RESTART_DISCOVERY(pwp);
199210696SDavid.Hollister@Sun.COM } else {
199311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
199410696SDavid.Hollister@Sun.COM "%s: Retries exhausted for %s, killing",
199510696SDavid.Hollister@Sun.COM __func__, pptr->path);
199610696SDavid.Hollister@Sun.COM pptr->config_stop = 0;
199710696SDavid.Hollister@Sun.COM pmcs_kill_changed(pwp, pptr, 0);
199810696SDavid.Hollister@Sun.COM }
199910696SDavid.Hollister@Sun.COM return (-1);
200010696SDavid.Hollister@Sun.COM }
200110696SDavid.Hollister@Sun.COM /*
200210696SDavid.Hollister@Sun.COM * Other errors or no valid device id is fatal, but don't
200310696SDavid.Hollister@Sun.COM * preclude a future action.
200410696SDavid.Hollister@Sun.COM */
200510696SDavid.Hollister@Sun.COM if (result || pptr->valid_device_id == 0) {
200611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
200711048SDavid.Hollister@Sun.COM "%s: %s could not be registered", __func__,
200811048SDavid.Hollister@Sun.COM pptr->path);
200910696SDavid.Hollister@Sun.COM return (-1);
201010696SDavid.Hollister@Sun.COM }
201110696SDavid.Hollister@Sun.COM }
201210696SDavid.Hollister@Sun.COM return (0);
201310696SDavid.Hollister@Sun.COM }
201410696SDavid.Hollister@Sun.COM
201510696SDavid.Hollister@Sun.COM int
pmcs_iport_tgtmap_create(pmcs_iport_t * iport)201610696SDavid.Hollister@Sun.COM pmcs_iport_tgtmap_create(pmcs_iport_t *iport)
201710696SDavid.Hollister@Sun.COM {
201810696SDavid.Hollister@Sun.COM ASSERT(iport);
201910696SDavid.Hollister@Sun.COM if (iport == NULL)
202010696SDavid.Hollister@Sun.COM return (B_FALSE);
202110696SDavid.Hollister@Sun.COM
202211048SDavid.Hollister@Sun.COM pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s", __func__);
202310696SDavid.Hollister@Sun.COM
202410696SDavid.Hollister@Sun.COM /* create target map */
202512207SChris.Horne@Sun.COM if (scsi_hba_tgtmap_create(iport->dip, SCSI_TM_FULLSET,
202612207SChris.Horne@Sun.COM tgtmap_csync_usec, tgtmap_stable_usec, (void *)iport,
202712207SChris.Horne@Sun.COM pmcs_tgtmap_activate_cb, pmcs_tgtmap_deactivate_cb,
202811601SDavid.Hollister@Sun.COM &iport->iss_tgtmap) != DDI_SUCCESS) {
202911048SDavid.Hollister@Sun.COM pmcs_prt(iport->pwp, PMCS_PRT_DEBUG, NULL, NULL,
203010696SDavid.Hollister@Sun.COM "%s: failed to create tgtmap", __func__);
203110696SDavid.Hollister@Sun.COM return (B_FALSE);
203210696SDavid.Hollister@Sun.COM }
203310696SDavid.Hollister@Sun.COM return (B_TRUE);
203410696SDavid.Hollister@Sun.COM }
203510696SDavid.Hollister@Sun.COM
203610696SDavid.Hollister@Sun.COM int
pmcs_iport_tgtmap_destroy(pmcs_iport_t * iport)203710696SDavid.Hollister@Sun.COM pmcs_iport_tgtmap_destroy(pmcs_iport_t *iport)
203810696SDavid.Hollister@Sun.COM {
203910696SDavid.Hollister@Sun.COM ASSERT(iport && iport->iss_tgtmap);
204010696SDavid.Hollister@Sun.COM if ((iport == NULL) || (iport->iss_tgtmap == NULL))
204110696SDavid.Hollister@Sun.COM return (B_FALSE);
204210696SDavid.Hollister@Sun.COM
204311048SDavid.Hollister@Sun.COM pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s", __func__);
204410696SDavid.Hollister@Sun.COM
204510696SDavid.Hollister@Sun.COM /* destroy target map */
204610696SDavid.Hollister@Sun.COM scsi_hba_tgtmap_destroy(iport->iss_tgtmap);
204710696SDavid.Hollister@Sun.COM return (B_TRUE);
204810696SDavid.Hollister@Sun.COM }
204910696SDavid.Hollister@Sun.COM
205010696SDavid.Hollister@Sun.COM /*
205111133SJesse.Butler@Sun.COM * Remove all phys from an iport's phymap and empty it's phylist.
205211692SJesse.Butler@Sun.COM * Called when a port has been reset by the host (see pmcs_intr.c)
205311692SJesse.Butler@Sun.COM * or prior to issuing a soft reset if we detect a stall on the chip
205411692SJesse.Butler@Sun.COM * (see pmcs_attach.c).
205511133SJesse.Butler@Sun.COM */
205611133SJesse.Butler@Sun.COM void
pmcs_iport_teardown_phys(pmcs_iport_t * iport)205711133SJesse.Butler@Sun.COM pmcs_iport_teardown_phys(pmcs_iport_t *iport)
205811133SJesse.Butler@Sun.COM {
205911133SJesse.Butler@Sun.COM pmcs_hw_t *pwp;
206011133SJesse.Butler@Sun.COM sas_phymap_phys_t *phys;
206111133SJesse.Butler@Sun.COM int phynum;
206211133SJesse.Butler@Sun.COM
206311133SJesse.Butler@Sun.COM ASSERT(iport);
206411133SJesse.Butler@Sun.COM ASSERT(mutex_owned(&iport->lock));
206511133SJesse.Butler@Sun.COM pwp = iport->pwp;
206611133SJesse.Butler@Sun.COM ASSERT(pwp);
206711133SJesse.Butler@Sun.COM
206811133SJesse.Butler@Sun.COM /*
206911133SJesse.Butler@Sun.COM * Remove all phys from the iport handle's phy list, unset its
207011133SJesse.Butler@Sun.COM * primary phy and update its state.
207111133SJesse.Butler@Sun.COM */
207211133SJesse.Butler@Sun.COM pmcs_remove_phy_from_iport(iport, NULL);
207311133SJesse.Butler@Sun.COM iport->pptr = NULL;
207411133SJesse.Butler@Sun.COM iport->ua_state = UA_PEND_DEACTIVATE;
207511133SJesse.Butler@Sun.COM
207611133SJesse.Butler@Sun.COM /* Remove all phys from the phymap */
207711133SJesse.Butler@Sun.COM phys = sas_phymap_ua2phys(pwp->hss_phymap, iport->ua);
207811692SJesse.Butler@Sun.COM if (phys) {
207911692SJesse.Butler@Sun.COM while ((phynum = sas_phymap_phys_next(phys)) != -1) {
208011692SJesse.Butler@Sun.COM (void) sas_phymap_phy_rem(pwp->hss_phymap, phynum);
208111692SJesse.Butler@Sun.COM }
208211692SJesse.Butler@Sun.COM sas_phymap_phys_free(phys);
208311692SJesse.Butler@Sun.COM }
208411133SJesse.Butler@Sun.COM }
208511133SJesse.Butler@Sun.COM
208611133SJesse.Butler@Sun.COM /*
208710696SDavid.Hollister@Sun.COM * Query the phymap and populate the iport handle passed in.
208810696SDavid.Hollister@Sun.COM * Called with iport lock held.
208910696SDavid.Hollister@Sun.COM */
209010696SDavid.Hollister@Sun.COM int
pmcs_iport_configure_phys(pmcs_iport_t * iport)209110696SDavid.Hollister@Sun.COM pmcs_iport_configure_phys(pmcs_iport_t *iport)
209210696SDavid.Hollister@Sun.COM {
209310696SDavid.Hollister@Sun.COM pmcs_hw_t *pwp;
209410696SDavid.Hollister@Sun.COM pmcs_phy_t *pptr;
209510696SDavid.Hollister@Sun.COM sas_phymap_phys_t *phys;
209610696SDavid.Hollister@Sun.COM int phynum;
209710696SDavid.Hollister@Sun.COM int inst;
209810696SDavid.Hollister@Sun.COM
209910696SDavid.Hollister@Sun.COM ASSERT(iport);
210010696SDavid.Hollister@Sun.COM ASSERT(mutex_owned(&iport->lock));
210110696SDavid.Hollister@Sun.COM pwp = iport->pwp;
210210696SDavid.Hollister@Sun.COM ASSERT(pwp);
210310696SDavid.Hollister@Sun.COM inst = ddi_get_instance(iport->dip);
210410696SDavid.Hollister@Sun.COM
210510696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
210610696SDavid.Hollister@Sun.COM ASSERT(pwp->root_phys != NULL);
210710696SDavid.Hollister@Sun.COM
210810696SDavid.Hollister@Sun.COM /*
210910696SDavid.Hollister@Sun.COM * Query the phymap regarding the phys in this iport and populate
211010696SDavid.Hollister@Sun.COM * the iport's phys list. Hereafter this list is maintained via
211110696SDavid.Hollister@Sun.COM * port up and down events in pmcs_intr.c
211210696SDavid.Hollister@Sun.COM */
211310696SDavid.Hollister@Sun.COM ASSERT(list_is_empty(&iport->phys));
211410696SDavid.Hollister@Sun.COM phys = sas_phymap_ua2phys(pwp->hss_phymap, iport->ua);
211511692SJesse.Butler@Sun.COM ASSERT(phys != NULL);
211610696SDavid.Hollister@Sun.COM while ((phynum = sas_phymap_phys_next(phys)) != -1) {
211710696SDavid.Hollister@Sun.COM /* Grab the phy pointer from root_phys */
211810696SDavid.Hollister@Sun.COM pptr = pwp->root_phys + phynum;
211910696SDavid.Hollister@Sun.COM ASSERT(pptr);
212010696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
212110696SDavid.Hollister@Sun.COM ASSERT(pptr->phynum == phynum);
212210696SDavid.Hollister@Sun.COM
212310696SDavid.Hollister@Sun.COM /*
212410696SDavid.Hollister@Sun.COM * Set a back pointer in the phy to this iport.
212510696SDavid.Hollister@Sun.COM */
212610696SDavid.Hollister@Sun.COM pptr->iport = iport;
212710696SDavid.Hollister@Sun.COM
212810696SDavid.Hollister@Sun.COM /*
212910696SDavid.Hollister@Sun.COM * If this phy is the primary, set a pointer to it on our
213010696SDavid.Hollister@Sun.COM * iport handle, and set our portid from it.
213110696SDavid.Hollister@Sun.COM */
213210696SDavid.Hollister@Sun.COM if (!pptr->subsidiary) {
213310696SDavid.Hollister@Sun.COM iport->pptr = pptr;
213410696SDavid.Hollister@Sun.COM iport->portid = pptr->portid;
213510696SDavid.Hollister@Sun.COM }
213610696SDavid.Hollister@Sun.COM
213710696SDavid.Hollister@Sun.COM /*
213810696SDavid.Hollister@Sun.COM * Finally, insert the phy into our list
213910696SDavid.Hollister@Sun.COM */
214011090SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
214110696SDavid.Hollister@Sun.COM pmcs_add_phy_to_iport(iport, pptr);
214210696SDavid.Hollister@Sun.COM
214311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "%s: found "
214411048SDavid.Hollister@Sun.COM "phy %d [0x%p] on iport%d, refcnt(%d)", __func__, phynum,
214510696SDavid.Hollister@Sun.COM (void *)pptr, inst, iport->refcnt);
214610696SDavid.Hollister@Sun.COM }
214710696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
214810696SDavid.Hollister@Sun.COM sas_phymap_phys_free(phys);
214910696SDavid.Hollister@Sun.COM RESTART_DISCOVERY(pwp);
215010696SDavid.Hollister@Sun.COM return (DDI_SUCCESS);
215110696SDavid.Hollister@Sun.COM }
215210696SDavid.Hollister@Sun.COM
215310696SDavid.Hollister@Sun.COM /*
215410696SDavid.Hollister@Sun.COM * Return the iport that ua is associated with, or NULL. If an iport is
215510696SDavid.Hollister@Sun.COM * returned, it will be held and the caller must release the hold.
215610696SDavid.Hollister@Sun.COM */
215710696SDavid.Hollister@Sun.COM static pmcs_iport_t *
pmcs_get_iport_by_ua(pmcs_hw_t * pwp,char * ua)215810696SDavid.Hollister@Sun.COM pmcs_get_iport_by_ua(pmcs_hw_t *pwp, char *ua)
215910696SDavid.Hollister@Sun.COM {
216010696SDavid.Hollister@Sun.COM pmcs_iport_t *iport = NULL;
216110696SDavid.Hollister@Sun.COM
216210696SDavid.Hollister@Sun.COM rw_enter(&pwp->iports_lock, RW_READER);
216310696SDavid.Hollister@Sun.COM for (iport = list_head(&pwp->iports);
216410696SDavid.Hollister@Sun.COM iport != NULL;
216510696SDavid.Hollister@Sun.COM iport = list_next(&pwp->iports, iport)) {
216610696SDavid.Hollister@Sun.COM mutex_enter(&iport->lock);
216710696SDavid.Hollister@Sun.COM if (strcmp(iport->ua, ua) == 0) {
216810696SDavid.Hollister@Sun.COM mutex_exit(&iport->lock);
216912462Sjesse.butler@oracle.com pmcs_hold_iport(iport);
217010696SDavid.Hollister@Sun.COM break;
217110696SDavid.Hollister@Sun.COM }
217210696SDavid.Hollister@Sun.COM mutex_exit(&iport->lock);
217310696SDavid.Hollister@Sun.COM }
217410696SDavid.Hollister@Sun.COM rw_exit(&pwp->iports_lock);
217510696SDavid.Hollister@Sun.COM
217610696SDavid.Hollister@Sun.COM return (iport);
217710696SDavid.Hollister@Sun.COM }
217810696SDavid.Hollister@Sun.COM
217910696SDavid.Hollister@Sun.COM /*
218010696SDavid.Hollister@Sun.COM * Return the iport that pptr is associated with, or NULL.
218110696SDavid.Hollister@Sun.COM * If an iport is returned, there is a hold that the caller must release.
218210696SDavid.Hollister@Sun.COM */
218310696SDavid.Hollister@Sun.COM pmcs_iport_t *
pmcs_get_iport_by_wwn(pmcs_hw_t * pwp,uint64_t wwn)218411601SDavid.Hollister@Sun.COM pmcs_get_iport_by_wwn(pmcs_hw_t *pwp, uint64_t wwn)
218510696SDavid.Hollister@Sun.COM {
218610696SDavid.Hollister@Sun.COM pmcs_iport_t *iport = NULL;
218710696SDavid.Hollister@Sun.COM char *ua;
218810696SDavid.Hollister@Sun.COM
218911601SDavid.Hollister@Sun.COM ua = sas_phymap_lookup_ua(pwp->hss_phymap, pwp->sas_wwns[0], wwn);
219010696SDavid.Hollister@Sun.COM if (ua) {
219110696SDavid.Hollister@Sun.COM iport = pmcs_get_iport_by_ua(pwp, ua);
219210696SDavid.Hollister@Sun.COM if (iport) {
219310696SDavid.Hollister@Sun.COM mutex_enter(&iport->lock);
219411601SDavid.Hollister@Sun.COM pmcs_iport_active(iport);
219511601SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: "
219611601SDavid.Hollister@Sun.COM "found iport [0x%p] on ua (%s), refcnt (%d)",
219711601SDavid.Hollister@Sun.COM __func__, (void *)iport, ua, iport->refcnt);
219810696SDavid.Hollister@Sun.COM mutex_exit(&iport->lock);
219910696SDavid.Hollister@Sun.COM }
220010696SDavid.Hollister@Sun.COM }
220110696SDavid.Hollister@Sun.COM
220210696SDavid.Hollister@Sun.COM return (iport);
220310696SDavid.Hollister@Sun.COM }
220410696SDavid.Hollister@Sun.COM
220511133SJesse.Butler@Sun.COM /*
220611267SJesse.Butler@Sun.COM * Promote the next phy on this port to primary, and return it.
220711133SJesse.Butler@Sun.COM * Called when the primary PHY on a port is going down, but the port
220811133SJesse.Butler@Sun.COM * remains up (see pmcs_intr.c).
220911133SJesse.Butler@Sun.COM */
221011133SJesse.Butler@Sun.COM pmcs_phy_t *
pmcs_promote_next_phy(pmcs_phy_t * prev_primary)221111133SJesse.Butler@Sun.COM pmcs_promote_next_phy(pmcs_phy_t *prev_primary)
221211133SJesse.Butler@Sun.COM {
221311267SJesse.Butler@Sun.COM pmcs_hw_t *pwp;
221411267SJesse.Butler@Sun.COM pmcs_iport_t *iport;
221511267SJesse.Butler@Sun.COM pmcs_phy_t *pptr, *child;
221611267SJesse.Butler@Sun.COM int portid;
221711267SJesse.Butler@Sun.COM
221811267SJesse.Butler@Sun.COM pmcs_lock_phy(prev_primary);
221911267SJesse.Butler@Sun.COM portid = prev_primary->portid;
222011267SJesse.Butler@Sun.COM iport = prev_primary->iport;
222111267SJesse.Butler@Sun.COM pwp = prev_primary->pwp;
222211267SJesse.Butler@Sun.COM
222311267SJesse.Butler@Sun.COM /* Use the first available phy in this port */
222411267SJesse.Butler@Sun.COM for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
222511267SJesse.Butler@Sun.COM if ((pptr->portid == portid) && (pptr != prev_primary)) {
222611267SJesse.Butler@Sun.COM mutex_enter(&pptr->phy_lock);
222711133SJesse.Butler@Sun.COM break;
222811133SJesse.Butler@Sun.COM }
222911133SJesse.Butler@Sun.COM }
223011133SJesse.Butler@Sun.COM
223111133SJesse.Butler@Sun.COM if (pptr == NULL) {
223211267SJesse.Butler@Sun.COM pmcs_unlock_phy(prev_primary);
223311133SJesse.Butler@Sun.COM return (NULL);
223411133SJesse.Butler@Sun.COM }
223511133SJesse.Butler@Sun.COM
223611267SJesse.Butler@Sun.COM if (iport) {
223711267SJesse.Butler@Sun.COM mutex_enter(&iport->lock);
223811267SJesse.Butler@Sun.COM iport->pptr = pptr;
223911267SJesse.Butler@Sun.COM mutex_exit(&iport->lock);
224011267SJesse.Butler@Sun.COM }
224111267SJesse.Butler@Sun.COM
224211133SJesse.Butler@Sun.COM /* Update the phy handle with the data from the previous primary */
224311133SJesse.Butler@Sun.COM pptr->children = prev_primary->children;
224411133SJesse.Butler@Sun.COM child = pptr->children;
224511133SJesse.Butler@Sun.COM while (child) {
224611133SJesse.Butler@Sun.COM child->parent = pptr;
224711133SJesse.Butler@Sun.COM child = child->sibling;
224811133SJesse.Butler@Sun.COM }
224911133SJesse.Butler@Sun.COM pptr->ncphy = prev_primary->ncphy;
225011133SJesse.Butler@Sun.COM pptr->width = prev_primary->width;
225111133SJesse.Butler@Sun.COM pptr->dtype = prev_primary->dtype;
225211133SJesse.Butler@Sun.COM pptr->pend_dtype = prev_primary->pend_dtype;
225311133SJesse.Butler@Sun.COM pptr->tolerates_sas2 = prev_primary->tolerates_sas2;
225411133SJesse.Butler@Sun.COM pptr->atdt = prev_primary->atdt;
225511133SJesse.Butler@Sun.COM pptr->portid = prev_primary->portid;
225611133SJesse.Butler@Sun.COM pptr->link_rate = prev_primary->link_rate;
225711133SJesse.Butler@Sun.COM pptr->configured = prev_primary->configured;
225811133SJesse.Butler@Sun.COM pptr->iport = prev_primary->iport;
225911133SJesse.Butler@Sun.COM pptr->target = prev_primary->target;
226011267SJesse.Butler@Sun.COM if (pptr->target) {
226111267SJesse.Butler@Sun.COM pptr->target->phy = pptr;
226211267SJesse.Butler@Sun.COM }
226311307SDavid.Hollister@Sun.COM
226411307SDavid.Hollister@Sun.COM /* Update the phy mask properties for the affected PHYs */
226511307SDavid.Hollister@Sun.COM /* Clear the current values... */
226611307SDavid.Hollister@Sun.COM pmcs_update_phy_pm_props(pptr, pptr->att_port_pm_tmp,
226711307SDavid.Hollister@Sun.COM pptr->tgt_port_pm_tmp, B_FALSE);
226811307SDavid.Hollister@Sun.COM /* ...replace with the values from prev_primary... */
226911307SDavid.Hollister@Sun.COM pmcs_update_phy_pm_props(pptr, prev_primary->att_port_pm_tmp,
227011307SDavid.Hollister@Sun.COM prev_primary->tgt_port_pm_tmp, B_TRUE);
227111307SDavid.Hollister@Sun.COM /* ...then clear prev_primary's PHY values from the new primary */
227211307SDavid.Hollister@Sun.COM pmcs_update_phy_pm_props(pptr, prev_primary->att_port_pm,
227311307SDavid.Hollister@Sun.COM prev_primary->tgt_port_pm, B_FALSE);
227411307SDavid.Hollister@Sun.COM /* Clear the prev_primary's values */
227511307SDavid.Hollister@Sun.COM pmcs_update_phy_pm_props(prev_primary, prev_primary->att_port_pm_tmp,
227611307SDavid.Hollister@Sun.COM prev_primary->tgt_port_pm_tmp, B_FALSE);
227711307SDavid.Hollister@Sun.COM
227811133SJesse.Butler@Sun.COM pptr->subsidiary = 0;
227911133SJesse.Butler@Sun.COM
228011133SJesse.Butler@Sun.COM prev_primary->subsidiary = 1;
228111133SJesse.Butler@Sun.COM prev_primary->children = NULL;
228211267SJesse.Butler@Sun.COM prev_primary->target = NULL;
228311347SRamana.Srikanth@Sun.COM pptr->device_id = prev_primary->device_id;
228412462Sjesse.butler@oracle.com pptr->valid_device_id = prev_primary->valid_device_id;
228511133SJesse.Butler@Sun.COM pmcs_unlock_phy(prev_primary);
228611133SJesse.Butler@Sun.COM
228711133SJesse.Butler@Sun.COM /*
228811133SJesse.Butler@Sun.COM * We call pmcs_unlock_phy() on pptr because it now contains the
228911133SJesse.Butler@Sun.COM * list of children.
229011133SJesse.Butler@Sun.COM */
229111133SJesse.Butler@Sun.COM pmcs_unlock_phy(pptr);
229211133SJesse.Butler@Sun.COM
229311133SJesse.Butler@Sun.COM return (pptr);
229411133SJesse.Butler@Sun.COM }
229511133SJesse.Butler@Sun.COM
229610696SDavid.Hollister@Sun.COM void
pmcs_hold_iport(pmcs_iport_t * iport)229712462Sjesse.butler@oracle.com pmcs_hold_iport(pmcs_iport_t *iport)
229812462Sjesse.butler@oracle.com {
229912462Sjesse.butler@oracle.com /*
230012462Sjesse.butler@oracle.com * Grab a reference to this iport.
230112462Sjesse.butler@oracle.com */
230212462Sjesse.butler@oracle.com ASSERT(iport);
230312462Sjesse.butler@oracle.com mutex_enter(&iport->refcnt_lock);
230412462Sjesse.butler@oracle.com iport->refcnt++;
230512462Sjesse.butler@oracle.com mutex_exit(&iport->refcnt_lock);
230612462Sjesse.butler@oracle.com
230712462Sjesse.butler@oracle.com pmcs_prt(iport->pwp, PMCS_PRT_DEBUG2, NULL, NULL, "%s: iport "
230812462Sjesse.butler@oracle.com "[0x%p] refcnt (%d)", __func__, (void *)iport, iport->refcnt);
230912462Sjesse.butler@oracle.com }
231012462Sjesse.butler@oracle.com
231112462Sjesse.butler@oracle.com void
pmcs_rele_iport(pmcs_iport_t * iport)231210696SDavid.Hollister@Sun.COM pmcs_rele_iport(pmcs_iport_t *iport)
231310696SDavid.Hollister@Sun.COM {
231410696SDavid.Hollister@Sun.COM /*
231510696SDavid.Hollister@Sun.COM * Release a refcnt on this iport. If this is the last reference,
231610696SDavid.Hollister@Sun.COM * signal the potential waiter in pmcs_iport_unattach().
231710696SDavid.Hollister@Sun.COM */
231810696SDavid.Hollister@Sun.COM ASSERT(iport->refcnt > 0);
231910696SDavid.Hollister@Sun.COM mutex_enter(&iport->refcnt_lock);
232010696SDavid.Hollister@Sun.COM iport->refcnt--;
232110696SDavid.Hollister@Sun.COM mutex_exit(&iport->refcnt_lock);
232210696SDavid.Hollister@Sun.COM if (iport->refcnt == 0) {
232310696SDavid.Hollister@Sun.COM cv_signal(&iport->refcnt_cv);
232410696SDavid.Hollister@Sun.COM }
232512462Sjesse.butler@oracle.com pmcs_prt(iport->pwp, PMCS_PRT_DEBUG2, NULL, NULL, "%s: iport "
232611048SDavid.Hollister@Sun.COM "[0x%p] refcnt (%d)", __func__, (void *)iport, iport->refcnt);
232710696SDavid.Hollister@Sun.COM }
232810696SDavid.Hollister@Sun.COM
232910696SDavid.Hollister@Sun.COM void
pmcs_phymap_activate(void * arg,char * ua,void ** privp)233010696SDavid.Hollister@Sun.COM pmcs_phymap_activate(void *arg, char *ua, void **privp)
233110696SDavid.Hollister@Sun.COM {
233210696SDavid.Hollister@Sun.COM _NOTE(ARGUNUSED(privp));
233310696SDavid.Hollister@Sun.COM pmcs_hw_t *pwp = arg;
233410696SDavid.Hollister@Sun.COM pmcs_iport_t *iport = NULL;
233510696SDavid.Hollister@Sun.COM
233610696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
233712258Ssrikanth.suravajhala@oracle.com if ((pwp->state == STATE_UNPROBING) || (pwp->state == STATE_DEAD) ||
233812258Ssrikanth.suravajhala@oracle.com (pwp->state == STATE_IN_RESET)) {
233910696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
234010696SDavid.Hollister@Sun.COM return;
234110696SDavid.Hollister@Sun.COM }
234210696SDavid.Hollister@Sun.COM pwp->phymap_active++;
234310696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
234410696SDavid.Hollister@Sun.COM
234510696SDavid.Hollister@Sun.COM if (scsi_hba_iportmap_iport_add(pwp->hss_iportmap, ua, NULL) !=
234610696SDavid.Hollister@Sun.COM DDI_SUCCESS) {
234711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: failed to "
234811048SDavid.Hollister@Sun.COM "add iport handle on unit address [%s]", __func__, ua);
234910696SDavid.Hollister@Sun.COM } else {
235011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: "
235111048SDavid.Hollister@Sun.COM "phymap_active count (%d), added iport handle on unit "
235211048SDavid.Hollister@Sun.COM "address [%s]", __func__, pwp->phymap_active, ua);
235310696SDavid.Hollister@Sun.COM }
235410696SDavid.Hollister@Sun.COM
235510696SDavid.Hollister@Sun.COM /* Set the HBA softstate as our private data for this unit address */
235610696SDavid.Hollister@Sun.COM *privp = (void *)pwp;
235710696SDavid.Hollister@Sun.COM
235810696SDavid.Hollister@Sun.COM /*
235910696SDavid.Hollister@Sun.COM * We are waiting on attach for this iport node, unless it is still
236010696SDavid.Hollister@Sun.COM * attached. This can happen if a consumer has an outstanding open
236110696SDavid.Hollister@Sun.COM * on our iport node, but the port is down. If this is the case, we
236210696SDavid.Hollister@Sun.COM * need to configure our iport here for reuse.
236310696SDavid.Hollister@Sun.COM */
236410696SDavid.Hollister@Sun.COM iport = pmcs_get_iport_by_ua(pwp, ua);
236510696SDavid.Hollister@Sun.COM if (iport) {
236610696SDavid.Hollister@Sun.COM mutex_enter(&iport->lock);
236710696SDavid.Hollister@Sun.COM if (pmcs_iport_configure_phys(iport) != DDI_SUCCESS) {
236811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: "
236910696SDavid.Hollister@Sun.COM "failed to configure phys on iport [0x%p] at "
237010696SDavid.Hollister@Sun.COM "unit address (%s)", __func__, (void *)iport, ua);
237110696SDavid.Hollister@Sun.COM }
237211601SDavid.Hollister@Sun.COM pmcs_iport_active(iport);
237310696SDavid.Hollister@Sun.COM pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
237410696SDavid.Hollister@Sun.COM &iport->nphy);
237510696SDavid.Hollister@Sun.COM mutex_exit(&iport->lock);
237610696SDavid.Hollister@Sun.COM pmcs_rele_iport(iport);
237710696SDavid.Hollister@Sun.COM }
237810696SDavid.Hollister@Sun.COM
237910696SDavid.Hollister@Sun.COM }
238010696SDavid.Hollister@Sun.COM
238110696SDavid.Hollister@Sun.COM void
pmcs_phymap_deactivate(void * arg,char * ua,void * privp)238210696SDavid.Hollister@Sun.COM pmcs_phymap_deactivate(void *arg, char *ua, void *privp)
238310696SDavid.Hollister@Sun.COM {
238410696SDavid.Hollister@Sun.COM _NOTE(ARGUNUSED(privp));
238510696SDavid.Hollister@Sun.COM pmcs_hw_t *pwp = arg;
238610696SDavid.Hollister@Sun.COM pmcs_iport_t *iport;
238710696SDavid.Hollister@Sun.COM
238810696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
238910696SDavid.Hollister@Sun.COM pwp->phymap_active--;
239010696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
239110696SDavid.Hollister@Sun.COM
239210696SDavid.Hollister@Sun.COM if (scsi_hba_iportmap_iport_remove(pwp->hss_iportmap, ua) !=
239310696SDavid.Hollister@Sun.COM DDI_SUCCESS) {
239411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: failed to "
239511048SDavid.Hollister@Sun.COM "remove iport handle on unit address [%s]", __func__, ua);
239610696SDavid.Hollister@Sun.COM } else {
239711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL, "%s: "
239811048SDavid.Hollister@Sun.COM "phymap_active count (%d), removed iport handle on unit "
239911048SDavid.Hollister@Sun.COM "address [%s]", __func__, pwp->phymap_active, ua);
240010696SDavid.Hollister@Sun.COM }
240110696SDavid.Hollister@Sun.COM
240210696SDavid.Hollister@Sun.COM iport = pmcs_get_iport_by_ua(pwp, ua);
240310696SDavid.Hollister@Sun.COM
240410696SDavid.Hollister@Sun.COM if (iport == NULL) {
240511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: failed "
240611048SDavid.Hollister@Sun.COM "lookup of iport handle on unit addr (%s)", __func__, ua);
240710696SDavid.Hollister@Sun.COM return;
240810696SDavid.Hollister@Sun.COM }
240910696SDavid.Hollister@Sun.COM
241010696SDavid.Hollister@Sun.COM mutex_enter(&iport->lock);
241110696SDavid.Hollister@Sun.COM iport->ua_state = UA_INACTIVE;
241210696SDavid.Hollister@Sun.COM iport->portid = PMCS_IPORT_INVALID_PORT_ID;
241310696SDavid.Hollister@Sun.COM pmcs_remove_phy_from_iport(iport, NULL);
241410696SDavid.Hollister@Sun.COM mutex_exit(&iport->lock);
241510696SDavid.Hollister@Sun.COM pmcs_rele_iport(iport);
241610696SDavid.Hollister@Sun.COM }
241710696SDavid.Hollister@Sun.COM
241810696SDavid.Hollister@Sun.COM /*
241910696SDavid.Hollister@Sun.COM * Top-level discovery function
242010696SDavid.Hollister@Sun.COM */
242110696SDavid.Hollister@Sun.COM void
pmcs_discover(pmcs_hw_t * pwp)242210696SDavid.Hollister@Sun.COM pmcs_discover(pmcs_hw_t *pwp)
242310696SDavid.Hollister@Sun.COM {
242410696SDavid.Hollister@Sun.COM pmcs_phy_t *pptr;
242510696SDavid.Hollister@Sun.COM pmcs_phy_t *root_phy;
242610696SDavid.Hollister@Sun.COM
242710696SDavid.Hollister@Sun.COM DTRACE_PROBE2(pmcs__discover__entry, ulong_t, pwp->work_flags,
242810696SDavid.Hollister@Sun.COM boolean_t, pwp->config_changed);
242910696SDavid.Hollister@Sun.COM
243010696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
243110696SDavid.Hollister@Sun.COM
243210696SDavid.Hollister@Sun.COM if (pwp->state != STATE_RUNNING) {
243310696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
243410696SDavid.Hollister@Sun.COM return;
243510696SDavid.Hollister@Sun.COM }
243610696SDavid.Hollister@Sun.COM
243710696SDavid.Hollister@Sun.COM /* Ensure we have at least one phymap active */
243810696SDavid.Hollister@Sun.COM if (pwp->phymap_active == 0) {
243910696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
244011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
244110696SDavid.Hollister@Sun.COM "%s: phymap inactive, exiting", __func__);
244210696SDavid.Hollister@Sun.COM return;
244310696SDavid.Hollister@Sun.COM }
244410696SDavid.Hollister@Sun.COM
244510696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
244610696SDavid.Hollister@Sun.COM
244710696SDavid.Hollister@Sun.COM /*
244810696SDavid.Hollister@Sun.COM * If no iports have attached, but we have PHYs that are up, we
244910696SDavid.Hollister@Sun.COM * are waiting for iport attach to complete. Restart discovery.
245010696SDavid.Hollister@Sun.COM */
245110696SDavid.Hollister@Sun.COM rw_enter(&pwp->iports_lock, RW_READER);
245210696SDavid.Hollister@Sun.COM if (!pwp->iports_attached) {
245310696SDavid.Hollister@Sun.COM rw_exit(&pwp->iports_lock);
245411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
245510696SDavid.Hollister@Sun.COM "%s: no iports attached, retry discovery", __func__);
245610696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);
245710696SDavid.Hollister@Sun.COM return;
245810696SDavid.Hollister@Sun.COM }
245910696SDavid.Hollister@Sun.COM rw_exit(&pwp->iports_lock);
246010696SDavid.Hollister@Sun.COM
246110696SDavid.Hollister@Sun.COM mutex_enter(&pwp->config_lock);
246210696SDavid.Hollister@Sun.COM if (pwp->configuring) {
246310696SDavid.Hollister@Sun.COM mutex_exit(&pwp->config_lock);
246411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
246510696SDavid.Hollister@Sun.COM "%s: configuration already in progress", __func__);
246610696SDavid.Hollister@Sun.COM return;
246710696SDavid.Hollister@Sun.COM }
246810696SDavid.Hollister@Sun.COM
246910696SDavid.Hollister@Sun.COM if (pmcs_acquire_scratch(pwp, B_FALSE)) {
247010696SDavid.Hollister@Sun.COM mutex_exit(&pwp->config_lock);
247111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
247210696SDavid.Hollister@Sun.COM "%s: cannot allocate scratch", __func__);
247310696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);
247410696SDavid.Hollister@Sun.COM return;
247510696SDavid.Hollister@Sun.COM }
247610696SDavid.Hollister@Sun.COM
247710696SDavid.Hollister@Sun.COM pwp->configuring = 1;
247810696SDavid.Hollister@Sun.COM pwp->config_changed = B_FALSE;
247910696SDavid.Hollister@Sun.COM mutex_exit(&pwp->config_lock);
248010696SDavid.Hollister@Sun.COM
248111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "Discovery begin");
248210696SDavid.Hollister@Sun.COM
248310696SDavid.Hollister@Sun.COM /*
248411347SRamana.Srikanth@Sun.COM * First, tell SCSA that we're beginning set operations.
248511347SRamana.Srikanth@Sun.COM */
248611347SRamana.Srikanth@Sun.COM pmcs_begin_observations(pwp);
248711347SRamana.Srikanth@Sun.COM
248811347SRamana.Srikanth@Sun.COM /*
248910696SDavid.Hollister@Sun.COM * The order of the following traversals is important.
249010696SDavid.Hollister@Sun.COM *
249110696SDavid.Hollister@Sun.COM * The first one checks for changed expanders.
249210696SDavid.Hollister@Sun.COM *
249310696SDavid.Hollister@Sun.COM * The second one aborts commands for dead devices and deregisters them.
249410696SDavid.Hollister@Sun.COM *
249510696SDavid.Hollister@Sun.COM * The third one clears the contents of dead expanders from the tree
249610696SDavid.Hollister@Sun.COM *
249710696SDavid.Hollister@Sun.COM * The fourth one clears now dead devices in expanders that remain.
249810696SDavid.Hollister@Sun.COM */
249910696SDavid.Hollister@Sun.COM
250010696SDavid.Hollister@Sun.COM /*
250110696SDavid.Hollister@Sun.COM * 1. Check expanders marked changed (but not dead) to see if they still
250210696SDavid.Hollister@Sun.COM * have the same number of phys and the same SAS address. Mark them,
250310696SDavid.Hollister@Sun.COM * their subsidiary phys (if wide) and their descendents dead if
250410696SDavid.Hollister@Sun.COM * anything has changed. Check the devices they contain to see if
250510696SDavid.Hollister@Sun.COM * *they* have changed. If they've changed from type NOTHING we leave
250610696SDavid.Hollister@Sun.COM * them marked changed to be configured later (picking up a new SAS
250710696SDavid.Hollister@Sun.COM * address and link rate if possible). Otherwise, any change in type,
250810696SDavid.Hollister@Sun.COM * SAS address or removal of target role will cause us to mark them
250910696SDavid.Hollister@Sun.COM * (and their descendents) as dead (and cause any pending commands
251010696SDavid.Hollister@Sun.COM * and associated devices to be removed).
251110901SDavid.Hollister@Sun.COM *
251210901SDavid.Hollister@Sun.COM * NOTE: We don't want to bail on discovery if the config has
251310901SDavid.Hollister@Sun.COM * changed until *after* we run pmcs_kill_devices.
251410696SDavid.Hollister@Sun.COM */
251510696SDavid.Hollister@Sun.COM root_phy = pwp->root_phys;
251611501SDavid.Hollister@Sun.COM pmcs_check_expanders(pwp, root_phy);
251710696SDavid.Hollister@Sun.COM
251810696SDavid.Hollister@Sun.COM /*
251910696SDavid.Hollister@Sun.COM * 2. Descend the tree looking for dead devices and kill them
252010696SDavid.Hollister@Sun.COM * by aborting all active commands and then deregistering them.
252110696SDavid.Hollister@Sun.COM */
252211501SDavid.Hollister@Sun.COM if (pmcs_kill_devices(pwp, root_phy)) {
252311501SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
252411501SDavid.Hollister@Sun.COM "%s: pmcs_kill_devices failed!", __func__);
252510696SDavid.Hollister@Sun.COM }
252610696SDavid.Hollister@Sun.COM
252710696SDavid.Hollister@Sun.COM /*
252810696SDavid.Hollister@Sun.COM * 3. Check for dead expanders and remove their children from the tree.
252910696SDavid.Hollister@Sun.COM * By the time we get here, the devices and commands for them have
253010696SDavid.Hollister@Sun.COM * already been terminated and removed.
253110696SDavid.Hollister@Sun.COM *
253210696SDavid.Hollister@Sun.COM * We do this independent of the configuration count changing so we can
253310696SDavid.Hollister@Sun.COM * free any dead device PHYs that were discovered while checking
253410696SDavid.Hollister@Sun.COM * expanders. We ignore any subsidiary phys as pmcs_clear_expander
253510696SDavid.Hollister@Sun.COM * will take care of those.
253610696SDavid.Hollister@Sun.COM *
253710696SDavid.Hollister@Sun.COM * NOTE: pmcs_clear_expander requires softstate lock
253810696SDavid.Hollister@Sun.COM */
253910696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
254010696SDavid.Hollister@Sun.COM for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
254110696SDavid.Hollister@Sun.COM /*
254210696SDavid.Hollister@Sun.COM * Call pmcs_clear_expander for every root PHY. It will
254310696SDavid.Hollister@Sun.COM * recurse and determine which (if any) expanders actually
254410696SDavid.Hollister@Sun.COM * need to be cleared.
254510696SDavid.Hollister@Sun.COM */
254610696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
254710696SDavid.Hollister@Sun.COM pmcs_clear_expander(pwp, pptr, 0);
254810696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
254910696SDavid.Hollister@Sun.COM }
255010696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
255110696SDavid.Hollister@Sun.COM
255210696SDavid.Hollister@Sun.COM /*
255310696SDavid.Hollister@Sun.COM * 4. Check for dead devices and nullify them. By the time we get here,
255410696SDavid.Hollister@Sun.COM * the devices and commands for them have already been terminated
255510696SDavid.Hollister@Sun.COM * and removed. This is different from step 2 in that this just nulls
255610696SDavid.Hollister@Sun.COM * phys that are part of expanders that are still here but used to
255710696SDavid.Hollister@Sun.COM * be something but are no longer something (e.g., after a pulled
255810696SDavid.Hollister@Sun.COM * disk drive). Note that dead expanders had their contained phys
255910696SDavid.Hollister@Sun.COM * removed from the tree- here, the expanders themselves are
256010696SDavid.Hollister@Sun.COM * nullified (unless they were removed by being contained in another
256110696SDavid.Hollister@Sun.COM * expander phy).
256210696SDavid.Hollister@Sun.COM */
256310696SDavid.Hollister@Sun.COM pmcs_clear_phys(pwp, root_phy);
256410696SDavid.Hollister@Sun.COM
256510696SDavid.Hollister@Sun.COM /*
256610696SDavid.Hollister@Sun.COM * 5. Now check for and configure new devices.
256710696SDavid.Hollister@Sun.COM */
256810696SDavid.Hollister@Sun.COM if (pmcs_configure_new_devices(pwp, root_phy)) {
256910696SDavid.Hollister@Sun.COM goto restart;
257010696SDavid.Hollister@Sun.COM }
257110696SDavid.Hollister@Sun.COM
257210696SDavid.Hollister@Sun.COM out:
257310696SDavid.Hollister@Sun.COM DTRACE_PROBE2(pmcs__discover__exit, ulong_t, pwp->work_flags,
257410696SDavid.Hollister@Sun.COM boolean_t, pwp->config_changed);
257511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "Discovery end");
257610696SDavid.Hollister@Sun.COM
257710696SDavid.Hollister@Sun.COM mutex_enter(&pwp->config_lock);
257810696SDavid.Hollister@Sun.COM
257910696SDavid.Hollister@Sun.COM if (pwp->config_changed == B_FALSE) {
258010696SDavid.Hollister@Sun.COM /*
258110696SDavid.Hollister@Sun.COM * Observation is stable, report what we currently see to
258210696SDavid.Hollister@Sun.COM * the tgtmaps for delta processing. Start by setting
258310696SDavid.Hollister@Sun.COM * BEGIN on all tgtmaps.
258410696SDavid.Hollister@Sun.COM */
258510696SDavid.Hollister@Sun.COM mutex_exit(&pwp->config_lock);
258610696SDavid.Hollister@Sun.COM if (pmcs_report_observations(pwp) == B_FALSE) {
258710696SDavid.Hollister@Sun.COM goto restart;
258810696SDavid.Hollister@Sun.COM }
258910696SDavid.Hollister@Sun.COM mutex_enter(&pwp->config_lock);
259010696SDavid.Hollister@Sun.COM } else {
259110696SDavid.Hollister@Sun.COM /*
259210696SDavid.Hollister@Sun.COM * If config_changed is TRUE, we need to reschedule
259310696SDavid.Hollister@Sun.COM * discovery now.
259410696SDavid.Hollister@Sun.COM */
259511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
259610696SDavid.Hollister@Sun.COM "%s: Config has changed, will re-run discovery", __func__);
259710696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);
259810696SDavid.Hollister@Sun.COM }
259910696SDavid.Hollister@Sun.COM
260010696SDavid.Hollister@Sun.COM pmcs_release_scratch(pwp);
260111692SJesse.Butler@Sun.COM if (!pwp->quiesced) {
260211692SJesse.Butler@Sun.COM pwp->blocked = 0;
260311692SJesse.Butler@Sun.COM }
260410696SDavid.Hollister@Sun.COM pwp->configuring = 0;
260512078SJesse.Butler@Sun.COM cv_signal(&pwp->config_cv);
260610696SDavid.Hollister@Sun.COM mutex_exit(&pwp->config_lock);
260710696SDavid.Hollister@Sun.COM
260810696SDavid.Hollister@Sun.COM #ifdef DEBUG
260910696SDavid.Hollister@Sun.COM pptr = pmcs_find_phy_needing_work(pwp, pwp->root_phys);
261010696SDavid.Hollister@Sun.COM if (pptr != NULL) {
261110696SDavid.Hollister@Sun.COM if (!WORK_IS_SCHEDULED(pwp, PMCS_WORK_DISCOVER)) {
261211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
261310696SDavid.Hollister@Sun.COM "PHY %s dead=%d changed=%d configured=%d "
261410696SDavid.Hollister@Sun.COM "but no work scheduled", pptr->path, pptr->dead,
261510696SDavid.Hollister@Sun.COM pptr->changed, pptr->configured);
261610696SDavid.Hollister@Sun.COM }
261710696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
261810696SDavid.Hollister@Sun.COM }
261910696SDavid.Hollister@Sun.COM #endif
262010696SDavid.Hollister@Sun.COM
262110696SDavid.Hollister@Sun.COM return;
262210696SDavid.Hollister@Sun.COM
262310696SDavid.Hollister@Sun.COM restart:
262410696SDavid.Hollister@Sun.COM /* Clean up and restart discovery */
262510696SDavid.Hollister@Sun.COM pmcs_release_scratch(pwp);
262612000SReed.Liu@Sun.COM pmcs_flush_observations(pwp);
262710696SDavid.Hollister@Sun.COM mutex_enter(&pwp->config_lock);
262810696SDavid.Hollister@Sun.COM pwp->configuring = 0;
262912078SJesse.Butler@Sun.COM cv_signal(&pwp->config_cv);
263010696SDavid.Hollister@Sun.COM RESTART_DISCOVERY_LOCKED(pwp);
263110696SDavid.Hollister@Sun.COM mutex_exit(&pwp->config_lock);
263210696SDavid.Hollister@Sun.COM }
263310696SDavid.Hollister@Sun.COM
263410696SDavid.Hollister@Sun.COM /*
263510696SDavid.Hollister@Sun.COM * Return any PHY that needs to have scheduled work done. The PHY is returned
263610696SDavid.Hollister@Sun.COM * locked.
263710696SDavid.Hollister@Sun.COM */
263810696SDavid.Hollister@Sun.COM static pmcs_phy_t *
pmcs_find_phy_needing_work(pmcs_hw_t * pwp,pmcs_phy_t * pptr)263910696SDavid.Hollister@Sun.COM pmcs_find_phy_needing_work(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
264010696SDavid.Hollister@Sun.COM {
264110696SDavid.Hollister@Sun.COM pmcs_phy_t *cphyp, *pnext;
264210696SDavid.Hollister@Sun.COM
264310696SDavid.Hollister@Sun.COM while (pptr) {
264410696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
264510696SDavid.Hollister@Sun.COM
264610696SDavid.Hollister@Sun.COM if (pptr->changed || (pptr->dead && pptr->valid_device_id)) {
264710696SDavid.Hollister@Sun.COM return (pptr);
264810696SDavid.Hollister@Sun.COM }
264910696SDavid.Hollister@Sun.COM
265010696SDavid.Hollister@Sun.COM pnext = pptr->sibling;
265110696SDavid.Hollister@Sun.COM
265210696SDavid.Hollister@Sun.COM if (pptr->children) {
265310696SDavid.Hollister@Sun.COM cphyp = pptr->children;
265410696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
265510696SDavid.Hollister@Sun.COM cphyp = pmcs_find_phy_needing_work(pwp, cphyp);
265610696SDavid.Hollister@Sun.COM if (cphyp) {
265710696SDavid.Hollister@Sun.COM return (cphyp);
265810696SDavid.Hollister@Sun.COM }
265910696SDavid.Hollister@Sun.COM } else {
266010696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
266110696SDavid.Hollister@Sun.COM }
266210696SDavid.Hollister@Sun.COM
266310696SDavid.Hollister@Sun.COM pptr = pnext;
266410696SDavid.Hollister@Sun.COM }
266510696SDavid.Hollister@Sun.COM
266610696SDavid.Hollister@Sun.COM return (NULL);
266710696SDavid.Hollister@Sun.COM }
266810696SDavid.Hollister@Sun.COM
266910696SDavid.Hollister@Sun.COM /*
267011347SRamana.Srikanth@Sun.COM * We may (or may not) report observations to SCSA. This is prefaced by
267111347SRamana.Srikanth@Sun.COM * issuing a set_begin for each iport target map.
267210696SDavid.Hollister@Sun.COM */
267311347SRamana.Srikanth@Sun.COM static void
pmcs_begin_observations(pmcs_hw_t * pwp)267411347SRamana.Srikanth@Sun.COM pmcs_begin_observations(pmcs_hw_t *pwp)
267510696SDavid.Hollister@Sun.COM {
267610696SDavid.Hollister@Sun.COM pmcs_iport_t *iport;
267710696SDavid.Hollister@Sun.COM scsi_hba_tgtmap_t *tgtmap;
267811347SRamana.Srikanth@Sun.COM
267910696SDavid.Hollister@Sun.COM rw_enter(&pwp->iports_lock, RW_READER);
268010696SDavid.Hollister@Sun.COM for (iport = list_head(&pwp->iports); iport != NULL;
268110696SDavid.Hollister@Sun.COM iport = list_next(&pwp->iports, iport)) {
268210696SDavid.Hollister@Sun.COM /*
268310696SDavid.Hollister@Sun.COM * Unless we have at least one phy up, skip this iport.
268410696SDavid.Hollister@Sun.COM * Note we don't need to lock the iport for report_skip
268510696SDavid.Hollister@Sun.COM * since it is only used here. We are doing the skip so that
268610696SDavid.Hollister@Sun.COM * the phymap and iportmap stabilization times are honored -
268710696SDavid.Hollister@Sun.COM * giving us the ability to recover port operation within the
268810696SDavid.Hollister@Sun.COM * stabilization time without unconfiguring targets using the
268910696SDavid.Hollister@Sun.COM * port.
269010696SDavid.Hollister@Sun.COM */
269110696SDavid.Hollister@Sun.COM if (!sas_phymap_uahasphys(pwp->hss_phymap, iport->ua)) {
269210696SDavid.Hollister@Sun.COM iport->report_skip = 1;
269310696SDavid.Hollister@Sun.COM continue; /* skip set_begin */
269410696SDavid.Hollister@Sun.COM }
269510696SDavid.Hollister@Sun.COM iport->report_skip = 0;
269610696SDavid.Hollister@Sun.COM
269710696SDavid.Hollister@Sun.COM tgtmap = iport->iss_tgtmap;
269810696SDavid.Hollister@Sun.COM ASSERT(tgtmap);
269910696SDavid.Hollister@Sun.COM if (scsi_hba_tgtmap_set_begin(tgtmap) != DDI_SUCCESS) {
270011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
270110696SDavid.Hollister@Sun.COM "%s: cannot set_begin tgtmap ", __func__);
270210696SDavid.Hollister@Sun.COM rw_exit(&pwp->iports_lock);
270311347SRamana.Srikanth@Sun.COM return;
270410696SDavid.Hollister@Sun.COM }
270511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
270611048SDavid.Hollister@Sun.COM "%s: set begin on tgtmap [0x%p]", __func__, (void *)tgtmap);
270710696SDavid.Hollister@Sun.COM }
270810696SDavid.Hollister@Sun.COM rw_exit(&pwp->iports_lock);
270911347SRamana.Srikanth@Sun.COM }
271011347SRamana.Srikanth@Sun.COM
271111347SRamana.Srikanth@Sun.COM /*
271212000SReed.Liu@Sun.COM * Tell SCSA to flush the observations we've already sent (if any), as they
271312000SReed.Liu@Sun.COM * are no longer valid.
271412000SReed.Liu@Sun.COM */
271512000SReed.Liu@Sun.COM static void
pmcs_flush_observations(pmcs_hw_t * pwp)271612000SReed.Liu@Sun.COM pmcs_flush_observations(pmcs_hw_t *pwp)
271712000SReed.Liu@Sun.COM {
271812000SReed.Liu@Sun.COM pmcs_iport_t *iport;
271912000SReed.Liu@Sun.COM scsi_hba_tgtmap_t *tgtmap;
272012000SReed.Liu@Sun.COM
272112000SReed.Liu@Sun.COM rw_enter(&pwp->iports_lock, RW_READER);
272212000SReed.Liu@Sun.COM for (iport = list_head(&pwp->iports); iport != NULL;
272312000SReed.Liu@Sun.COM iport = list_next(&pwp->iports, iport)) {
272412000SReed.Liu@Sun.COM /*
272512000SReed.Liu@Sun.COM * Skip this iport if it has no PHYs up.
272612000SReed.Liu@Sun.COM */
272712000SReed.Liu@Sun.COM if (!sas_phymap_uahasphys(pwp->hss_phymap, iport->ua)) {
272812000SReed.Liu@Sun.COM continue;
272912000SReed.Liu@Sun.COM }
273012000SReed.Liu@Sun.COM
273112000SReed.Liu@Sun.COM tgtmap = iport->iss_tgtmap;
273212000SReed.Liu@Sun.COM ASSERT(tgtmap);
273312000SReed.Liu@Sun.COM if (scsi_hba_tgtmap_set_flush(tgtmap) != DDI_SUCCESS) {
273412000SReed.Liu@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
273512000SReed.Liu@Sun.COM "%s: Failed set_flush on tgtmap 0x%p", __func__,
273612000SReed.Liu@Sun.COM (void *)tgtmap);
273712000SReed.Liu@Sun.COM } else {
273812000SReed.Liu@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
273912000SReed.Liu@Sun.COM "%s: set flush on tgtmap 0x%p", __func__,
274012000SReed.Liu@Sun.COM (void *)tgtmap);
274112000SReed.Liu@Sun.COM }
274212000SReed.Liu@Sun.COM }
274312000SReed.Liu@Sun.COM rw_exit(&pwp->iports_lock);
274412000SReed.Liu@Sun.COM }
274512000SReed.Liu@Sun.COM
274612000SReed.Liu@Sun.COM /*
274711347SRamana.Srikanth@Sun.COM * Report current observations to SCSA.
274811347SRamana.Srikanth@Sun.COM */
274911347SRamana.Srikanth@Sun.COM static boolean_t
pmcs_report_observations(pmcs_hw_t * pwp)275011347SRamana.Srikanth@Sun.COM pmcs_report_observations(pmcs_hw_t *pwp)
275111347SRamana.Srikanth@Sun.COM {
275211347SRamana.Srikanth@Sun.COM pmcs_iport_t *iport;
275311347SRamana.Srikanth@Sun.COM scsi_hba_tgtmap_t *tgtmap;
275411347SRamana.Srikanth@Sun.COM char *ap;
275511347SRamana.Srikanth@Sun.COM pmcs_phy_t *pptr;
275611347SRamana.Srikanth@Sun.COM uint64_t wwn;
275710696SDavid.Hollister@Sun.COM
275810696SDavid.Hollister@Sun.COM /*
275911347SRamana.Srikanth@Sun.COM * Observation is stable, report what we currently see to the tgtmaps
276011347SRamana.Srikanth@Sun.COM * for delta processing.
276110696SDavid.Hollister@Sun.COM */
276210696SDavid.Hollister@Sun.COM pptr = pwp->root_phys;
276310696SDavid.Hollister@Sun.COM
276410696SDavid.Hollister@Sun.COM while (pptr) {
276510696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
276610696SDavid.Hollister@Sun.COM
276710696SDavid.Hollister@Sun.COM /*
276810696SDavid.Hollister@Sun.COM * Skip PHYs that have nothing attached or are dead.
276910696SDavid.Hollister@Sun.COM */
277010696SDavid.Hollister@Sun.COM if ((pptr->dtype == NOTHING) || pptr->dead) {
277110696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
277210696SDavid.Hollister@Sun.COM pptr = pptr->sibling;
277310696SDavid.Hollister@Sun.COM continue;
277410696SDavid.Hollister@Sun.COM }
277510696SDavid.Hollister@Sun.COM
277610696SDavid.Hollister@Sun.COM if (pptr->changed) {
277711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
277810696SDavid.Hollister@Sun.COM "%s: oops, PHY %s changed; restart discovery",
277910696SDavid.Hollister@Sun.COM __func__, pptr->path);
278010696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
278110696SDavid.Hollister@Sun.COM return (B_FALSE);
278210696SDavid.Hollister@Sun.COM }
278310696SDavid.Hollister@Sun.COM
278410696SDavid.Hollister@Sun.COM /*
278510696SDavid.Hollister@Sun.COM * Get the iport for this root PHY, then call the helper
278610696SDavid.Hollister@Sun.COM * to report observations for this iport's targets
278710696SDavid.Hollister@Sun.COM */
278811601SDavid.Hollister@Sun.COM wwn = pmcs_barray2wwn(pptr->sas_address);
278911601SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
279011601SDavid.Hollister@Sun.COM iport = pmcs_get_iport_by_wwn(pwp, wwn);
279110696SDavid.Hollister@Sun.COM if (iport == NULL) {
279210696SDavid.Hollister@Sun.COM /* No iport for this tgt */
279311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
279411048SDavid.Hollister@Sun.COM "%s: no iport for this target", __func__);
279510696SDavid.Hollister@Sun.COM pptr = pptr->sibling;
279610696SDavid.Hollister@Sun.COM continue;
279710696SDavid.Hollister@Sun.COM }
279810696SDavid.Hollister@Sun.COM
279911601SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
280010696SDavid.Hollister@Sun.COM if (!iport->report_skip) {
280110696SDavid.Hollister@Sun.COM if (pmcs_report_iport_observations(
280210696SDavid.Hollister@Sun.COM pwp, iport, pptr) == B_FALSE) {
280310696SDavid.Hollister@Sun.COM pmcs_rele_iport(iport);
280410696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
280510696SDavid.Hollister@Sun.COM return (B_FALSE);
280610696SDavid.Hollister@Sun.COM }
280710696SDavid.Hollister@Sun.COM }
280810696SDavid.Hollister@Sun.COM pmcs_rele_iport(iport);
280910696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
281010696SDavid.Hollister@Sun.COM pptr = pptr->sibling;
281110696SDavid.Hollister@Sun.COM }
281210696SDavid.Hollister@Sun.COM
281310696SDavid.Hollister@Sun.COM /*
281410696SDavid.Hollister@Sun.COM * The observation is complete, end sets. Note we will skip any
281510696SDavid.Hollister@Sun.COM * iports that are active, but have no PHYs in them (i.e. awaiting
281610696SDavid.Hollister@Sun.COM * unconfigure). Set to restart discovery if we find this.
281710696SDavid.Hollister@Sun.COM */
281810696SDavid.Hollister@Sun.COM rw_enter(&pwp->iports_lock, RW_READER);
281910696SDavid.Hollister@Sun.COM for (iport = list_head(&pwp->iports);
282010696SDavid.Hollister@Sun.COM iport != NULL;
282110696SDavid.Hollister@Sun.COM iport = list_next(&pwp->iports, iport)) {
282210696SDavid.Hollister@Sun.COM
282310696SDavid.Hollister@Sun.COM if (iport->report_skip)
282410696SDavid.Hollister@Sun.COM continue; /* skip set_end */
282510696SDavid.Hollister@Sun.COM
282610696SDavid.Hollister@Sun.COM tgtmap = iport->iss_tgtmap;
282710696SDavid.Hollister@Sun.COM ASSERT(tgtmap);
282810696SDavid.Hollister@Sun.COM if (scsi_hba_tgtmap_set_end(tgtmap, 0) != DDI_SUCCESS) {
282911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
283010696SDavid.Hollister@Sun.COM "%s: cannot set_end tgtmap ", __func__);
283110696SDavid.Hollister@Sun.COM rw_exit(&pwp->iports_lock);
283210696SDavid.Hollister@Sun.COM return (B_FALSE);
283310696SDavid.Hollister@Sun.COM }
283411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
283511048SDavid.Hollister@Sun.COM "%s: set end on tgtmap [0x%p]", __func__, (void *)tgtmap);
283610696SDavid.Hollister@Sun.COM }
283710696SDavid.Hollister@Sun.COM
283810696SDavid.Hollister@Sun.COM /*
283910696SDavid.Hollister@Sun.COM * Now that discovery is complete, set up the necessary
284010696SDavid.Hollister@Sun.COM * DDI properties on each iport node.
284110696SDavid.Hollister@Sun.COM */
284210696SDavid.Hollister@Sun.COM for (iport = list_head(&pwp->iports); iport != NULL;
284310696SDavid.Hollister@Sun.COM iport = list_next(&pwp->iports, iport)) {
284410696SDavid.Hollister@Sun.COM /* Set up the 'attached-port' property on the iport */
284510696SDavid.Hollister@Sun.COM ap = kmem_zalloc(PMCS_MAX_UA_SIZE, KM_SLEEP);
284610696SDavid.Hollister@Sun.COM mutex_enter(&iport->lock);
284710696SDavid.Hollister@Sun.COM pptr = iport->pptr;
284810696SDavid.Hollister@Sun.COM mutex_exit(&iport->lock);
284910696SDavid.Hollister@Sun.COM if (pptr == NULL) {
285010696SDavid.Hollister@Sun.COM /*
285110696SDavid.Hollister@Sun.COM * This iport is down, but has not been
285210696SDavid.Hollister@Sun.COM * removed from our list (unconfigured).
285310696SDavid.Hollister@Sun.COM * Set our value to '0'.
285410696SDavid.Hollister@Sun.COM */
285510696SDavid.Hollister@Sun.COM (void) snprintf(ap, 1, "%s", "0");
285610696SDavid.Hollister@Sun.COM } else {
285710696SDavid.Hollister@Sun.COM /* Otherwise, set it to remote phy's wwn */
285810696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
285910696SDavid.Hollister@Sun.COM wwn = pmcs_barray2wwn(pptr->sas_address);
286010696SDavid.Hollister@Sun.COM (void) scsi_wwn_to_wwnstr(wwn, 1, ap);
286110696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
286210696SDavid.Hollister@Sun.COM }
286310696SDavid.Hollister@Sun.COM if (ndi_prop_update_string(DDI_DEV_T_NONE, iport->dip,
286411307SDavid.Hollister@Sun.COM SCSI_ADDR_PROP_ATTACHED_PORT, ap) != DDI_SUCCESS) {
286511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed "
286611048SDavid.Hollister@Sun.COM "to set prop ("SCSI_ADDR_PROP_ATTACHED_PORT")",
286710696SDavid.Hollister@Sun.COM __func__);
286810696SDavid.Hollister@Sun.COM }
286910696SDavid.Hollister@Sun.COM kmem_free(ap, PMCS_MAX_UA_SIZE);
287010696SDavid.Hollister@Sun.COM }
287110696SDavid.Hollister@Sun.COM rw_exit(&pwp->iports_lock);
287210696SDavid.Hollister@Sun.COM
287310696SDavid.Hollister@Sun.COM return (B_TRUE);
287410696SDavid.Hollister@Sun.COM }
287510696SDavid.Hollister@Sun.COM
287610696SDavid.Hollister@Sun.COM /*
287710696SDavid.Hollister@Sun.COM * Report observations into a particular iport's target map
287810696SDavid.Hollister@Sun.COM *
287910696SDavid.Hollister@Sun.COM * Called with phyp (and all descendents) locked
288010696SDavid.Hollister@Sun.COM */
288110696SDavid.Hollister@Sun.COM static boolean_t
pmcs_report_iport_observations(pmcs_hw_t * pwp,pmcs_iport_t * iport,pmcs_phy_t * phyp)288210696SDavid.Hollister@Sun.COM pmcs_report_iport_observations(pmcs_hw_t *pwp, pmcs_iport_t *iport,
288310696SDavid.Hollister@Sun.COM pmcs_phy_t *phyp)
288410696SDavid.Hollister@Sun.COM {
288510696SDavid.Hollister@Sun.COM pmcs_phy_t *lphyp;
288610696SDavid.Hollister@Sun.COM scsi_hba_tgtmap_t *tgtmap;
288710696SDavid.Hollister@Sun.COM scsi_tgtmap_tgt_type_t tgt_type;
288810696SDavid.Hollister@Sun.COM char *ua;
288910696SDavid.Hollister@Sun.COM uint64_t wwn;
289010696SDavid.Hollister@Sun.COM
289110696SDavid.Hollister@Sun.COM tgtmap = iport->iss_tgtmap;
289210696SDavid.Hollister@Sun.COM ASSERT(tgtmap);
289310696SDavid.Hollister@Sun.COM
289410696SDavid.Hollister@Sun.COM lphyp = phyp;
289510696SDavid.Hollister@Sun.COM while (lphyp) {
289610696SDavid.Hollister@Sun.COM switch (lphyp->dtype) {
289710696SDavid.Hollister@Sun.COM default: /* Skip unknown PHYs. */
289810696SDavid.Hollister@Sun.COM /* for non-root phys, skip to sibling */
289910696SDavid.Hollister@Sun.COM goto next_phy;
290010696SDavid.Hollister@Sun.COM
290110696SDavid.Hollister@Sun.COM case SATA:
290210696SDavid.Hollister@Sun.COM case SAS:
290310696SDavid.Hollister@Sun.COM tgt_type = SCSI_TGT_SCSI_DEVICE;
290410696SDavid.Hollister@Sun.COM break;
290510696SDavid.Hollister@Sun.COM
290610696SDavid.Hollister@Sun.COM case EXPANDER:
290710696SDavid.Hollister@Sun.COM tgt_type = SCSI_TGT_SMP_DEVICE;
290810696SDavid.Hollister@Sun.COM break;
290910696SDavid.Hollister@Sun.COM }
291010696SDavid.Hollister@Sun.COM
291111601SDavid.Hollister@Sun.COM if (lphyp->dead || !lphyp->configured) {
291210696SDavid.Hollister@Sun.COM goto next_phy;
291310696SDavid.Hollister@Sun.COM }
291410696SDavid.Hollister@Sun.COM
291511693SDavid.Hollister@Sun.COM /*
291611693SDavid.Hollister@Sun.COM * Validate the PHY's SAS address
291711693SDavid.Hollister@Sun.COM */
291811693SDavid.Hollister@Sun.COM if (((lphyp->sas_address[0] & 0xf0) >> 4) != NAA_IEEE_REG) {
291911693SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_ERR, lphyp, NULL,
292011693SDavid.Hollister@Sun.COM "PHY 0x%p (%s) has invalid SAS address; "
292111693SDavid.Hollister@Sun.COM "will not enumerate", (void *)lphyp, lphyp->path);
292211693SDavid.Hollister@Sun.COM goto next_phy;
292311693SDavid.Hollister@Sun.COM }
292411693SDavid.Hollister@Sun.COM
292510696SDavid.Hollister@Sun.COM wwn = pmcs_barray2wwn(lphyp->sas_address);
292610696SDavid.Hollister@Sun.COM ua = scsi_wwn_to_wwnstr(wwn, 1, NULL);
292710696SDavid.Hollister@Sun.COM
292811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, lphyp, NULL,
292910696SDavid.Hollister@Sun.COM "iport_observation: adding %s on tgtmap [0x%p] phy [0x%p]",
293010696SDavid.Hollister@Sun.COM ua, (void *)tgtmap, (void*)lphyp);
293110696SDavid.Hollister@Sun.COM
293210696SDavid.Hollister@Sun.COM if (scsi_hba_tgtmap_set_add(tgtmap, tgt_type, ua, NULL) !=
293310696SDavid.Hollister@Sun.COM DDI_SUCCESS) {
293411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, NULL, NULL,
293510696SDavid.Hollister@Sun.COM "%s: failed to add address %s", __func__, ua);
293610696SDavid.Hollister@Sun.COM scsi_free_wwnstr(ua);
293710696SDavid.Hollister@Sun.COM return (B_FALSE);
293810696SDavid.Hollister@Sun.COM }
293910696SDavid.Hollister@Sun.COM scsi_free_wwnstr(ua);
294010696SDavid.Hollister@Sun.COM
294110696SDavid.Hollister@Sun.COM if (lphyp->children) {
294210696SDavid.Hollister@Sun.COM if (pmcs_report_iport_observations(pwp, iport,
294310696SDavid.Hollister@Sun.COM lphyp->children) == B_FALSE) {
294410696SDavid.Hollister@Sun.COM return (B_FALSE);
294510696SDavid.Hollister@Sun.COM }
294610696SDavid.Hollister@Sun.COM }
294710696SDavid.Hollister@Sun.COM
294810696SDavid.Hollister@Sun.COM /* for non-root phys, report siblings too */
294910696SDavid.Hollister@Sun.COM next_phy:
295010696SDavid.Hollister@Sun.COM if (IS_ROOT_PHY(lphyp)) {
295110696SDavid.Hollister@Sun.COM lphyp = NULL;
295210696SDavid.Hollister@Sun.COM } else {
295310696SDavid.Hollister@Sun.COM lphyp = lphyp->sibling;
295410696SDavid.Hollister@Sun.COM }
295510696SDavid.Hollister@Sun.COM }
295610696SDavid.Hollister@Sun.COM
295710696SDavid.Hollister@Sun.COM return (B_TRUE);
295810696SDavid.Hollister@Sun.COM }
295910696SDavid.Hollister@Sun.COM
296010696SDavid.Hollister@Sun.COM /*
296110696SDavid.Hollister@Sun.COM * Check for and configure new devices.
296210696SDavid.Hollister@Sun.COM *
296310696SDavid.Hollister@Sun.COM * If the changed device is a SATA device, add a SATA device.
296410696SDavid.Hollister@Sun.COM *
296510696SDavid.Hollister@Sun.COM * If the changed device is a SAS device, add a SAS device.
296610696SDavid.Hollister@Sun.COM *
296710696SDavid.Hollister@Sun.COM * If the changed device is an EXPANDER device, do a REPORT
296810696SDavid.Hollister@Sun.COM * GENERAL SMP command to find out the number of contained phys.
296910696SDavid.Hollister@Sun.COM *
297010696SDavid.Hollister@Sun.COM * For each number of contained phys, allocate a phy, do a
297110696SDavid.Hollister@Sun.COM * DISCOVERY SMP command to find out what kind of device it
297210696SDavid.Hollister@Sun.COM * is and add it to the linked list of phys on the *next* level.
297310696SDavid.Hollister@Sun.COM *
297410696SDavid.Hollister@Sun.COM * NOTE: pptr passed in by the caller will be a root PHY
297510696SDavid.Hollister@Sun.COM */
297610696SDavid.Hollister@Sun.COM static int
pmcs_configure_new_devices(pmcs_hw_t * pwp,pmcs_phy_t * pptr)297710696SDavid.Hollister@Sun.COM pmcs_configure_new_devices(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
297810696SDavid.Hollister@Sun.COM {
297910696SDavid.Hollister@Sun.COM int rval = 0;
298010696SDavid.Hollister@Sun.COM pmcs_iport_t *iport;
298110696SDavid.Hollister@Sun.COM pmcs_phy_t *pnext, *orig_pptr = pptr, *root_phy, *pchild;
298211601SDavid.Hollister@Sun.COM uint64_t wwn;
298310696SDavid.Hollister@Sun.COM
298410696SDavid.Hollister@Sun.COM /*
298510696SDavid.Hollister@Sun.COM * First, walk through each PHY at this level
298610696SDavid.Hollister@Sun.COM */
298710696SDavid.Hollister@Sun.COM while (pptr) {
298810696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
298910696SDavid.Hollister@Sun.COM pnext = pptr->sibling;
299010696SDavid.Hollister@Sun.COM
299110696SDavid.Hollister@Sun.COM /*
299210696SDavid.Hollister@Sun.COM * Set the new dtype if it has changed
299310696SDavid.Hollister@Sun.COM */
299410696SDavid.Hollister@Sun.COM if ((pptr->pend_dtype != NEW) &&
299510696SDavid.Hollister@Sun.COM (pptr->pend_dtype != pptr->dtype)) {
299610696SDavid.Hollister@Sun.COM pptr->dtype = pptr->pend_dtype;
299710696SDavid.Hollister@Sun.COM }
299810696SDavid.Hollister@Sun.COM
299910696SDavid.Hollister@Sun.COM if (pptr->changed == 0 || pptr->dead || pptr->configured) {
300010696SDavid.Hollister@Sun.COM goto next_phy;
300110696SDavid.Hollister@Sun.COM }
300210696SDavid.Hollister@Sun.COM
300312462Sjesse.butler@oracle.com /* Confirm that this iport is configured */
300410696SDavid.Hollister@Sun.COM root_phy = pmcs_get_root_phy(pptr);
300511601SDavid.Hollister@Sun.COM wwn = pmcs_barray2wwn(root_phy->sas_address);
300611601SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
300711601SDavid.Hollister@Sun.COM iport = pmcs_get_iport_by_wwn(pwp, wwn);
300810696SDavid.Hollister@Sun.COM if (iport == NULL) {
300911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
301010696SDavid.Hollister@Sun.COM "%s: iport not yet configured, "
301110696SDavid.Hollister@Sun.COM "retry discovery", __func__);
301210696SDavid.Hollister@Sun.COM pnext = NULL;
301310696SDavid.Hollister@Sun.COM rval = -1;
301411601SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
301510696SDavid.Hollister@Sun.COM goto next_phy;
301610696SDavid.Hollister@Sun.COM }
301710696SDavid.Hollister@Sun.COM
301811601SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
301910696SDavid.Hollister@Sun.COM switch (pptr->dtype) {
302010696SDavid.Hollister@Sun.COM case NOTHING:
302110696SDavid.Hollister@Sun.COM pptr->changed = 0;
302210696SDavid.Hollister@Sun.COM break;
302310696SDavid.Hollister@Sun.COM case SATA:
302410696SDavid.Hollister@Sun.COM case SAS:
302510696SDavid.Hollister@Sun.COM pptr->iport = iport;
302610696SDavid.Hollister@Sun.COM pmcs_new_tport(pwp, pptr);
302710696SDavid.Hollister@Sun.COM break;
302810696SDavid.Hollister@Sun.COM case EXPANDER:
302910696SDavid.Hollister@Sun.COM pmcs_configure_expander(pwp, pptr, iport);
303010696SDavid.Hollister@Sun.COM break;
303110696SDavid.Hollister@Sun.COM }
303210696SDavid.Hollister@Sun.COM pmcs_rele_iport(iport);
303310696SDavid.Hollister@Sun.COM
303410696SDavid.Hollister@Sun.COM mutex_enter(&pwp->config_lock);
303510696SDavid.Hollister@Sun.COM if (pwp->config_changed) {
303610696SDavid.Hollister@Sun.COM mutex_exit(&pwp->config_lock);
303710696SDavid.Hollister@Sun.COM pnext = NULL;
303810696SDavid.Hollister@Sun.COM goto next_phy;
303910696SDavid.Hollister@Sun.COM }
304010696SDavid.Hollister@Sun.COM mutex_exit(&pwp->config_lock);
304110696SDavid.Hollister@Sun.COM
304210696SDavid.Hollister@Sun.COM next_phy:
304310696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
304410696SDavid.Hollister@Sun.COM pptr = pnext;
304510696SDavid.Hollister@Sun.COM }
304610696SDavid.Hollister@Sun.COM
304710696SDavid.Hollister@Sun.COM if (rval != 0) {
304810696SDavid.Hollister@Sun.COM return (rval);
304910696SDavid.Hollister@Sun.COM }
305010696SDavid.Hollister@Sun.COM
305110696SDavid.Hollister@Sun.COM /*
305210696SDavid.Hollister@Sun.COM * Now walk through each PHY again, recalling ourselves if they
305310696SDavid.Hollister@Sun.COM * have children
305410696SDavid.Hollister@Sun.COM */
305510696SDavid.Hollister@Sun.COM pptr = orig_pptr;
305610696SDavid.Hollister@Sun.COM while (pptr) {
305710696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
305810696SDavid.Hollister@Sun.COM pnext = pptr->sibling;
305910696SDavid.Hollister@Sun.COM pchild = pptr->children;
306010696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
306110696SDavid.Hollister@Sun.COM
306210696SDavid.Hollister@Sun.COM if (pchild) {
306310696SDavid.Hollister@Sun.COM rval = pmcs_configure_new_devices(pwp, pchild);
306410696SDavid.Hollister@Sun.COM if (rval != 0) {
306510696SDavid.Hollister@Sun.COM break;
306610696SDavid.Hollister@Sun.COM }
306710696SDavid.Hollister@Sun.COM }
306810696SDavid.Hollister@Sun.COM
306910696SDavid.Hollister@Sun.COM pptr = pnext;
307010696SDavid.Hollister@Sun.COM }
307110696SDavid.Hollister@Sun.COM
307210696SDavid.Hollister@Sun.COM return (rval);
307310696SDavid.Hollister@Sun.COM }
307410696SDavid.Hollister@Sun.COM
307510696SDavid.Hollister@Sun.COM /*
307610696SDavid.Hollister@Sun.COM * Set all phys and descendent phys as changed if changed == B_TRUE, otherwise
307710696SDavid.Hollister@Sun.COM * mark them all as not changed.
307810696SDavid.Hollister@Sun.COM *
307910696SDavid.Hollister@Sun.COM * Called with parent PHY locked.
308010696SDavid.Hollister@Sun.COM */
308110696SDavid.Hollister@Sun.COM void
pmcs_set_changed(pmcs_hw_t * pwp,pmcs_phy_t * parent,boolean_t changed,int level)308210696SDavid.Hollister@Sun.COM pmcs_set_changed(pmcs_hw_t *pwp, pmcs_phy_t *parent, boolean_t changed,
308310696SDavid.Hollister@Sun.COM int level)
308410696SDavid.Hollister@Sun.COM {
308510696SDavid.Hollister@Sun.COM pmcs_phy_t *pptr;
308610696SDavid.Hollister@Sun.COM
308710696SDavid.Hollister@Sun.COM if (level == 0) {
308810696SDavid.Hollister@Sun.COM if (changed) {
308910696SDavid.Hollister@Sun.COM PHY_CHANGED(pwp, parent);
309010696SDavid.Hollister@Sun.COM } else {
309110696SDavid.Hollister@Sun.COM parent->changed = 0;
309210696SDavid.Hollister@Sun.COM }
309310696SDavid.Hollister@Sun.COM if (parent->dtype == EXPANDER && parent->level) {
309410696SDavid.Hollister@Sun.COM parent->width = 1;
309510696SDavid.Hollister@Sun.COM }
309610696SDavid.Hollister@Sun.COM if (parent->children) {
309710696SDavid.Hollister@Sun.COM pmcs_set_changed(pwp, parent->children, changed,
309810696SDavid.Hollister@Sun.COM level + 1);
309910696SDavid.Hollister@Sun.COM }
310010696SDavid.Hollister@Sun.COM } else {
310110696SDavid.Hollister@Sun.COM pptr = parent;
310210696SDavid.Hollister@Sun.COM while (pptr) {
310310696SDavid.Hollister@Sun.COM if (changed) {
310410696SDavid.Hollister@Sun.COM PHY_CHANGED(pwp, pptr);
310510696SDavid.Hollister@Sun.COM } else {
310610696SDavid.Hollister@Sun.COM pptr->changed = 0;
310710696SDavid.Hollister@Sun.COM }
310810696SDavid.Hollister@Sun.COM if (pptr->dtype == EXPANDER && pptr->level) {
310910696SDavid.Hollister@Sun.COM pptr->width = 1;
311010696SDavid.Hollister@Sun.COM }
311110696SDavid.Hollister@Sun.COM if (pptr->children) {
311210696SDavid.Hollister@Sun.COM pmcs_set_changed(pwp, pptr->children, changed,
311310696SDavid.Hollister@Sun.COM level + 1);
311410696SDavid.Hollister@Sun.COM }
311510696SDavid.Hollister@Sun.COM pptr = pptr->sibling;
311610696SDavid.Hollister@Sun.COM }
311710696SDavid.Hollister@Sun.COM }
311810696SDavid.Hollister@Sun.COM }
311910696SDavid.Hollister@Sun.COM
312010696SDavid.Hollister@Sun.COM /*
312110696SDavid.Hollister@Sun.COM * Take the passed phy mark it and its descendants as dead.
312210696SDavid.Hollister@Sun.COM * Fire up reconfiguration to abort commands and bury it.
312310696SDavid.Hollister@Sun.COM *
312410696SDavid.Hollister@Sun.COM * Called with the parent PHY locked.
312510696SDavid.Hollister@Sun.COM */
312610696SDavid.Hollister@Sun.COM void
pmcs_kill_changed(pmcs_hw_t * pwp,pmcs_phy_t * parent,int level)312710696SDavid.Hollister@Sun.COM pmcs_kill_changed(pmcs_hw_t *pwp, pmcs_phy_t *parent, int level)
312810696SDavid.Hollister@Sun.COM {
312910696SDavid.Hollister@Sun.COM pmcs_phy_t *pptr = parent;
313010696SDavid.Hollister@Sun.COM
313110696SDavid.Hollister@Sun.COM while (pptr) {
313210696SDavid.Hollister@Sun.COM pptr->link_rate = 0;
313310696SDavid.Hollister@Sun.COM pptr->abort_sent = 0;
313410696SDavid.Hollister@Sun.COM pptr->abort_pending = 1;
313510696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
313610696SDavid.Hollister@Sun.COM pptr->need_rl_ext = 0;
313710696SDavid.Hollister@Sun.COM
313810696SDavid.Hollister@Sun.COM if (pptr->dead == 0) {
313910696SDavid.Hollister@Sun.COM PHY_CHANGED(pwp, pptr);
314010696SDavid.Hollister@Sun.COM RESTART_DISCOVERY(pwp);
314110696SDavid.Hollister@Sun.COM }
314210696SDavid.Hollister@Sun.COM
314310696SDavid.Hollister@Sun.COM pptr->dead = 1;
314410696SDavid.Hollister@Sun.COM
314510696SDavid.Hollister@Sun.COM if (pptr->children) {
314610696SDavid.Hollister@Sun.COM pmcs_kill_changed(pwp, pptr->children, level + 1);
314710696SDavid.Hollister@Sun.COM }
314810696SDavid.Hollister@Sun.COM
314910696SDavid.Hollister@Sun.COM /*
315010696SDavid.Hollister@Sun.COM * Only kill siblings at level > 0
315110696SDavid.Hollister@Sun.COM */
315210696SDavid.Hollister@Sun.COM if (level == 0) {
315310696SDavid.Hollister@Sun.COM return;
315410696SDavid.Hollister@Sun.COM }
315510696SDavid.Hollister@Sun.COM
315610696SDavid.Hollister@Sun.COM pptr = pptr->sibling;
315710696SDavid.Hollister@Sun.COM }
315810696SDavid.Hollister@Sun.COM }
315910696SDavid.Hollister@Sun.COM
316010696SDavid.Hollister@Sun.COM /*
316110696SDavid.Hollister@Sun.COM * Go through every PHY and clear any that are dead (unless they're expanders)
316210696SDavid.Hollister@Sun.COM */
316310696SDavid.Hollister@Sun.COM static void
pmcs_clear_phys(pmcs_hw_t * pwp,pmcs_phy_t * pptr)316410696SDavid.Hollister@Sun.COM pmcs_clear_phys(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
316510696SDavid.Hollister@Sun.COM {
316610696SDavid.Hollister@Sun.COM pmcs_phy_t *pnext, *phyp;
316710696SDavid.Hollister@Sun.COM
316810696SDavid.Hollister@Sun.COM phyp = pptr;
316910696SDavid.Hollister@Sun.COM while (phyp) {
317010696SDavid.Hollister@Sun.COM if (IS_ROOT_PHY(phyp)) {
317110696SDavid.Hollister@Sun.COM pmcs_lock_phy(phyp);
317210696SDavid.Hollister@Sun.COM }
317310696SDavid.Hollister@Sun.COM
317410696SDavid.Hollister@Sun.COM if ((phyp->dtype != EXPANDER) && phyp->dead) {
317510696SDavid.Hollister@Sun.COM pmcs_clear_phy(pwp, phyp);
317610696SDavid.Hollister@Sun.COM }
317710696SDavid.Hollister@Sun.COM
317810696SDavid.Hollister@Sun.COM if (phyp->children) {
317910696SDavid.Hollister@Sun.COM pmcs_clear_phys(pwp, phyp->children);
318010696SDavid.Hollister@Sun.COM }
318110696SDavid.Hollister@Sun.COM
318210696SDavid.Hollister@Sun.COM pnext = phyp->sibling;
318310696SDavid.Hollister@Sun.COM
318410696SDavid.Hollister@Sun.COM if (IS_ROOT_PHY(phyp)) {
318510696SDavid.Hollister@Sun.COM pmcs_unlock_phy(phyp);
318610696SDavid.Hollister@Sun.COM }
318710696SDavid.Hollister@Sun.COM
318810696SDavid.Hollister@Sun.COM phyp = pnext;
318910696SDavid.Hollister@Sun.COM }
319010696SDavid.Hollister@Sun.COM }
319110696SDavid.Hollister@Sun.COM
319210696SDavid.Hollister@Sun.COM /*
319310696SDavid.Hollister@Sun.COM * Clear volatile parts of a phy. Called with PHY locked.
319410696SDavid.Hollister@Sun.COM */
319510696SDavid.Hollister@Sun.COM void
pmcs_clear_phy(pmcs_hw_t * pwp,pmcs_phy_t * pptr)319610696SDavid.Hollister@Sun.COM pmcs_clear_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
319710696SDavid.Hollister@Sun.COM {
319811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "%s: %s",
319911048SDavid.Hollister@Sun.COM __func__, pptr->path);
320010696SDavid.Hollister@Sun.COM ASSERT(mutex_owned(&pptr->phy_lock));
320110696SDavid.Hollister@Sun.COM /* keep sibling */
320210696SDavid.Hollister@Sun.COM /* keep children */
320310696SDavid.Hollister@Sun.COM /* keep parent */
320410696SDavid.Hollister@Sun.COM pptr->device_id = PMCS_INVALID_DEVICE_ID;
320510696SDavid.Hollister@Sun.COM /* keep hw_event_ack */
320610696SDavid.Hollister@Sun.COM pptr->ncphy = 0;
320710696SDavid.Hollister@Sun.COM /* keep phynum */
320810696SDavid.Hollister@Sun.COM pptr->width = 0;
320910696SDavid.Hollister@Sun.COM pptr->ds_recovery_retries = 0;
321011087SRamana.Srikanth@Sun.COM pptr->ds_prev_good_recoveries = 0;
321111087SRamana.Srikanth@Sun.COM pptr->last_good_recovery = 0;
321211087SRamana.Srikanth@Sun.COM pptr->prev_recovery = 0;
321311090SDavid.Hollister@Sun.COM
321410696SDavid.Hollister@Sun.COM /* keep dtype */
321510696SDavid.Hollister@Sun.COM pptr->config_stop = 0;
321610696SDavid.Hollister@Sun.COM pptr->spinup_hold = 0;
321710696SDavid.Hollister@Sun.COM pptr->atdt = 0;
321810696SDavid.Hollister@Sun.COM /* keep portid */
321910696SDavid.Hollister@Sun.COM pptr->link_rate = 0;
322010696SDavid.Hollister@Sun.COM pptr->valid_device_id = 0;
322110696SDavid.Hollister@Sun.COM pptr->abort_sent = 0;
322210696SDavid.Hollister@Sun.COM pptr->abort_pending = 0;
322310696SDavid.Hollister@Sun.COM pptr->need_rl_ext = 0;
322410696SDavid.Hollister@Sun.COM pptr->subsidiary = 0;
322510696SDavid.Hollister@Sun.COM pptr->configured = 0;
322611347SRamana.Srikanth@Sun.COM pptr->deregister_wait = 0;
322711601SDavid.Hollister@Sun.COM pptr->reenumerate = 0;
322810696SDavid.Hollister@Sun.COM /* Only mark dead if it's not a root PHY and its dtype isn't NOTHING */
322910696SDavid.Hollister@Sun.COM /* XXX: What about directly attached disks? */
323010696SDavid.Hollister@Sun.COM if (!IS_ROOT_PHY(pptr) && (pptr->dtype != NOTHING))
323110696SDavid.Hollister@Sun.COM pptr->dead = 1;
323210696SDavid.Hollister@Sun.COM pptr->changed = 0;
323310696SDavid.Hollister@Sun.COM /* keep SAS address */
323410696SDavid.Hollister@Sun.COM /* keep path */
323510696SDavid.Hollister@Sun.COM /* keep ref_count */
323610696SDavid.Hollister@Sun.COM /* Don't clear iport on root PHYs - they are handled in pmcs_intr.c */
323710696SDavid.Hollister@Sun.COM if (!IS_ROOT_PHY(pptr)) {
323811501SDavid.Hollister@Sun.COM pptr->last_iport = pptr->iport;
323910696SDavid.Hollister@Sun.COM pptr->iport = NULL;
324010696SDavid.Hollister@Sun.COM }
324110755SJesse.Butler@Sun.COM /* keep target */
324210696SDavid.Hollister@Sun.COM }
324310696SDavid.Hollister@Sun.COM
324410696SDavid.Hollister@Sun.COM /*
324510696SDavid.Hollister@Sun.COM * Allocate softstate for this target if there isn't already one. If there
324610696SDavid.Hollister@Sun.COM * is, just redo our internal configuration. If it is actually "new", we'll
324710696SDavid.Hollister@Sun.COM * soon get a tran_tgt_init for it.
324810696SDavid.Hollister@Sun.COM *
324910696SDavid.Hollister@Sun.COM * Called with PHY locked.
325010696SDavid.Hollister@Sun.COM */
325110696SDavid.Hollister@Sun.COM static void
pmcs_new_tport(pmcs_hw_t * pwp,pmcs_phy_t * pptr)325210696SDavid.Hollister@Sun.COM pmcs_new_tport(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
325310696SDavid.Hollister@Sun.COM {
325411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "%s: phy 0x%p @ %s",
325511048SDavid.Hollister@Sun.COM __func__, (void *)pptr, pptr->path);
325610696SDavid.Hollister@Sun.COM
325710696SDavid.Hollister@Sun.COM if (pmcs_configure_phy(pwp, pptr) == B_FALSE) {
325810696SDavid.Hollister@Sun.COM /*
325910696SDavid.Hollister@Sun.COM * If the config failed, mark the PHY as changed.
326010696SDavid.Hollister@Sun.COM */
326110696SDavid.Hollister@Sun.COM PHY_CHANGED(pwp, pptr);
326211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
326310696SDavid.Hollister@Sun.COM "%s: pmcs_configure_phy failed for phy 0x%p", __func__,
326410696SDavid.Hollister@Sun.COM (void *)pptr);
326510696SDavid.Hollister@Sun.COM return;
326610696SDavid.Hollister@Sun.COM }
326710696SDavid.Hollister@Sun.COM
326810696SDavid.Hollister@Sun.COM /* Mark PHY as no longer changed */
326910696SDavid.Hollister@Sun.COM pptr->changed = 0;
327010696SDavid.Hollister@Sun.COM
327110696SDavid.Hollister@Sun.COM /*
327211501SDavid.Hollister@Sun.COM * If the PHY has no target pointer:
327311501SDavid.Hollister@Sun.COM *
327411501SDavid.Hollister@Sun.COM * If it's a root PHY, see if another PHY in the iport holds the
327511501SDavid.Hollister@Sun.COM * target pointer (primary PHY changed). If so, move it over.
327611501SDavid.Hollister@Sun.COM *
327711501SDavid.Hollister@Sun.COM * If it's not a root PHY, see if there's a PHY on the dead_phys
327811501SDavid.Hollister@Sun.COM * list that matches.
327910696SDavid.Hollister@Sun.COM */
328010696SDavid.Hollister@Sun.COM if (pptr->target == NULL) {
328111501SDavid.Hollister@Sun.COM if (IS_ROOT_PHY(pptr)) {
328211501SDavid.Hollister@Sun.COM pmcs_phy_t *rphy = pwp->root_phys;
328311501SDavid.Hollister@Sun.COM
328411501SDavid.Hollister@Sun.COM while (rphy) {
328511501SDavid.Hollister@Sun.COM if (rphy == pptr) {
328611501SDavid.Hollister@Sun.COM rphy = rphy->sibling;
328711501SDavid.Hollister@Sun.COM continue;
328811501SDavid.Hollister@Sun.COM }
328911501SDavid.Hollister@Sun.COM
329011501SDavid.Hollister@Sun.COM mutex_enter(&rphy->phy_lock);
329111501SDavid.Hollister@Sun.COM if ((rphy->iport == pptr->iport) &&
329211501SDavid.Hollister@Sun.COM (rphy->target != NULL)) {
329311501SDavid.Hollister@Sun.COM mutex_enter(&rphy->target->statlock);
329411501SDavid.Hollister@Sun.COM pptr->target = rphy->target;
329511501SDavid.Hollister@Sun.COM rphy->target = NULL;
329611501SDavid.Hollister@Sun.COM pptr->target->phy = pptr;
329711501SDavid.Hollister@Sun.COM /* The target is now on pptr */
329811501SDavid.Hollister@Sun.COM mutex_exit(&pptr->target->statlock);
329911501SDavid.Hollister@Sun.COM mutex_exit(&rphy->phy_lock);
330011501SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
330111501SDavid.Hollister@Sun.COM pptr, pptr->target,
330211501SDavid.Hollister@Sun.COM "%s: Moved target from %s to %s",
330311501SDavid.Hollister@Sun.COM __func__, rphy->path, pptr->path);
330411501SDavid.Hollister@Sun.COM break;
330511501SDavid.Hollister@Sun.COM }
330611501SDavid.Hollister@Sun.COM mutex_exit(&rphy->phy_lock);
330711501SDavid.Hollister@Sun.COM
330811501SDavid.Hollister@Sun.COM rphy = rphy->sibling;
330911501SDavid.Hollister@Sun.COM }
331011501SDavid.Hollister@Sun.COM } else {
331111501SDavid.Hollister@Sun.COM pmcs_reap_dead_phy(pptr);
331211501SDavid.Hollister@Sun.COM }
331310696SDavid.Hollister@Sun.COM }
331410696SDavid.Hollister@Sun.COM
331510696SDavid.Hollister@Sun.COM /*
331610696SDavid.Hollister@Sun.COM * Only assign the device if there is a target for this PHY with a
331710696SDavid.Hollister@Sun.COM * matching SAS address. If an iport is disconnected from one piece
331810696SDavid.Hollister@Sun.COM * of storage and connected to another within the iport stabilization
331910696SDavid.Hollister@Sun.COM * time, we can get the PHY/target mismatch situation.
332010696SDavid.Hollister@Sun.COM *
332110696SDavid.Hollister@Sun.COM * Otherwise, it'll get done in tran_tgt_init.
332210696SDavid.Hollister@Sun.COM */
332310696SDavid.Hollister@Sun.COM if (pptr->target) {
332410696SDavid.Hollister@Sun.COM mutex_enter(&pptr->target->statlock);
332510696SDavid.Hollister@Sun.COM if (pmcs_phy_target_match(pptr) == B_FALSE) {
332610696SDavid.Hollister@Sun.COM mutex_exit(&pptr->target->statlock);
332710696SDavid.Hollister@Sun.COM if (!IS_ROOT_PHY(pptr)) {
332810696SDavid.Hollister@Sun.COM pmcs_dec_phy_ref_count(pptr);
332910696SDavid.Hollister@Sun.COM }
333011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
333110696SDavid.Hollister@Sun.COM "%s: Not assigning existing tgt %p for PHY %p "
333210696SDavid.Hollister@Sun.COM "(WWN mismatch)", __func__, (void *)pptr->target,
333310696SDavid.Hollister@Sun.COM (void *)pptr);
333410696SDavid.Hollister@Sun.COM pptr->target = NULL;
333510696SDavid.Hollister@Sun.COM return;
333610696SDavid.Hollister@Sun.COM }
333710696SDavid.Hollister@Sun.COM
333810696SDavid.Hollister@Sun.COM if (!pmcs_assign_device(pwp, pptr->target)) {
333911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
334010696SDavid.Hollister@Sun.COM "%s: pmcs_assign_device failed for target 0x%p",
334110696SDavid.Hollister@Sun.COM __func__, (void *)pptr->target);
334210696SDavid.Hollister@Sun.COM }
334310696SDavid.Hollister@Sun.COM mutex_exit(&pptr->target->statlock);
334410696SDavid.Hollister@Sun.COM }
334510696SDavid.Hollister@Sun.COM }
334610696SDavid.Hollister@Sun.COM
334710696SDavid.Hollister@Sun.COM /*
334810696SDavid.Hollister@Sun.COM * Called with PHY lock held.
334910696SDavid.Hollister@Sun.COM */
335010696SDavid.Hollister@Sun.COM static boolean_t
pmcs_configure_phy(pmcs_hw_t * pwp,pmcs_phy_t * pptr)335110696SDavid.Hollister@Sun.COM pmcs_configure_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
335210696SDavid.Hollister@Sun.COM {
335310696SDavid.Hollister@Sun.COM char *dtype;
335410696SDavid.Hollister@Sun.COM
335510696SDavid.Hollister@Sun.COM ASSERT(mutex_owned(&pptr->phy_lock));
335610696SDavid.Hollister@Sun.COM
335710696SDavid.Hollister@Sun.COM /*
335810696SDavid.Hollister@Sun.COM * Mark this device as no longer changed.
335910696SDavid.Hollister@Sun.COM */
336010696SDavid.Hollister@Sun.COM pptr->changed = 0;
336110696SDavid.Hollister@Sun.COM
336210696SDavid.Hollister@Sun.COM /*
336310696SDavid.Hollister@Sun.COM * If we don't have a device handle, get one.
336410696SDavid.Hollister@Sun.COM */
336510696SDavid.Hollister@Sun.COM if (pmcs_get_device_handle(pwp, pptr)) {
336610696SDavid.Hollister@Sun.COM return (B_FALSE);
336710696SDavid.Hollister@Sun.COM }
336810696SDavid.Hollister@Sun.COM
336910696SDavid.Hollister@Sun.COM pptr->configured = 1;
337010696SDavid.Hollister@Sun.COM
337110696SDavid.Hollister@Sun.COM switch (pptr->dtype) {
337210696SDavid.Hollister@Sun.COM case SAS:
337310696SDavid.Hollister@Sun.COM dtype = "SAS";
337410696SDavid.Hollister@Sun.COM break;
337510696SDavid.Hollister@Sun.COM case SATA:
337610696SDavid.Hollister@Sun.COM dtype = "SATA";
337710696SDavid.Hollister@Sun.COM break;
337810696SDavid.Hollister@Sun.COM case EXPANDER:
337910696SDavid.Hollister@Sun.COM dtype = "SMP";
338010696SDavid.Hollister@Sun.COM break;
338110696SDavid.Hollister@Sun.COM default:
338210696SDavid.Hollister@Sun.COM dtype = "???";
338310696SDavid.Hollister@Sun.COM }
338410696SDavid.Hollister@Sun.COM
338511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "config_dev: %s "
338611048SDavid.Hollister@Sun.COM "dev %s " SAS_ADDR_FMT " dev id 0x%x lr 0x%x", dtype, pptr->path,
338710696SDavid.Hollister@Sun.COM SAS_ADDR_PRT(pptr->sas_address), pptr->device_id, pptr->link_rate);
338810696SDavid.Hollister@Sun.COM
338910696SDavid.Hollister@Sun.COM return (B_TRUE);
339010696SDavid.Hollister@Sun.COM }
339110696SDavid.Hollister@Sun.COM
339210696SDavid.Hollister@Sun.COM /*
339310696SDavid.Hollister@Sun.COM * Called with PHY locked
339410696SDavid.Hollister@Sun.COM */
339510696SDavid.Hollister@Sun.COM static void
pmcs_configure_expander(pmcs_hw_t * pwp,pmcs_phy_t * pptr,pmcs_iport_t * iport)339610696SDavid.Hollister@Sun.COM pmcs_configure_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr, pmcs_iport_t *iport)
339710696SDavid.Hollister@Sun.COM {
339810696SDavid.Hollister@Sun.COM pmcs_phy_t *ctmp, *clist = NULL, *cnext;
339910696SDavid.Hollister@Sun.COM int result, i, nphy = 0;
340010696SDavid.Hollister@Sun.COM boolean_t root_phy = B_FALSE;
340110696SDavid.Hollister@Sun.COM
340210696SDavid.Hollister@Sun.COM ASSERT(iport);
340310696SDavid.Hollister@Sun.COM
340410696SDavid.Hollister@Sun.COM /*
340510696SDavid.Hollister@Sun.COM * Step 1- clear our "changed" bit. If we need to retry/restart due
340610696SDavid.Hollister@Sun.COM * to resource shortages, we'll set it again. While we're doing
340710696SDavid.Hollister@Sun.COM * configuration, other events may set it again as well. If the PHY
340810696SDavid.Hollister@Sun.COM * is a root PHY and is currently marked as having changed, reset the
340910696SDavid.Hollister@Sun.COM * config_stop timer as well.
341010696SDavid.Hollister@Sun.COM */
341110696SDavid.Hollister@Sun.COM if (IS_ROOT_PHY(pptr) && pptr->changed) {
341210696SDavid.Hollister@Sun.COM pptr->config_stop = ddi_get_lbolt() +
341310696SDavid.Hollister@Sun.COM drv_usectohz(PMCS_MAX_CONFIG_TIME);
341410696SDavid.Hollister@Sun.COM }
341510696SDavid.Hollister@Sun.COM pptr->changed = 0;
341610696SDavid.Hollister@Sun.COM
341710696SDavid.Hollister@Sun.COM /*
341810696SDavid.Hollister@Sun.COM * Step 2- make sure we don't overflow
341910696SDavid.Hollister@Sun.COM */
342010696SDavid.Hollister@Sun.COM if (pptr->level == PMCS_MAX_XPND-1) {
342111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_WARN, pptr, NULL,
342210696SDavid.Hollister@Sun.COM "%s: SAS expansion tree too deep", __func__);
342310696SDavid.Hollister@Sun.COM return;
342410696SDavid.Hollister@Sun.COM }
342510696SDavid.Hollister@Sun.COM
342610696SDavid.Hollister@Sun.COM /*
342710696SDavid.Hollister@Sun.COM * Step 3- Check if this expander is part of a wide phy that has
342810696SDavid.Hollister@Sun.COM * already been configured.
342910696SDavid.Hollister@Sun.COM *
343010696SDavid.Hollister@Sun.COM * This is known by checking this level for another EXPANDER device
343110696SDavid.Hollister@Sun.COM * with the same SAS address and isn't already marked as a subsidiary
343210696SDavid.Hollister@Sun.COM * phy and a parent whose SAS address is the same as our SAS address
343310696SDavid.Hollister@Sun.COM * (if there are parents).
343410696SDavid.Hollister@Sun.COM */
343510696SDavid.Hollister@Sun.COM if (!IS_ROOT_PHY(pptr)) {
343610696SDavid.Hollister@Sun.COM /*
343710696SDavid.Hollister@Sun.COM * No need to lock the parent here because we're in discovery
343810696SDavid.Hollister@Sun.COM * and the only time a PHY's children pointer can change is
343910696SDavid.Hollister@Sun.COM * in discovery; either in pmcs_clear_expander (which has
344010696SDavid.Hollister@Sun.COM * already been called) or here, down below. Plus, trying to
344110696SDavid.Hollister@Sun.COM * grab the parent's lock here can cause deadlock.
344210696SDavid.Hollister@Sun.COM */
344310696SDavid.Hollister@Sun.COM ctmp = pptr->parent->children;
344410696SDavid.Hollister@Sun.COM } else {
344510696SDavid.Hollister@Sun.COM ctmp = pwp->root_phys;
344610696SDavid.Hollister@Sun.COM root_phy = B_TRUE;
344710696SDavid.Hollister@Sun.COM }
344810696SDavid.Hollister@Sun.COM
344910696SDavid.Hollister@Sun.COM while (ctmp) {
345010696SDavid.Hollister@Sun.COM /*
345110696SDavid.Hollister@Sun.COM * If we've checked all PHYs up to pptr, we stop. Otherwise,
345210696SDavid.Hollister@Sun.COM * we'll be checking for a primary PHY with a higher PHY
345310696SDavid.Hollister@Sun.COM * number than pptr, which will never happen. The primary
345410696SDavid.Hollister@Sun.COM * PHY on non-root expanders will ALWAYS be the lowest
345510696SDavid.Hollister@Sun.COM * numbered PHY.
345610696SDavid.Hollister@Sun.COM */
345710696SDavid.Hollister@Sun.COM if (ctmp == pptr) {
345810696SDavid.Hollister@Sun.COM break;
345910696SDavid.Hollister@Sun.COM }
346010696SDavid.Hollister@Sun.COM
346110696SDavid.Hollister@Sun.COM /*
346210696SDavid.Hollister@Sun.COM * If pptr and ctmp are root PHYs, just grab the mutex on
346310696SDavid.Hollister@Sun.COM * ctmp. No need to lock the entire tree. If they are not
346410696SDavid.Hollister@Sun.COM * root PHYs, there is no need to lock since a non-root PHY's
346510696SDavid.Hollister@Sun.COM * SAS address and other characteristics can only change in
346610696SDavid.Hollister@Sun.COM * discovery anyway.
346710696SDavid.Hollister@Sun.COM */
346810696SDavid.Hollister@Sun.COM if (root_phy) {
346910696SDavid.Hollister@Sun.COM mutex_enter(&ctmp->phy_lock);
347010696SDavid.Hollister@Sun.COM }
347110696SDavid.Hollister@Sun.COM
347210696SDavid.Hollister@Sun.COM if (ctmp->dtype == EXPANDER && ctmp->width &&
347310696SDavid.Hollister@Sun.COM memcmp(ctmp->sas_address, pptr->sas_address, 8) == 0) {
347410696SDavid.Hollister@Sun.COM int widephy = 0;
347510696SDavid.Hollister@Sun.COM /*
347610696SDavid.Hollister@Sun.COM * If these phys are not root PHYs, compare their SAS
347710696SDavid.Hollister@Sun.COM * addresses too.
347810696SDavid.Hollister@Sun.COM */
347910696SDavid.Hollister@Sun.COM if (!root_phy) {
348010696SDavid.Hollister@Sun.COM if (memcmp(ctmp->parent->sas_address,
348110696SDavid.Hollister@Sun.COM pptr->parent->sas_address, 8) == 0) {
348210696SDavid.Hollister@Sun.COM widephy = 1;
348310696SDavid.Hollister@Sun.COM }
348410696SDavid.Hollister@Sun.COM } else {
348510696SDavid.Hollister@Sun.COM widephy = 1;
348610696SDavid.Hollister@Sun.COM }
348710696SDavid.Hollister@Sun.COM if (widephy) {
348810696SDavid.Hollister@Sun.COM ctmp->width++;
348910696SDavid.Hollister@Sun.COM pptr->subsidiary = 1;
349011307SDavid.Hollister@Sun.COM
349111307SDavid.Hollister@Sun.COM /*
349211307SDavid.Hollister@Sun.COM * Update the primary PHY's attached-port-pm
349311307SDavid.Hollister@Sun.COM * and target-port-pm information with the info
349411307SDavid.Hollister@Sun.COM * from this subsidiary
349511307SDavid.Hollister@Sun.COM */
349611307SDavid.Hollister@Sun.COM pmcs_update_phy_pm_props(ctmp,
349711307SDavid.Hollister@Sun.COM pptr->att_port_pm_tmp,
349811307SDavid.Hollister@Sun.COM pptr->tgt_port_pm_tmp, B_TRUE);
349911307SDavid.Hollister@Sun.COM
350011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
350111048SDavid.Hollister@Sun.COM "%s: PHY %s part of wide PHY %s "
350211048SDavid.Hollister@Sun.COM "(now %d wide)", __func__, pptr->path,
350311048SDavid.Hollister@Sun.COM ctmp->path, ctmp->width);
350410696SDavid.Hollister@Sun.COM if (root_phy) {
350510696SDavid.Hollister@Sun.COM mutex_exit(&ctmp->phy_lock);
350610696SDavid.Hollister@Sun.COM }
350710696SDavid.Hollister@Sun.COM return;
350810696SDavid.Hollister@Sun.COM }
350910696SDavid.Hollister@Sun.COM }
351010696SDavid.Hollister@Sun.COM
351110696SDavid.Hollister@Sun.COM cnext = ctmp->sibling;
351210696SDavid.Hollister@Sun.COM if (root_phy) {
351310696SDavid.Hollister@Sun.COM mutex_exit(&ctmp->phy_lock);
351410696SDavid.Hollister@Sun.COM }
351510696SDavid.Hollister@Sun.COM ctmp = cnext;
351610696SDavid.Hollister@Sun.COM }
351710696SDavid.Hollister@Sun.COM
351810696SDavid.Hollister@Sun.COM /*
351910696SDavid.Hollister@Sun.COM * Step 4- If we don't have a device handle, get one. Since this
352010696SDavid.Hollister@Sun.COM * is the primary PHY, make sure subsidiary is cleared.
352110696SDavid.Hollister@Sun.COM */
352210696SDavid.Hollister@Sun.COM pptr->subsidiary = 0;
352311267SJesse.Butler@Sun.COM pptr->iport = iport;
352410696SDavid.Hollister@Sun.COM if (pmcs_get_device_handle(pwp, pptr)) {
352510696SDavid.Hollister@Sun.COM goto out;
352610696SDavid.Hollister@Sun.COM }
352711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, "Config expander %s "
352810696SDavid.Hollister@Sun.COM SAS_ADDR_FMT " dev id 0x%x lr 0x%x", pptr->path,
352910696SDavid.Hollister@Sun.COM SAS_ADDR_PRT(pptr->sas_address), pptr->device_id, pptr->link_rate);
353010696SDavid.Hollister@Sun.COM
353110696SDavid.Hollister@Sun.COM /*
353210696SDavid.Hollister@Sun.COM * Step 5- figure out how many phys are in this expander.
353310696SDavid.Hollister@Sun.COM */
353410696SDavid.Hollister@Sun.COM nphy = pmcs_expander_get_nphy(pwp, pptr);
353510696SDavid.Hollister@Sun.COM if (nphy <= 0) {
353610696SDavid.Hollister@Sun.COM if (nphy == 0 && ddi_get_lbolt() < pptr->config_stop) {
353710696SDavid.Hollister@Sun.COM PHY_CHANGED(pwp, pptr);
353810696SDavid.Hollister@Sun.COM RESTART_DISCOVERY(pwp);
353910696SDavid.Hollister@Sun.COM } else {
354011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
354110696SDavid.Hollister@Sun.COM "%s: Retries exhausted for %s, killing", __func__,
354210696SDavid.Hollister@Sun.COM pptr->path);
354310696SDavid.Hollister@Sun.COM pptr->config_stop = 0;
354410696SDavid.Hollister@Sun.COM pmcs_kill_changed(pwp, pptr, 0);
354510696SDavid.Hollister@Sun.COM }
354610696SDavid.Hollister@Sun.COM goto out;
354710696SDavid.Hollister@Sun.COM }
354810696SDavid.Hollister@Sun.COM
354910696SDavid.Hollister@Sun.COM /*
355010696SDavid.Hollister@Sun.COM * Step 6- Allocate a list of phys for this expander and figure out
355110696SDavid.Hollister@Sun.COM * what each one is.
355210696SDavid.Hollister@Sun.COM */
355310696SDavid.Hollister@Sun.COM for (i = 0; i < nphy; i++) {
355410696SDavid.Hollister@Sun.COM ctmp = kmem_cache_alloc(pwp->phy_cache, KM_SLEEP);
355510696SDavid.Hollister@Sun.COM bzero(ctmp, sizeof (pmcs_phy_t));
355610696SDavid.Hollister@Sun.COM ctmp->device_id = PMCS_INVALID_DEVICE_ID;
355710696SDavid.Hollister@Sun.COM ctmp->sibling = clist;
355810696SDavid.Hollister@Sun.COM ctmp->pend_dtype = NEW; /* Init pending dtype */
355910696SDavid.Hollister@Sun.COM ctmp->config_stop = ddi_get_lbolt() +
356010696SDavid.Hollister@Sun.COM drv_usectohz(PMCS_MAX_CONFIG_TIME);
356110696SDavid.Hollister@Sun.COM clist = ctmp;
356210696SDavid.Hollister@Sun.COM }
356310696SDavid.Hollister@Sun.COM
356410696SDavid.Hollister@Sun.COM mutex_enter(&pwp->config_lock);
356510696SDavid.Hollister@Sun.COM if (pwp->config_changed) {
356610696SDavid.Hollister@Sun.COM RESTART_DISCOVERY_LOCKED(pwp);
356710696SDavid.Hollister@Sun.COM mutex_exit(&pwp->config_lock);
356810696SDavid.Hollister@Sun.COM /*
356910696SDavid.Hollister@Sun.COM * Clean up the newly allocated PHYs and return
357010696SDavid.Hollister@Sun.COM */
357110696SDavid.Hollister@Sun.COM while (clist) {
357210696SDavid.Hollister@Sun.COM ctmp = clist->sibling;
357312668Ssrikanth.suravajhala@oracle.com clist->target_addr = NULL;
357410696SDavid.Hollister@Sun.COM kmem_cache_free(pwp->phy_cache, clist);
357510696SDavid.Hollister@Sun.COM clist = ctmp;
357610696SDavid.Hollister@Sun.COM }
357710696SDavid.Hollister@Sun.COM return;
357810696SDavid.Hollister@Sun.COM }
357910696SDavid.Hollister@Sun.COM mutex_exit(&pwp->config_lock);
358010696SDavid.Hollister@Sun.COM
358110696SDavid.Hollister@Sun.COM /*
358210696SDavid.Hollister@Sun.COM * Step 7- Now fill in the rest of the static portions of the phy.
358310696SDavid.Hollister@Sun.COM */
358410696SDavid.Hollister@Sun.COM for (i = 0, ctmp = clist; ctmp; ctmp = ctmp->sibling, i++) {
358510696SDavid.Hollister@Sun.COM ctmp->parent = pptr;
358610696SDavid.Hollister@Sun.COM ctmp->pwp = pwp;
358710696SDavid.Hollister@Sun.COM ctmp->level = pptr->level+1;
358810696SDavid.Hollister@Sun.COM ctmp->portid = pptr->portid;
358910696SDavid.Hollister@Sun.COM if (ctmp->tolerates_sas2) {
359010696SDavid.Hollister@Sun.COM ASSERT(i < SAS2_PHYNUM_MAX);
359110696SDavid.Hollister@Sun.COM ctmp->phynum = i & SAS2_PHYNUM_MASK;
359210696SDavid.Hollister@Sun.COM } else {
359310696SDavid.Hollister@Sun.COM ASSERT(i < SAS_PHYNUM_MAX);
359410696SDavid.Hollister@Sun.COM ctmp->phynum = i & SAS_PHYNUM_MASK;
359510696SDavid.Hollister@Sun.COM }
359610696SDavid.Hollister@Sun.COM pmcs_phy_name(pwp, ctmp, ctmp->path, sizeof (ctmp->path));
359710696SDavid.Hollister@Sun.COM pmcs_lock_phy(ctmp);
359810696SDavid.Hollister@Sun.COM }
359910696SDavid.Hollister@Sun.COM
360010696SDavid.Hollister@Sun.COM /*
360110696SDavid.Hollister@Sun.COM * Step 8- Discover things about each phy in the expander.
360210696SDavid.Hollister@Sun.COM */
360310696SDavid.Hollister@Sun.COM for (i = 0, ctmp = clist; ctmp; ctmp = ctmp->sibling, i++) {
360410696SDavid.Hollister@Sun.COM result = pmcs_expander_content_discover(pwp, pptr, ctmp);
360510696SDavid.Hollister@Sun.COM if (result <= 0) {
360610696SDavid.Hollister@Sun.COM if (ddi_get_lbolt() < pptr->config_stop) {
360710696SDavid.Hollister@Sun.COM PHY_CHANGED(pwp, pptr);
360810696SDavid.Hollister@Sun.COM RESTART_DISCOVERY(pwp);
360910696SDavid.Hollister@Sun.COM } else {
361010696SDavid.Hollister@Sun.COM pptr->config_stop = 0;
361111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
361210696SDavid.Hollister@Sun.COM "%s: Retries exhausted for %s, killing",
361310696SDavid.Hollister@Sun.COM __func__, pptr->path);
361410696SDavid.Hollister@Sun.COM pmcs_kill_changed(pwp, pptr, 0);
361510696SDavid.Hollister@Sun.COM }
361610696SDavid.Hollister@Sun.COM goto out;
361710696SDavid.Hollister@Sun.COM }
361810696SDavid.Hollister@Sun.COM
361910696SDavid.Hollister@Sun.COM /* Set pend_dtype to dtype for 1st time initialization */
362010696SDavid.Hollister@Sun.COM ctmp->pend_dtype = ctmp->dtype;
362110696SDavid.Hollister@Sun.COM }
362210696SDavid.Hollister@Sun.COM
362310696SDavid.Hollister@Sun.COM /*
362411601SDavid.Hollister@Sun.COM * Step 9: Install the new list on the next level. There should
362511601SDavid.Hollister@Sun.COM * typically be no children pointer on this PHY. There is one known
362611601SDavid.Hollister@Sun.COM * case where this can happen, though. If a root PHY goes down and
362711601SDavid.Hollister@Sun.COM * comes back up before discovery can run, we will fail to remove the
362811601SDavid.Hollister@Sun.COM * children from that PHY since it will no longer be marked dead.
362911601SDavid.Hollister@Sun.COM * However, in this case, all children should also be marked dead. If
363011601SDavid.Hollister@Sun.COM * we see that, take those children and put them on the dead_phys list.
363110696SDavid.Hollister@Sun.COM */
363210696SDavid.Hollister@Sun.COM if (pptr->children != NULL) {
363311601SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
363411601SDavid.Hollister@Sun.COM "%s: Expander @ %s still has children: Clean up",
363511048SDavid.Hollister@Sun.COM __func__, pptr->path);
363611601SDavid.Hollister@Sun.COM pmcs_add_dead_phys(pwp, pptr->children);
363711601SDavid.Hollister@Sun.COM }
363811601SDavid.Hollister@Sun.COM
363911601SDavid.Hollister@Sun.COM /*
364011601SDavid.Hollister@Sun.COM * Set the new children pointer for this expander
364111601SDavid.Hollister@Sun.COM */
364211601SDavid.Hollister@Sun.COM pptr->children = clist;
364310696SDavid.Hollister@Sun.COM clist = NULL;
364410696SDavid.Hollister@Sun.COM pptr->ncphy = nphy;
364510696SDavid.Hollister@Sun.COM pptr->configured = 1;
364610696SDavid.Hollister@Sun.COM
364710696SDavid.Hollister@Sun.COM /*
364810696SDavid.Hollister@Sun.COM * We only set width if we're greater than level 0.
364910696SDavid.Hollister@Sun.COM */
365010696SDavid.Hollister@Sun.COM if (pptr->level) {
365110696SDavid.Hollister@Sun.COM pptr->width = 1;
365210696SDavid.Hollister@Sun.COM }
365310696SDavid.Hollister@Sun.COM
365410696SDavid.Hollister@Sun.COM /*
365510696SDavid.Hollister@Sun.COM * Now tell the rest of the world about us, as an SMP node.
365610696SDavid.Hollister@Sun.COM */
365710696SDavid.Hollister@Sun.COM pptr->iport = iport;
365810696SDavid.Hollister@Sun.COM pmcs_new_tport(pwp, pptr);
365910696SDavid.Hollister@Sun.COM
366010696SDavid.Hollister@Sun.COM out:
366110696SDavid.Hollister@Sun.COM while (clist) {
366210696SDavid.Hollister@Sun.COM ctmp = clist->sibling;
366310696SDavid.Hollister@Sun.COM pmcs_unlock_phy(clist);
366412668Ssrikanth.suravajhala@oracle.com clist->target_addr = NULL;
366510696SDavid.Hollister@Sun.COM kmem_cache_free(pwp->phy_cache, clist);
366610696SDavid.Hollister@Sun.COM clist = ctmp;
366710696SDavid.Hollister@Sun.COM }
366810696SDavid.Hollister@Sun.COM }
366910696SDavid.Hollister@Sun.COM
367010696SDavid.Hollister@Sun.COM /*
367110696SDavid.Hollister@Sun.COM * 2. Check expanders marked changed (but not dead) to see if they still have
367210696SDavid.Hollister@Sun.COM * the same number of phys and the same SAS address. Mark them, their subsidiary
367310696SDavid.Hollister@Sun.COM * phys (if wide) and their descendents dead if anything has changed. Check the
367410696SDavid.Hollister@Sun.COM * the devices they contain to see if *they* have changed. If they've changed
367510696SDavid.Hollister@Sun.COM * from type NOTHING we leave them marked changed to be configured later
367610696SDavid.Hollister@Sun.COM * (picking up a new SAS address and link rate if possible). Otherwise, any
367710696SDavid.Hollister@Sun.COM * change in type, SAS address or removal of target role will cause us to
367810696SDavid.Hollister@Sun.COM * mark them (and their descendents) as dead and cause any pending commands
367910696SDavid.Hollister@Sun.COM * and associated devices to be removed.
368010696SDavid.Hollister@Sun.COM *
368110696SDavid.Hollister@Sun.COM * Called with PHY (pptr) locked.
368210696SDavid.Hollister@Sun.COM */
368310696SDavid.Hollister@Sun.COM
368410696SDavid.Hollister@Sun.COM static void
pmcs_check_expander(pmcs_hw_t * pwp,pmcs_phy_t * pptr)368510696SDavid.Hollister@Sun.COM pmcs_check_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
368610696SDavid.Hollister@Sun.COM {
368710696SDavid.Hollister@Sun.COM int nphy, result;
368810696SDavid.Hollister@Sun.COM pmcs_phy_t *ctmp, *local, *local_list = NULL, *local_tail = NULL;
368910696SDavid.Hollister@Sun.COM boolean_t kill_changed, changed;
369010696SDavid.Hollister@Sun.COM
369111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
369210696SDavid.Hollister@Sun.COM "%s: check %s", __func__, pptr->path);
369310696SDavid.Hollister@Sun.COM
369410696SDavid.Hollister@Sun.COM /*
369510696SDavid.Hollister@Sun.COM * Step 1: Mark phy as not changed. We will mark it changed if we need
369610696SDavid.Hollister@Sun.COM * to retry.
369710696SDavid.Hollister@Sun.COM */
369810696SDavid.Hollister@Sun.COM pptr->changed = 0;
369910696SDavid.Hollister@Sun.COM
370010696SDavid.Hollister@Sun.COM /*
370110696SDavid.Hollister@Sun.COM * Reset the config_stop time. Although we're not actually configuring
370210696SDavid.Hollister@Sun.COM * anything here, we do want some indication of when to give up trying
370310696SDavid.Hollister@Sun.COM * if we can't communicate with the expander.
370410696SDavid.Hollister@Sun.COM */
370510696SDavid.Hollister@Sun.COM pptr->config_stop = ddi_get_lbolt() +
370610696SDavid.Hollister@Sun.COM drv_usectohz(PMCS_MAX_CONFIG_TIME);
370710696SDavid.Hollister@Sun.COM
370810696SDavid.Hollister@Sun.COM /*
370910696SDavid.Hollister@Sun.COM * Step 2: Figure out how many phys are in this expander. If
371010696SDavid.Hollister@Sun.COM * pmcs_expander_get_nphy returns 0 we ran out of resources,
371110696SDavid.Hollister@Sun.COM * so reschedule and try later. If it returns another error,
371210696SDavid.Hollister@Sun.COM * just return.
371310696SDavid.Hollister@Sun.COM */
371410696SDavid.Hollister@Sun.COM nphy = pmcs_expander_get_nphy(pwp, pptr);
371510696SDavid.Hollister@Sun.COM if (nphy <= 0) {
371610696SDavid.Hollister@Sun.COM if ((nphy == 0) && (ddi_get_lbolt() < pptr->config_stop)) {
371710696SDavid.Hollister@Sun.COM PHY_CHANGED(pwp, pptr);
371810696SDavid.Hollister@Sun.COM RESTART_DISCOVERY(pwp);
371910696SDavid.Hollister@Sun.COM } else {
372010696SDavid.Hollister@Sun.COM pptr->config_stop = 0;
372111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
372210696SDavid.Hollister@Sun.COM "%s: Retries exhausted for %s, killing", __func__,
372310696SDavid.Hollister@Sun.COM pptr->path);
372410696SDavid.Hollister@Sun.COM pmcs_kill_changed(pwp, pptr, 0);
372510696SDavid.Hollister@Sun.COM }
372610696SDavid.Hollister@Sun.COM return;
372710696SDavid.Hollister@Sun.COM }
372810696SDavid.Hollister@Sun.COM
372910696SDavid.Hollister@Sun.COM /*
373010696SDavid.Hollister@Sun.COM * Step 3: If the number of phys don't agree, kill the old sub-tree.
373110696SDavid.Hollister@Sun.COM */
373210696SDavid.Hollister@Sun.COM if (nphy != pptr->ncphy) {
373311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
373410696SDavid.Hollister@Sun.COM "%s: number of contained phys for %s changed from %d to %d",
373510696SDavid.Hollister@Sun.COM __func__, pptr->path, pptr->ncphy, nphy);
373610696SDavid.Hollister@Sun.COM /*
373710696SDavid.Hollister@Sun.COM * Force a rescan of this expander after dead contents
373810696SDavid.Hollister@Sun.COM * are cleared and removed.
373910696SDavid.Hollister@Sun.COM */
374010696SDavid.Hollister@Sun.COM pmcs_kill_changed(pwp, pptr, 0);
374110696SDavid.Hollister@Sun.COM return;
374210696SDavid.Hollister@Sun.COM }
374310696SDavid.Hollister@Sun.COM
374410696SDavid.Hollister@Sun.COM /*
374510696SDavid.Hollister@Sun.COM * Step 4: if we're at the bottom of the stack, we're done
374610696SDavid.Hollister@Sun.COM * (we can't have any levels below us)
374710696SDavid.Hollister@Sun.COM */
374810696SDavid.Hollister@Sun.COM if (pptr->level == PMCS_MAX_XPND-1) {
374910696SDavid.Hollister@Sun.COM return;
375010696SDavid.Hollister@Sun.COM }
375110696SDavid.Hollister@Sun.COM
375210696SDavid.Hollister@Sun.COM /*
375310696SDavid.Hollister@Sun.COM * Step 5: Discover things about each phy in this expander. We do
375410696SDavid.Hollister@Sun.COM * this by walking the current list of contained phys and doing a
375510696SDavid.Hollister@Sun.COM * content discovery for it to a local phy.
375610696SDavid.Hollister@Sun.COM */
375710696SDavid.Hollister@Sun.COM ctmp = pptr->children;
375810696SDavid.Hollister@Sun.COM ASSERT(ctmp);
375910696SDavid.Hollister@Sun.COM if (ctmp == NULL) {
376011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
376110696SDavid.Hollister@Sun.COM "%s: No children attached to expander @ %s?", __func__,
376210696SDavid.Hollister@Sun.COM pptr->path);
376310696SDavid.Hollister@Sun.COM return;
376410696SDavid.Hollister@Sun.COM }
376510696SDavid.Hollister@Sun.COM
376610696SDavid.Hollister@Sun.COM while (ctmp) {
376710696SDavid.Hollister@Sun.COM /*
376810696SDavid.Hollister@Sun.COM * Allocate a local PHY to contain the proposed new contents
376910696SDavid.Hollister@Sun.COM * and link it to the rest of the local PHYs so that they
377010696SDavid.Hollister@Sun.COM * can all be freed later.
377110696SDavid.Hollister@Sun.COM */
377210696SDavid.Hollister@Sun.COM local = pmcs_clone_phy(ctmp);
377310696SDavid.Hollister@Sun.COM
377410696SDavid.Hollister@Sun.COM if (local_list == NULL) {
377510696SDavid.Hollister@Sun.COM local_list = local;
377610696SDavid.Hollister@Sun.COM local_tail = local;
377710696SDavid.Hollister@Sun.COM } else {
377810696SDavid.Hollister@Sun.COM local_tail->sibling = local;
377910696SDavid.Hollister@Sun.COM local_tail = local;
378010696SDavid.Hollister@Sun.COM }
378110696SDavid.Hollister@Sun.COM
378210696SDavid.Hollister@Sun.COM /*
378310696SDavid.Hollister@Sun.COM * Need to lock the local PHY since pmcs_expander_content_
378410696SDavid.Hollister@Sun.COM * discovery may call pmcs_clear_phy on it, which expects
378510696SDavid.Hollister@Sun.COM * the PHY to be locked.
378610696SDavid.Hollister@Sun.COM */
378710696SDavid.Hollister@Sun.COM pmcs_lock_phy(local);
378810696SDavid.Hollister@Sun.COM result = pmcs_expander_content_discover(pwp, pptr, local);
378910696SDavid.Hollister@Sun.COM pmcs_unlock_phy(local);
379010696SDavid.Hollister@Sun.COM if (result <= 0) {
379110696SDavid.Hollister@Sun.COM if (ddi_get_lbolt() < pptr->config_stop) {
379210696SDavid.Hollister@Sun.COM PHY_CHANGED(pwp, pptr);
379310696SDavid.Hollister@Sun.COM RESTART_DISCOVERY(pwp);
379410696SDavid.Hollister@Sun.COM } else {
379510696SDavid.Hollister@Sun.COM pptr->config_stop = 0;
379611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
379710696SDavid.Hollister@Sun.COM "%s: Retries exhausted for %s, killing",
379810696SDavid.Hollister@Sun.COM __func__, pptr->path);
379910696SDavid.Hollister@Sun.COM pmcs_kill_changed(pwp, pptr, 0);
380010696SDavid.Hollister@Sun.COM }
380110696SDavid.Hollister@Sun.COM
380210696SDavid.Hollister@Sun.COM /*
380310696SDavid.Hollister@Sun.COM * Release all the local PHYs that we allocated.
380410696SDavid.Hollister@Sun.COM */
380510696SDavid.Hollister@Sun.COM pmcs_free_phys(pwp, local_list);
380610696SDavid.Hollister@Sun.COM return;
380710696SDavid.Hollister@Sun.COM }
380810696SDavid.Hollister@Sun.COM
380910696SDavid.Hollister@Sun.COM ctmp = ctmp->sibling;
381010696SDavid.Hollister@Sun.COM }
381110696SDavid.Hollister@Sun.COM
381210696SDavid.Hollister@Sun.COM /*
381310696SDavid.Hollister@Sun.COM * Step 6: Compare the local PHY's contents to our current PHY. If
381410696SDavid.Hollister@Sun.COM * there are changes, take the appropriate action.
381510696SDavid.Hollister@Sun.COM * This is done in two steps (step 5 above, and 6 here) so that if we
381610696SDavid.Hollister@Sun.COM * have to bail during this process (e.g. pmcs_expander_content_discover
381710696SDavid.Hollister@Sun.COM * fails), we haven't actually changed the state of any of the real
381810696SDavid.Hollister@Sun.COM * PHYs. Next time we come through here, we'll be starting over from
381910696SDavid.Hollister@Sun.COM * scratch. This keeps us from marking a changed PHY as no longer
382010696SDavid.Hollister@Sun.COM * changed, but then having to bail only to come back next time and
382110696SDavid.Hollister@Sun.COM * think that the PHY hadn't changed. If this were to happen, we
382210696SDavid.Hollister@Sun.COM * would fail to properly configure the device behind this PHY.
382310696SDavid.Hollister@Sun.COM */
382410696SDavid.Hollister@Sun.COM local = local_list;
382510696SDavid.Hollister@Sun.COM ctmp = pptr->children;
382610696SDavid.Hollister@Sun.COM
382710696SDavid.Hollister@Sun.COM while (ctmp) {
382810696SDavid.Hollister@Sun.COM changed = B_FALSE;
382910696SDavid.Hollister@Sun.COM kill_changed = B_FALSE;
383010696SDavid.Hollister@Sun.COM
383110696SDavid.Hollister@Sun.COM /*
383210696SDavid.Hollister@Sun.COM * We set local to local_list prior to this loop so that we
383310696SDavid.Hollister@Sun.COM * can simply walk the local_list while we walk this list. The
383410696SDavid.Hollister@Sun.COM * two lists should be completely in sync.
383510696SDavid.Hollister@Sun.COM *
383610696SDavid.Hollister@Sun.COM * Clear the changed flag here.
383710696SDavid.Hollister@Sun.COM */
383810696SDavid.Hollister@Sun.COM ctmp->changed = 0;
383910696SDavid.Hollister@Sun.COM
384010696SDavid.Hollister@Sun.COM if (ctmp->dtype != local->dtype) {
384110696SDavid.Hollister@Sun.COM if (ctmp->dtype != NOTHING) {
384211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
384311048SDavid.Hollister@Sun.COM "%s: %s type changed from %s to %s "
384411048SDavid.Hollister@Sun.COM "(killing)", __func__, ctmp->path,
384511048SDavid.Hollister@Sun.COM PHY_TYPE(ctmp), PHY_TYPE(local));
384610696SDavid.Hollister@Sun.COM /*
384710696SDavid.Hollister@Sun.COM * Force a rescan of this expander after dead
384810696SDavid.Hollister@Sun.COM * contents are cleared and removed.
384910696SDavid.Hollister@Sun.COM */
385010696SDavid.Hollister@Sun.COM changed = B_TRUE;
385110696SDavid.Hollister@Sun.COM kill_changed = B_TRUE;
385210696SDavid.Hollister@Sun.COM } else {
385310696SDavid.Hollister@Sun.COM changed = B_TRUE;
385411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
385510696SDavid.Hollister@Sun.COM "%s: %s type changed from NOTHING to %s",
385610696SDavid.Hollister@Sun.COM __func__, ctmp->path, PHY_TYPE(local));
385711501SDavid.Hollister@Sun.COM /*
385811501SDavid.Hollister@Sun.COM * Since this PHY was nothing and is now
385911501SDavid.Hollister@Sun.COM * something, reset the config_stop timer.
386011501SDavid.Hollister@Sun.COM */
386111501SDavid.Hollister@Sun.COM ctmp->config_stop = ddi_get_lbolt() +
386211501SDavid.Hollister@Sun.COM drv_usectohz(PMCS_MAX_CONFIG_TIME);
386310696SDavid.Hollister@Sun.COM }
386410696SDavid.Hollister@Sun.COM
386510696SDavid.Hollister@Sun.COM } else if (ctmp->atdt != local->atdt) {
386611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL, "%s: "
386711048SDavid.Hollister@Sun.COM "%s attached device type changed from %d to %d "
386811048SDavid.Hollister@Sun.COM "(killing)", __func__, ctmp->path, ctmp->atdt,
386911048SDavid.Hollister@Sun.COM local->atdt);
387010696SDavid.Hollister@Sun.COM /*
387110696SDavid.Hollister@Sun.COM * Force a rescan of this expander after dead
387210696SDavid.Hollister@Sun.COM * contents are cleared and removed.
387310696SDavid.Hollister@Sun.COM */
387410696SDavid.Hollister@Sun.COM changed = B_TRUE;
387510696SDavid.Hollister@Sun.COM
387610696SDavid.Hollister@Sun.COM if (local->atdt == 0) {
387710696SDavid.Hollister@Sun.COM kill_changed = B_TRUE;
387810696SDavid.Hollister@Sun.COM }
387910696SDavid.Hollister@Sun.COM } else if (ctmp->link_rate != local->link_rate) {
388011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, ctmp, NULL, "%s: %s "
388111048SDavid.Hollister@Sun.COM "changed speed from %s to %s", __func__, ctmp->path,
388210696SDavid.Hollister@Sun.COM pmcs_get_rate(ctmp->link_rate),
388310696SDavid.Hollister@Sun.COM pmcs_get_rate(local->link_rate));
388410696SDavid.Hollister@Sun.COM /* If the speed changed from invalid, force rescan */
388510696SDavid.Hollister@Sun.COM if (!PMCS_VALID_LINK_RATE(ctmp->link_rate)) {
388610696SDavid.Hollister@Sun.COM changed = B_TRUE;
388710696SDavid.Hollister@Sun.COM RESTART_DISCOVERY(pwp);
388810696SDavid.Hollister@Sun.COM } else {
388910696SDavid.Hollister@Sun.COM /* Just update to the new link rate */
389010696SDavid.Hollister@Sun.COM ctmp->link_rate = local->link_rate;
389110696SDavid.Hollister@Sun.COM }
389210696SDavid.Hollister@Sun.COM
389310696SDavid.Hollister@Sun.COM if (!PMCS_VALID_LINK_RATE(local->link_rate)) {
389410696SDavid.Hollister@Sun.COM kill_changed = B_TRUE;
389510696SDavid.Hollister@Sun.COM }
389610696SDavid.Hollister@Sun.COM } else if (memcmp(ctmp->sas_address, local->sas_address,
389710696SDavid.Hollister@Sun.COM sizeof (ctmp->sas_address)) != 0) {
389811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
389911048SDavid.Hollister@Sun.COM "%s: SAS Addr for %s changed from " SAS_ADDR_FMT
390011048SDavid.Hollister@Sun.COM "to " SAS_ADDR_FMT " (kill old tree)", __func__,
390110696SDavid.Hollister@Sun.COM ctmp->path, SAS_ADDR_PRT(ctmp->sas_address),
390210696SDavid.Hollister@Sun.COM SAS_ADDR_PRT(local->sas_address));
390310696SDavid.Hollister@Sun.COM /*
390410696SDavid.Hollister@Sun.COM * Force a rescan of this expander after dead
390510696SDavid.Hollister@Sun.COM * contents are cleared and removed.
390610696SDavid.Hollister@Sun.COM */
390710696SDavid.Hollister@Sun.COM changed = B_TRUE;
390810696SDavid.Hollister@Sun.COM } else {
390911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
391010696SDavid.Hollister@Sun.COM "%s: %s looks the same (type %s)",
391110696SDavid.Hollister@Sun.COM __func__, ctmp->path, PHY_TYPE(ctmp));
391210696SDavid.Hollister@Sun.COM /*
391310696SDavid.Hollister@Sun.COM * If EXPANDER, still mark it changed so we
391410696SDavid.Hollister@Sun.COM * re-evaluate its contents. If it's not an expander,
391510696SDavid.Hollister@Sun.COM * but it hasn't been configured, also mark it as
391610696SDavid.Hollister@Sun.COM * changed so that it will undergo configuration.
391710696SDavid.Hollister@Sun.COM */
391810696SDavid.Hollister@Sun.COM if (ctmp->dtype == EXPANDER) {
391910696SDavid.Hollister@Sun.COM changed = B_TRUE;
392010696SDavid.Hollister@Sun.COM } else if ((ctmp->dtype != NOTHING) &&
392110696SDavid.Hollister@Sun.COM !ctmp->configured) {
392210696SDavid.Hollister@Sun.COM ctmp->changed = 1;
392310696SDavid.Hollister@Sun.COM } else {
392410696SDavid.Hollister@Sun.COM /* It simply hasn't changed */
392510696SDavid.Hollister@Sun.COM ctmp->changed = 0;
392610696SDavid.Hollister@Sun.COM }
392710696SDavid.Hollister@Sun.COM }
392810696SDavid.Hollister@Sun.COM
392910696SDavid.Hollister@Sun.COM /*
393010696SDavid.Hollister@Sun.COM * If the PHY changed, call pmcs_kill_changed if indicated,
393110696SDavid.Hollister@Sun.COM * update its contents to reflect its current state and mark it
393210696SDavid.Hollister@Sun.COM * as changed.
393310696SDavid.Hollister@Sun.COM */
393410696SDavid.Hollister@Sun.COM if (changed) {
393510696SDavid.Hollister@Sun.COM /*
393610696SDavid.Hollister@Sun.COM * pmcs_kill_changed will mark the PHY as changed, so
393710696SDavid.Hollister@Sun.COM * only do PHY_CHANGED if we did not do kill_changed.
393810696SDavid.Hollister@Sun.COM */
393910696SDavid.Hollister@Sun.COM if (kill_changed) {
394010696SDavid.Hollister@Sun.COM pmcs_kill_changed(pwp, ctmp, 0);
394110696SDavid.Hollister@Sun.COM } else {
394210696SDavid.Hollister@Sun.COM /*
394310696SDavid.Hollister@Sun.COM * If we're not killing the device, it's not
394410696SDavid.Hollister@Sun.COM * dead. Mark the PHY as changed.
394510696SDavid.Hollister@Sun.COM */
394610696SDavid.Hollister@Sun.COM PHY_CHANGED(pwp, ctmp);
394710696SDavid.Hollister@Sun.COM
394810696SDavid.Hollister@Sun.COM if (ctmp->dead) {
394910696SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
395011048SDavid.Hollister@Sun.COM ctmp, NULL, "%s: Unmarking PHY %s "
395111048SDavid.Hollister@Sun.COM "dead, restarting discovery",
395210696SDavid.Hollister@Sun.COM __func__, ctmp->path);
395310696SDavid.Hollister@Sun.COM ctmp->dead = 0;
395410696SDavid.Hollister@Sun.COM RESTART_DISCOVERY(pwp);
395510696SDavid.Hollister@Sun.COM }
395610696SDavid.Hollister@Sun.COM }
395710696SDavid.Hollister@Sun.COM
395810696SDavid.Hollister@Sun.COM /*
395910696SDavid.Hollister@Sun.COM * If the dtype of this PHY is now NOTHING, mark it as
396010696SDavid.Hollister@Sun.COM * unconfigured. Set pend_dtype to what the new dtype
396110696SDavid.Hollister@Sun.COM * is. It'll get updated at the end of the discovery
396210696SDavid.Hollister@Sun.COM * process.
396310696SDavid.Hollister@Sun.COM */
396410696SDavid.Hollister@Sun.COM if (local->dtype == NOTHING) {
396510696SDavid.Hollister@Sun.COM bzero(ctmp->sas_address,
396610696SDavid.Hollister@Sun.COM sizeof (local->sas_address));
396710696SDavid.Hollister@Sun.COM ctmp->atdt = 0;
396810696SDavid.Hollister@Sun.COM ctmp->link_rate = 0;
396910696SDavid.Hollister@Sun.COM ctmp->pend_dtype = NOTHING;
397010696SDavid.Hollister@Sun.COM ctmp->configured = 0;
397110696SDavid.Hollister@Sun.COM } else {
397210696SDavid.Hollister@Sun.COM (void) memcpy(ctmp->sas_address,
397310696SDavid.Hollister@Sun.COM local->sas_address,
397410696SDavid.Hollister@Sun.COM sizeof (local->sas_address));
397510696SDavid.Hollister@Sun.COM ctmp->atdt = local->atdt;
397610696SDavid.Hollister@Sun.COM ctmp->link_rate = local->link_rate;
397710696SDavid.Hollister@Sun.COM ctmp->pend_dtype = local->dtype;
397813033Ssrikanth.suravajhala@oracle.com ctmp->att_port_pm_tmp = local->att_port_pm_tmp;
397913033Ssrikanth.suravajhala@oracle.com ctmp->tgt_port_pm_tmp = local->tgt_port_pm_tmp;
398010696SDavid.Hollister@Sun.COM }
398110696SDavid.Hollister@Sun.COM }
398210696SDavid.Hollister@Sun.COM
398310696SDavid.Hollister@Sun.COM local = local->sibling;
398410696SDavid.Hollister@Sun.COM ctmp = ctmp->sibling;
398510696SDavid.Hollister@Sun.COM }
398610696SDavid.Hollister@Sun.COM
398710696SDavid.Hollister@Sun.COM /*
398810696SDavid.Hollister@Sun.COM * If we got to here, that means we were able to see all the PHYs
398910696SDavid.Hollister@Sun.COM * and we can now update all of the real PHYs with the information
399010696SDavid.Hollister@Sun.COM * we got on the local PHYs. Once that's done, free all the local
399110696SDavid.Hollister@Sun.COM * PHYs.
399210696SDavid.Hollister@Sun.COM */
399310696SDavid.Hollister@Sun.COM
399410696SDavid.Hollister@Sun.COM pmcs_free_phys(pwp, local_list);
399510696SDavid.Hollister@Sun.COM }
399610696SDavid.Hollister@Sun.COM
399710696SDavid.Hollister@Sun.COM /*
399810696SDavid.Hollister@Sun.COM * Top level routine to check expanders. We call pmcs_check_expander for
399910696SDavid.Hollister@Sun.COM * each expander. Since we're not doing any configuration right now, it
400010696SDavid.Hollister@Sun.COM * doesn't matter if this is breadth-first.
400110696SDavid.Hollister@Sun.COM */
400211501SDavid.Hollister@Sun.COM static void
pmcs_check_expanders(pmcs_hw_t * pwp,pmcs_phy_t * pptr)400310696SDavid.Hollister@Sun.COM pmcs_check_expanders(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
400410696SDavid.Hollister@Sun.COM {
400510696SDavid.Hollister@Sun.COM pmcs_phy_t *phyp, *pnext, *pchild;
400610696SDavid.Hollister@Sun.COM
400711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
400811048SDavid.Hollister@Sun.COM "%s: %s", __func__, pptr->path);
400910696SDavid.Hollister@Sun.COM
401010696SDavid.Hollister@Sun.COM /*
401110696SDavid.Hollister@Sun.COM * Check each expander at this level
401210696SDavid.Hollister@Sun.COM */
401310696SDavid.Hollister@Sun.COM phyp = pptr;
401411501SDavid.Hollister@Sun.COM while (phyp) {
401510696SDavid.Hollister@Sun.COM pmcs_lock_phy(phyp);
401610696SDavid.Hollister@Sun.COM
401710696SDavid.Hollister@Sun.COM if ((phyp->dtype == EXPANDER) && phyp->changed &&
401810696SDavid.Hollister@Sun.COM !phyp->dead && !phyp->subsidiary &&
401910696SDavid.Hollister@Sun.COM phyp->configured) {
402010696SDavid.Hollister@Sun.COM pmcs_check_expander(pwp, phyp);
402110696SDavid.Hollister@Sun.COM }
402210696SDavid.Hollister@Sun.COM
402310696SDavid.Hollister@Sun.COM pnext = phyp->sibling;
402410696SDavid.Hollister@Sun.COM pmcs_unlock_phy(phyp);
402510696SDavid.Hollister@Sun.COM phyp = pnext;
402610696SDavid.Hollister@Sun.COM }
402710696SDavid.Hollister@Sun.COM
402810696SDavid.Hollister@Sun.COM /*
402910696SDavid.Hollister@Sun.COM * Now check the children
403010696SDavid.Hollister@Sun.COM */
403110696SDavid.Hollister@Sun.COM phyp = pptr;
403211501SDavid.Hollister@Sun.COM while (phyp) {
403310696SDavid.Hollister@Sun.COM pmcs_lock_phy(phyp);
403410696SDavid.Hollister@Sun.COM pnext = phyp->sibling;
403510696SDavid.Hollister@Sun.COM pchild = phyp->children;
403610696SDavid.Hollister@Sun.COM pmcs_unlock_phy(phyp);
403710696SDavid.Hollister@Sun.COM
403810696SDavid.Hollister@Sun.COM if (pchild) {
403911501SDavid.Hollister@Sun.COM pmcs_check_expanders(pwp, pchild);
404011501SDavid.Hollister@Sun.COM }
404110696SDavid.Hollister@Sun.COM
404210696SDavid.Hollister@Sun.COM phyp = pnext;
404310696SDavid.Hollister@Sun.COM }
404410696SDavid.Hollister@Sun.COM }
404510696SDavid.Hollister@Sun.COM
404610696SDavid.Hollister@Sun.COM /*
404710696SDavid.Hollister@Sun.COM * Called with softstate and PHY locked
404810696SDavid.Hollister@Sun.COM */
404910696SDavid.Hollister@Sun.COM static void
pmcs_clear_expander(pmcs_hw_t * pwp,pmcs_phy_t * pptr,int level)405010696SDavid.Hollister@Sun.COM pmcs_clear_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr, int level)
405110696SDavid.Hollister@Sun.COM {
405210696SDavid.Hollister@Sun.COM pmcs_phy_t *ctmp;
405310696SDavid.Hollister@Sun.COM
405410696SDavid.Hollister@Sun.COM ASSERT(mutex_owned(&pwp->lock));
405510696SDavid.Hollister@Sun.COM ASSERT(mutex_owned(&pptr->phy_lock));
405610696SDavid.Hollister@Sun.COM ASSERT(pptr->level < PMCS_MAX_XPND - 1);
405710696SDavid.Hollister@Sun.COM
405811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
405911048SDavid.Hollister@Sun.COM "%s: checking %s", __func__, pptr->path);
406010696SDavid.Hollister@Sun.COM
406110696SDavid.Hollister@Sun.COM ctmp = pptr->children;
406210696SDavid.Hollister@Sun.COM while (ctmp) {
406310696SDavid.Hollister@Sun.COM /*
406410696SDavid.Hollister@Sun.COM * If the expander is dead, mark its children dead
406510696SDavid.Hollister@Sun.COM */
406610696SDavid.Hollister@Sun.COM if (pptr->dead) {
406710696SDavid.Hollister@Sun.COM ctmp->dead = 1;
406810696SDavid.Hollister@Sun.COM }
406910696SDavid.Hollister@Sun.COM if (ctmp->dtype == EXPANDER) {
407010696SDavid.Hollister@Sun.COM pmcs_clear_expander(pwp, ctmp, level + 1);
407110696SDavid.Hollister@Sun.COM }
407210696SDavid.Hollister@Sun.COM ctmp = ctmp->sibling;
407310696SDavid.Hollister@Sun.COM }
407410696SDavid.Hollister@Sun.COM
407510696SDavid.Hollister@Sun.COM /*
407610696SDavid.Hollister@Sun.COM * If this expander is not dead, we're done here.
407710696SDavid.Hollister@Sun.COM */
407810696SDavid.Hollister@Sun.COM if (!pptr->dead) {
407910696SDavid.Hollister@Sun.COM return;
408010696SDavid.Hollister@Sun.COM }
408110696SDavid.Hollister@Sun.COM
408210696SDavid.Hollister@Sun.COM /*
408310696SDavid.Hollister@Sun.COM * Now snip out the list of children below us and release them
408410696SDavid.Hollister@Sun.COM */
408511601SDavid.Hollister@Sun.COM if (pptr->children) {
408611601SDavid.Hollister@Sun.COM pmcs_add_dead_phys(pwp, pptr->children);
408710696SDavid.Hollister@Sun.COM }
408810696SDavid.Hollister@Sun.COM
408910696SDavid.Hollister@Sun.COM pptr->children = NULL;
409010696SDavid.Hollister@Sun.COM
409110696SDavid.Hollister@Sun.COM /*
409210696SDavid.Hollister@Sun.COM * Clear subsidiary phys as well. Getting the parent's PHY lock
409310696SDavid.Hollister@Sun.COM * is only necessary if level == 0 since otherwise the parent is
409410696SDavid.Hollister@Sun.COM * already locked.
409510696SDavid.Hollister@Sun.COM */
409610696SDavid.Hollister@Sun.COM if (!IS_ROOT_PHY(pptr)) {
409710696SDavid.Hollister@Sun.COM if (level == 0) {
409810696SDavid.Hollister@Sun.COM mutex_enter(&pptr->parent->phy_lock);
409910696SDavid.Hollister@Sun.COM }
410010696SDavid.Hollister@Sun.COM ctmp = pptr->parent->children;
410110696SDavid.Hollister@Sun.COM if (level == 0) {
410210696SDavid.Hollister@Sun.COM mutex_exit(&pptr->parent->phy_lock);
410310696SDavid.Hollister@Sun.COM }
410410696SDavid.Hollister@Sun.COM } else {
410510696SDavid.Hollister@Sun.COM ctmp = pwp->root_phys;
410610696SDavid.Hollister@Sun.COM }
410710696SDavid.Hollister@Sun.COM
410810696SDavid.Hollister@Sun.COM while (ctmp) {
410910696SDavid.Hollister@Sun.COM if (ctmp == pptr) {
411010696SDavid.Hollister@Sun.COM ctmp = ctmp->sibling;
411110696SDavid.Hollister@Sun.COM continue;
411210696SDavid.Hollister@Sun.COM }
411310696SDavid.Hollister@Sun.COM /*
411410696SDavid.Hollister@Sun.COM * We only need to lock subsidiary PHYs on the level 0
411510696SDavid.Hollister@Sun.COM * expander. Any children of that expander, subsidiaries or
411610696SDavid.Hollister@Sun.COM * not, will already be locked.
411710696SDavid.Hollister@Sun.COM */
411810696SDavid.Hollister@Sun.COM if (level == 0) {
411910696SDavid.Hollister@Sun.COM pmcs_lock_phy(ctmp);
412010696SDavid.Hollister@Sun.COM }
412110696SDavid.Hollister@Sun.COM if (ctmp->dtype != EXPANDER || ctmp->subsidiary == 0 ||
412210696SDavid.Hollister@Sun.COM memcmp(ctmp->sas_address, pptr->sas_address,
412310696SDavid.Hollister@Sun.COM sizeof (ctmp->sas_address)) != 0) {
412410696SDavid.Hollister@Sun.COM if (level == 0) {
412510696SDavid.Hollister@Sun.COM pmcs_unlock_phy(ctmp);
412610696SDavid.Hollister@Sun.COM }
412710696SDavid.Hollister@Sun.COM ctmp = ctmp->sibling;
412810696SDavid.Hollister@Sun.COM continue;
412910696SDavid.Hollister@Sun.COM }
413011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
413111048SDavid.Hollister@Sun.COM "%s: subsidiary %s", __func__, ctmp->path);
413210696SDavid.Hollister@Sun.COM pmcs_clear_phy(pwp, ctmp);
413310696SDavid.Hollister@Sun.COM if (level == 0) {
413410696SDavid.Hollister@Sun.COM pmcs_unlock_phy(ctmp);
413510696SDavid.Hollister@Sun.COM }
413610696SDavid.Hollister@Sun.COM ctmp = ctmp->sibling;
413710696SDavid.Hollister@Sun.COM }
413810696SDavid.Hollister@Sun.COM
413910696SDavid.Hollister@Sun.COM pmcs_clear_phy(pwp, pptr);
414010696SDavid.Hollister@Sun.COM }
414110696SDavid.Hollister@Sun.COM
414210696SDavid.Hollister@Sun.COM /*
414310696SDavid.Hollister@Sun.COM * Called with PHY locked and with scratch acquired. We return 0 if
414410696SDavid.Hollister@Sun.COM * we fail to allocate resources or notice that the configuration
414510696SDavid.Hollister@Sun.COM * count changed while we were running the command. We return
414610696SDavid.Hollister@Sun.COM * less than zero if we had an I/O error or received an unsupported
414710696SDavid.Hollister@Sun.COM * configuration. Otherwise we return the number of phys in the
414810696SDavid.Hollister@Sun.COM * expander.
414910696SDavid.Hollister@Sun.COM */
415010696SDavid.Hollister@Sun.COM #define DFM(m, y) if (m == NULL) m = y
415110696SDavid.Hollister@Sun.COM static int
pmcs_expander_get_nphy(pmcs_hw_t * pwp,pmcs_phy_t * pptr)415210696SDavid.Hollister@Sun.COM pmcs_expander_get_nphy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
415310696SDavid.Hollister@Sun.COM {
415410696SDavid.Hollister@Sun.COM struct pmcwork *pwrk;
415512462Sjesse.butler@oracle.com pmcs_iport_t *iport;
415610696SDavid.Hollister@Sun.COM char buf[64];
415710696SDavid.Hollister@Sun.COM const uint_t rdoff = 0x100; /* returned data offset */
415810696SDavid.Hollister@Sun.COM smp_response_frame_t *srf;
415910696SDavid.Hollister@Sun.COM smp_report_general_resp_t *srgr;
416010696SDavid.Hollister@Sun.COM uint32_t msg[PMCS_MSG_SIZE], *ptr, htag, status, ival;
416111601SDavid.Hollister@Sun.COM int result = 0;
416210696SDavid.Hollister@Sun.COM
416310696SDavid.Hollister@Sun.COM ival = 0x40001100;
416411601SDavid.Hollister@Sun.COM
416510696SDavid.Hollister@Sun.COM again:
416611601SDavid.Hollister@Sun.COM if (!pptr->iport || !pptr->valid_device_id) {
416711601SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
416811601SDavid.Hollister@Sun.COM "%s: Can't reach PHY %s", __func__, pptr->path);
416911601SDavid.Hollister@Sun.COM goto out;
417011601SDavid.Hollister@Sun.COM }
417111601SDavid.Hollister@Sun.COM
417210696SDavid.Hollister@Sun.COM pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
417310696SDavid.Hollister@Sun.COM if (pwrk == NULL) {
417410696SDavid.Hollister@Sun.COM goto out;
417510696SDavid.Hollister@Sun.COM }
417610696SDavid.Hollister@Sun.COM (void) memset(pwp->scratch, 0x77, PMCS_SCRATCH_SIZE);
417710696SDavid.Hollister@Sun.COM pwrk->arg = pwp->scratch;
417810696SDavid.Hollister@Sun.COM pwrk->dtype = pptr->dtype;
417912462Sjesse.butler@oracle.com pwrk->xp = pptr->target;
418012258Ssrikanth.suravajhala@oracle.com pwrk->htag |= PMCS_TAG_NONIO_CMD;
418110696SDavid.Hollister@Sun.COM mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
418210696SDavid.Hollister@Sun.COM ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
418310696SDavid.Hollister@Sun.COM if (ptr == NULL) {
418410696SDavid.Hollister@Sun.COM mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
418511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, NULL,
418611048SDavid.Hollister@Sun.COM "%s: GET_IQ_ENTRY failed", __func__);
418710696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
418810696SDavid.Hollister@Sun.COM goto out;
418910696SDavid.Hollister@Sun.COM }
419010696SDavid.Hollister@Sun.COM
419110696SDavid.Hollister@Sun.COM msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST));
419210696SDavid.Hollister@Sun.COM msg[1] = LE_32(pwrk->htag);
419310696SDavid.Hollister@Sun.COM msg[2] = LE_32(pptr->device_id);
419410696SDavid.Hollister@Sun.COM msg[3] = LE_32((4 << SMP_REQUEST_LENGTH_SHIFT) | SMP_INDIRECT_RESPONSE);
419510696SDavid.Hollister@Sun.COM /*
419610696SDavid.Hollister@Sun.COM * Send SMP REPORT GENERAL (of either SAS1.1 or SAS2 flavors).
419710696SDavid.Hollister@Sun.COM */
419810696SDavid.Hollister@Sun.COM msg[4] = BE_32(ival);
419910696SDavid.Hollister@Sun.COM msg[5] = 0;
420010696SDavid.Hollister@Sun.COM msg[6] = 0;
420110696SDavid.Hollister@Sun.COM msg[7] = 0;
420210696SDavid.Hollister@Sun.COM msg[8] = 0;
420310696SDavid.Hollister@Sun.COM msg[9] = 0;
420410696SDavid.Hollister@Sun.COM msg[10] = 0;
420510696SDavid.Hollister@Sun.COM msg[11] = 0;
420610696SDavid.Hollister@Sun.COM msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff));
420710696SDavid.Hollister@Sun.COM msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff));
420810696SDavid.Hollister@Sun.COM msg[14] = LE_32(PMCS_SCRATCH_SIZE - rdoff);
420910696SDavid.Hollister@Sun.COM msg[15] = 0;
421010696SDavid.Hollister@Sun.COM
421110696SDavid.Hollister@Sun.COM COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
421211267SJesse.Butler@Sun.COM
421312462Sjesse.butler@oracle.com pmcs_hold_iport(pptr->iport);
421412462Sjesse.butler@oracle.com iport = pptr->iport;
421512462Sjesse.butler@oracle.com pmcs_smp_acquire(iport);
421610696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_ONCHIP;
421710696SDavid.Hollister@Sun.COM htag = pwrk->htag;
421810696SDavid.Hollister@Sun.COM INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
421910696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
422010696SDavid.Hollister@Sun.COM WAIT_FOR(pwrk, 1000, result);
422112462Sjesse.butler@oracle.com pmcs_pwork(pwp, pwrk);
422213090Sjesse.butler@oracle.com pmcs_smp_release(iport);
422313090Sjesse.butler@oracle.com pmcs_rele_iport(iport);
422412862Sjesse.butler@oracle.com pmcs_lock_phy(pptr);
422512862Sjesse.butler@oracle.com if (result) {
422612862Sjesse.butler@oracle.com pmcs_timed_out(pwp, htag, __func__);
422712862Sjesse.butler@oracle.com pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
422812862Sjesse.butler@oracle.com "%s: Issuing SMP ABORT for htag 0x%08x", __func__, htag);
422912862Sjesse.butler@oracle.com if (pmcs_abort(pwp, pptr, htag, 0, 1)) {
423012862Sjesse.butler@oracle.com pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
423112862Sjesse.butler@oracle.com "%s: SMP ABORT failed for cmd (htag 0x%08x)",
423212862Sjesse.butler@oracle.com __func__, htag);
423312862Sjesse.butler@oracle.com }
423412862Sjesse.butler@oracle.com result = 0;
423512862Sjesse.butler@oracle.com goto out;
423612862Sjesse.butler@oracle.com }
423710696SDavid.Hollister@Sun.COM
423810696SDavid.Hollister@Sun.COM mutex_enter(&pwp->config_lock);
423910696SDavid.Hollister@Sun.COM if (pwp->config_changed) {
424010696SDavid.Hollister@Sun.COM RESTART_DISCOVERY_LOCKED(pwp);
424110696SDavid.Hollister@Sun.COM mutex_exit(&pwp->config_lock);
424210696SDavid.Hollister@Sun.COM result = 0;
424310696SDavid.Hollister@Sun.COM goto out;
424410696SDavid.Hollister@Sun.COM }
424510696SDavid.Hollister@Sun.COM mutex_exit(&pwp->config_lock);
424610696SDavid.Hollister@Sun.COM
424710696SDavid.Hollister@Sun.COM ptr = (void *)pwp->scratch;
424810696SDavid.Hollister@Sun.COM status = LE_32(ptr[2]);
424910696SDavid.Hollister@Sun.COM if (status == PMCOUT_STATUS_UNDERFLOW ||
425010696SDavid.Hollister@Sun.COM status == PMCOUT_STATUS_OVERFLOW) {
425111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, pptr, NULL,
425210696SDavid.Hollister@Sun.COM "%s: over/underflow", __func__);
425310696SDavid.Hollister@Sun.COM status = PMCOUT_STATUS_OK;
425410696SDavid.Hollister@Sun.COM }
425510696SDavid.Hollister@Sun.COM srf = (smp_response_frame_t *)&((uint32_t *)pwp->scratch)[rdoff >> 2];
425610696SDavid.Hollister@Sun.COM srgr = (smp_report_general_resp_t *)
425710696SDavid.Hollister@Sun.COM &((uint32_t *)pwp->scratch)[(rdoff >> 2)+1];
425810696SDavid.Hollister@Sun.COM
425910696SDavid.Hollister@Sun.COM if (status != PMCOUT_STATUS_OK) {
426010696SDavid.Hollister@Sun.COM char *nag = NULL;
426110696SDavid.Hollister@Sun.COM (void) snprintf(buf, sizeof (buf),
426210696SDavid.Hollister@Sun.COM "%s: SMP op failed (0x%x)", __func__, status);
426310696SDavid.Hollister@Sun.COM switch (status) {
426410696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_PORT_IN_RESET:
426510696SDavid.Hollister@Sun.COM DFM(nag, "I/O Port In Reset");
426610696SDavid.Hollister@Sun.COM /* FALLTHROUGH */
426710696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_ERROR_HW_TIMEOUT:
426810696SDavid.Hollister@Sun.COM DFM(nag, "Hardware Timeout");
426910696SDavid.Hollister@Sun.COM /* FALLTHROUGH */
427010696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE:
427110696SDavid.Hollister@Sun.COM DFM(nag, "Internal SMP Resource Failure");
427210696SDavid.Hollister@Sun.COM /* FALLTHROUGH */
427310696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
427410696SDavid.Hollister@Sun.COM DFM(nag, "PHY Not Ready");
427510696SDavid.Hollister@Sun.COM /* FALLTHROUGH */
427610696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
427710696SDavid.Hollister@Sun.COM DFM(nag, "Connection Rate Not Supported");
427810696SDavid.Hollister@Sun.COM /* FALLTHROUGH */
427910696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
428010696SDavid.Hollister@Sun.COM DFM(nag, "Open Retry Timeout");
428110696SDavid.Hollister@Sun.COM /* FALLTHROUGH */
428211693SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
428311693SDavid.Hollister@Sun.COM DFM(nag, "HW Resource Busy");
428411693SDavid.Hollister@Sun.COM /* FALLTHROUGH */
428510696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR:
428610696SDavid.Hollister@Sun.COM DFM(nag, "Response Connection Error");
428711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
428810696SDavid.Hollister@Sun.COM "%s: expander %s SMP operation failed (%s)",
428910696SDavid.Hollister@Sun.COM __func__, pptr->path, nag);
429010696SDavid.Hollister@Sun.COM break;
429110696SDavid.Hollister@Sun.COM
429210696SDavid.Hollister@Sun.COM /*
429310696SDavid.Hollister@Sun.COM * For the IO_DS_NON_OPERATIONAL case, we need to kick off
429410696SDavid.Hollister@Sun.COM * device state recovery and return 0 so that the caller
429510696SDavid.Hollister@Sun.COM * doesn't assume this expander is dead for good.
429610696SDavid.Hollister@Sun.COM */
429710696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL: {
429810696SDavid.Hollister@Sun.COM pmcs_xscsi_t *xp = pptr->target;
429910696SDavid.Hollister@Sun.COM
430011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, pptr, xp,
430110696SDavid.Hollister@Sun.COM "%s: expander %s device state non-operational",
430210696SDavid.Hollister@Sun.COM __func__, pptr->path);
430310696SDavid.Hollister@Sun.COM
430410696SDavid.Hollister@Sun.COM if (xp == NULL) {
430511267SJesse.Butler@Sun.COM /*
430611267SJesse.Butler@Sun.COM * Kick off recovery right now.
430711267SJesse.Butler@Sun.COM */
430811267SJesse.Butler@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_DS_ERR_RECOVERY);
430911267SJesse.Butler@Sun.COM (void) ddi_taskq_dispatch(pwp->tq, pmcs_worker,
431011267SJesse.Butler@Sun.COM pwp, DDI_NOSLEEP);
431111267SJesse.Butler@Sun.COM } else {
431211267SJesse.Butler@Sun.COM mutex_enter(&xp->statlock);
431311267SJesse.Butler@Sun.COM pmcs_start_dev_state_recovery(xp, pptr);
431411267SJesse.Butler@Sun.COM mutex_exit(&xp->statlock);
431510696SDavid.Hollister@Sun.COM }
431610696SDavid.Hollister@Sun.COM
431710696SDavid.Hollister@Sun.COM break;
431810696SDavid.Hollister@Sun.COM }
431910696SDavid.Hollister@Sun.COM
432010696SDavid.Hollister@Sun.COM default:
432110696SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG, buf, ptr);
432210696SDavid.Hollister@Sun.COM result = -EIO;
432310696SDavid.Hollister@Sun.COM break;
432410696SDavid.Hollister@Sun.COM }
432510696SDavid.Hollister@Sun.COM } else if (srf->srf_frame_type != SMP_FRAME_TYPE_RESPONSE) {
432611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
432710696SDavid.Hollister@Sun.COM "%s: bad response frame type 0x%x",
432810696SDavid.Hollister@Sun.COM __func__, srf->srf_frame_type);
432910696SDavid.Hollister@Sun.COM result = -EINVAL;
433010696SDavid.Hollister@Sun.COM } else if (srf->srf_function != SMP_FUNC_REPORT_GENERAL) {
433111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
433211048SDavid.Hollister@Sun.COM "%s: bad response function 0x%x",
433310696SDavid.Hollister@Sun.COM __func__, srf->srf_function);
433410696SDavid.Hollister@Sun.COM result = -EINVAL;
433510696SDavid.Hollister@Sun.COM } else if (srf->srf_result != 0) {
433610696SDavid.Hollister@Sun.COM /*
433710696SDavid.Hollister@Sun.COM * Check to see if we have a value of 3 for failure and
433810696SDavid.Hollister@Sun.COM * whether we were using a SAS2.0 allocation length value
433910696SDavid.Hollister@Sun.COM * and retry without it.
434010696SDavid.Hollister@Sun.COM */
434110696SDavid.Hollister@Sun.COM if (srf->srf_result == 3 && (ival & 0xff00)) {
434210696SDavid.Hollister@Sun.COM ival &= ~0xff00;
434311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
434410696SDavid.Hollister@Sun.COM "%s: err 0x%x with SAS2 request- retry with SAS1",
434510696SDavid.Hollister@Sun.COM __func__, srf->srf_result);
434610696SDavid.Hollister@Sun.COM goto again;
434710696SDavid.Hollister@Sun.COM }
434811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
434911048SDavid.Hollister@Sun.COM "%s: bad response 0x%x", __func__, srf->srf_result);
435010696SDavid.Hollister@Sun.COM result = -EINVAL;
435110696SDavid.Hollister@Sun.COM } else if (srgr->srgr_configuring) {
435211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
435310696SDavid.Hollister@Sun.COM "%s: expander at phy %s is still configuring",
435410696SDavid.Hollister@Sun.COM __func__, pptr->path);
435510696SDavid.Hollister@Sun.COM result = 0;
435610696SDavid.Hollister@Sun.COM } else {
435710696SDavid.Hollister@Sun.COM result = srgr->srgr_number_of_phys;
435810696SDavid.Hollister@Sun.COM if (ival & 0xff00) {
435910696SDavid.Hollister@Sun.COM pptr->tolerates_sas2 = 1;
436010696SDavid.Hollister@Sun.COM }
436111601SDavid.Hollister@Sun.COM /*
436211601SDavid.Hollister@Sun.COM * Save off the REPORT_GENERAL response
436311601SDavid.Hollister@Sun.COM */
436411601SDavid.Hollister@Sun.COM bcopy(srgr, &pptr->rg_resp, sizeof (smp_report_general_resp_t));
436511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
436610696SDavid.Hollister@Sun.COM "%s has %d phys and %s SAS2", pptr->path, result,
436710696SDavid.Hollister@Sun.COM pptr->tolerates_sas2? "tolerates" : "does not tolerate");
436810696SDavid.Hollister@Sun.COM }
436910696SDavid.Hollister@Sun.COM out:
437010696SDavid.Hollister@Sun.COM return (result);
437110696SDavid.Hollister@Sun.COM }
437210696SDavid.Hollister@Sun.COM
437310696SDavid.Hollister@Sun.COM /*
437410696SDavid.Hollister@Sun.COM * Called with expander locked (and thus, pptr) as well as all PHYs up to
437510696SDavid.Hollister@Sun.COM * the root, and scratch acquired. Return 0 if we fail to allocate resources
437610696SDavid.Hollister@Sun.COM * or notice that the configuration changed while we were running the command.
437710696SDavid.Hollister@Sun.COM *
437810696SDavid.Hollister@Sun.COM * We return less than zero if we had an I/O error or received an
437910696SDavid.Hollister@Sun.COM * unsupported configuration.
438010696SDavid.Hollister@Sun.COM */
438110696SDavid.Hollister@Sun.COM static int
pmcs_expander_content_discover(pmcs_hw_t * pwp,pmcs_phy_t * expander,pmcs_phy_t * pptr)438210696SDavid.Hollister@Sun.COM pmcs_expander_content_discover(pmcs_hw_t *pwp, pmcs_phy_t *expander,
438310696SDavid.Hollister@Sun.COM pmcs_phy_t *pptr)
438410696SDavid.Hollister@Sun.COM {
438510696SDavid.Hollister@Sun.COM struct pmcwork *pwrk;
438612462Sjesse.butler@oracle.com pmcs_iport_t *iport;
438710696SDavid.Hollister@Sun.COM char buf[64];
438810696SDavid.Hollister@Sun.COM uint8_t sas_address[8];
438910696SDavid.Hollister@Sun.COM uint8_t att_sas_address[8];
439010696SDavid.Hollister@Sun.COM smp_response_frame_t *srf;
439110696SDavid.Hollister@Sun.COM smp_discover_resp_t *sdr;
439210696SDavid.Hollister@Sun.COM const uint_t rdoff = 0x100; /* returned data offset */
439310696SDavid.Hollister@Sun.COM uint8_t *roff;
439410696SDavid.Hollister@Sun.COM uint32_t status, *ptr, msg[PMCS_MSG_SIZE], htag;
439511601SDavid.Hollister@Sun.COM int result = 0;
439610696SDavid.Hollister@Sun.COM uint8_t ini_support;
439710696SDavid.Hollister@Sun.COM uint8_t tgt_support;
439810696SDavid.Hollister@Sun.COM
439911601SDavid.Hollister@Sun.COM if (!expander->iport || !expander->valid_device_id) {
440011601SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, expander, expander->target,
440111601SDavid.Hollister@Sun.COM "%s: Can't reach PHY %s", __func__, expander->path);
440211601SDavid.Hollister@Sun.COM goto out;
440311601SDavid.Hollister@Sun.COM }
440411601SDavid.Hollister@Sun.COM
440510696SDavid.Hollister@Sun.COM pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, expander);
440610696SDavid.Hollister@Sun.COM if (pwrk == NULL) {
440710696SDavid.Hollister@Sun.COM goto out;
440810696SDavid.Hollister@Sun.COM }
440910696SDavid.Hollister@Sun.COM (void) memset(pwp->scratch, 0x77, PMCS_SCRATCH_SIZE);
441010696SDavid.Hollister@Sun.COM pwrk->arg = pwp->scratch;
441110696SDavid.Hollister@Sun.COM pwrk->dtype = expander->dtype;
441212462Sjesse.butler@oracle.com pwrk->xp = expander->target;
441312258Ssrikanth.suravajhala@oracle.com pwrk->htag |= PMCS_TAG_NONIO_CMD;
441410696SDavid.Hollister@Sun.COM msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST));
441510696SDavid.Hollister@Sun.COM msg[1] = LE_32(pwrk->htag);
441610696SDavid.Hollister@Sun.COM msg[2] = LE_32(expander->device_id);
441710696SDavid.Hollister@Sun.COM msg[3] = LE_32((12 << SMP_REQUEST_LENGTH_SHIFT) |
441810696SDavid.Hollister@Sun.COM SMP_INDIRECT_RESPONSE);
441910696SDavid.Hollister@Sun.COM /*
442010696SDavid.Hollister@Sun.COM * Send SMP DISCOVER (of either SAS1.1 or SAS2 flavors).
442110696SDavid.Hollister@Sun.COM */
442210696SDavid.Hollister@Sun.COM if (expander->tolerates_sas2) {
442310696SDavid.Hollister@Sun.COM msg[4] = BE_32(0x40101B00);
442410696SDavid.Hollister@Sun.COM } else {
442510696SDavid.Hollister@Sun.COM msg[4] = BE_32(0x40100000);
442610696SDavid.Hollister@Sun.COM }
442710696SDavid.Hollister@Sun.COM msg[5] = 0;
442810696SDavid.Hollister@Sun.COM msg[6] = BE_32((pptr->phynum << 16));
442910696SDavid.Hollister@Sun.COM msg[7] = 0;
443010696SDavid.Hollister@Sun.COM msg[8] = 0;
443110696SDavid.Hollister@Sun.COM msg[9] = 0;
443210696SDavid.Hollister@Sun.COM msg[10] = 0;
443310696SDavid.Hollister@Sun.COM msg[11] = 0;
443410696SDavid.Hollister@Sun.COM msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff));
443510696SDavid.Hollister@Sun.COM msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff));
443610696SDavid.Hollister@Sun.COM msg[14] = LE_32(PMCS_SCRATCH_SIZE - rdoff);
443710696SDavid.Hollister@Sun.COM msg[15] = 0;
443810696SDavid.Hollister@Sun.COM mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
443910696SDavid.Hollister@Sun.COM ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
444010696SDavid.Hollister@Sun.COM if (ptr == NULL) {
444110696SDavid.Hollister@Sun.COM mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
444210696SDavid.Hollister@Sun.COM goto out;
444310696SDavid.Hollister@Sun.COM }
444410696SDavid.Hollister@Sun.COM
444510696SDavid.Hollister@Sun.COM COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
444611267SJesse.Butler@Sun.COM
444712462Sjesse.butler@oracle.com pmcs_hold_iport(expander->iport);
444812462Sjesse.butler@oracle.com iport = expander->iport;
444912462Sjesse.butler@oracle.com pmcs_smp_acquire(iport);
445010696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_ONCHIP;
445110696SDavid.Hollister@Sun.COM htag = pwrk->htag;
445210696SDavid.Hollister@Sun.COM INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
445310696SDavid.Hollister@Sun.COM pmcs_unlock_phy(expander);
445410696SDavid.Hollister@Sun.COM WAIT_FOR(pwrk, 1000, result);
445512462Sjesse.butler@oracle.com pmcs_pwork(pwp, pwrk);
445613090Sjesse.butler@oracle.com pmcs_smp_release(iport);
445713090Sjesse.butler@oracle.com pmcs_rele_iport(iport);
445812862Sjesse.butler@oracle.com pmcs_lock_phy(expander);
445912862Sjesse.butler@oracle.com if (result) {
446012862Sjesse.butler@oracle.com pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
446112862Sjesse.butler@oracle.com "%s: Issuing SMP ABORT for htag 0x%08x", __func__, htag);
446212862Sjesse.butler@oracle.com if (pmcs_abort(pwp, pptr, htag, 0, 1)) {
446312862Sjesse.butler@oracle.com pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
446412862Sjesse.butler@oracle.com "%s: SMP ABORT failed for cmd (htag 0x%08x)",
446512862Sjesse.butler@oracle.com __func__, htag);
446612862Sjesse.butler@oracle.com }
446712862Sjesse.butler@oracle.com result = -ETIMEDOUT;
446812862Sjesse.butler@oracle.com goto out;
446912862Sjesse.butler@oracle.com }
447011267SJesse.Butler@Sun.COM
447110696SDavid.Hollister@Sun.COM mutex_enter(&pwp->config_lock);
447210696SDavid.Hollister@Sun.COM if (pwp->config_changed) {
447310696SDavid.Hollister@Sun.COM RESTART_DISCOVERY_LOCKED(pwp);
447410696SDavid.Hollister@Sun.COM mutex_exit(&pwp->config_lock);
447510696SDavid.Hollister@Sun.COM result = 0;
447610696SDavid.Hollister@Sun.COM goto out;
447710696SDavid.Hollister@Sun.COM }
447812862Sjesse.butler@oracle.com
447910696SDavid.Hollister@Sun.COM mutex_exit(&pwp->config_lock);
448010696SDavid.Hollister@Sun.COM ptr = (void *)pwp->scratch;
448110696SDavid.Hollister@Sun.COM /*
448210696SDavid.Hollister@Sun.COM * Point roff to the DMA offset for returned data
448310696SDavid.Hollister@Sun.COM */
448410696SDavid.Hollister@Sun.COM roff = pwp->scratch;
448510696SDavid.Hollister@Sun.COM roff += rdoff;
448610696SDavid.Hollister@Sun.COM srf = (smp_response_frame_t *)roff;
448710696SDavid.Hollister@Sun.COM sdr = (smp_discover_resp_t *)(roff+4);
448810696SDavid.Hollister@Sun.COM status = LE_32(ptr[2]);
448910696SDavid.Hollister@Sun.COM if (status == PMCOUT_STATUS_UNDERFLOW ||
449010696SDavid.Hollister@Sun.COM status == PMCOUT_STATUS_OVERFLOW) {
449111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, pptr, NULL,
449210696SDavid.Hollister@Sun.COM "%s: over/underflow", __func__);
449310696SDavid.Hollister@Sun.COM status = PMCOUT_STATUS_OK;
449410696SDavid.Hollister@Sun.COM }
449510696SDavid.Hollister@Sun.COM if (status != PMCOUT_STATUS_OK) {
449610696SDavid.Hollister@Sun.COM char *nag = NULL;
449710696SDavid.Hollister@Sun.COM (void) snprintf(buf, sizeof (buf),
449810696SDavid.Hollister@Sun.COM "%s: SMP op failed (0x%x)", __func__, status);
449910696SDavid.Hollister@Sun.COM switch (status) {
450010696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_ERROR_HW_TIMEOUT:
450110696SDavid.Hollister@Sun.COM DFM(nag, "Hardware Timeout");
450210696SDavid.Hollister@Sun.COM /* FALLTHROUGH */
450310696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE:
450410696SDavid.Hollister@Sun.COM DFM(nag, "Internal SMP Resource Failure");
450510696SDavid.Hollister@Sun.COM /* FALLTHROUGH */
450610696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
450710696SDavid.Hollister@Sun.COM DFM(nag, "PHY Not Ready");
450810696SDavid.Hollister@Sun.COM /* FALLTHROUGH */
450910696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
451010696SDavid.Hollister@Sun.COM DFM(nag, "Connection Rate Not Supported");
451110696SDavid.Hollister@Sun.COM /* FALLTHROUGH */
451210696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
451310696SDavid.Hollister@Sun.COM DFM(nag, "Open Retry Timeout");
451410696SDavid.Hollister@Sun.COM /* FALLTHROUGH */
451511693SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
451611693SDavid.Hollister@Sun.COM DFM(nag, "HW Resource Busy");
451711693SDavid.Hollister@Sun.COM /* FALLTHROUGH */
451810696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR:
451910696SDavid.Hollister@Sun.COM DFM(nag, "Response Connection Error");
452011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
452110696SDavid.Hollister@Sun.COM "%s: expander %s SMP operation failed (%s)",
452210696SDavid.Hollister@Sun.COM __func__, pptr->path, nag);
452310696SDavid.Hollister@Sun.COM break;
452410696SDavid.Hollister@Sun.COM default:
452510696SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG, buf, ptr);
452610696SDavid.Hollister@Sun.COM result = -EIO;
452710696SDavid.Hollister@Sun.COM break;
452810696SDavid.Hollister@Sun.COM }
452910696SDavid.Hollister@Sun.COM goto out;
453010696SDavid.Hollister@Sun.COM } else if (srf->srf_frame_type != SMP_FRAME_TYPE_RESPONSE) {
453111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
453210696SDavid.Hollister@Sun.COM "%s: bad response frame type 0x%x",
453310696SDavid.Hollister@Sun.COM __func__, srf->srf_frame_type);
453410696SDavid.Hollister@Sun.COM result = -EINVAL;
453510696SDavid.Hollister@Sun.COM goto out;
453610696SDavid.Hollister@Sun.COM } else if (srf->srf_function != SMP_FUNC_DISCOVER) {
453711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
453811048SDavid.Hollister@Sun.COM "%s: bad response function 0x%x",
453910696SDavid.Hollister@Sun.COM __func__, srf->srf_function);
454010696SDavid.Hollister@Sun.COM result = -EINVAL;
454110696SDavid.Hollister@Sun.COM goto out;
454210696SDavid.Hollister@Sun.COM } else if (srf->srf_result != SMP_RES_FUNCTION_ACCEPTED) {
454310696SDavid.Hollister@Sun.COM result = pmcs_smp_function_result(pwp, srf);
454410696SDavid.Hollister@Sun.COM /* Need not fail if PHY is Vacant */
454510696SDavid.Hollister@Sun.COM if (result != SMP_RES_PHY_VACANT) {
454610696SDavid.Hollister@Sun.COM result = -EINVAL;
454710696SDavid.Hollister@Sun.COM goto out;
454810696SDavid.Hollister@Sun.COM }
454910696SDavid.Hollister@Sun.COM }
455010696SDavid.Hollister@Sun.COM
455111601SDavid.Hollister@Sun.COM /*
455211601SDavid.Hollister@Sun.COM * Save off the DISCOVER response
455311601SDavid.Hollister@Sun.COM */
455411601SDavid.Hollister@Sun.COM bcopy(sdr, &pptr->disc_resp, sizeof (smp_discover_resp_t));
455511601SDavid.Hollister@Sun.COM
455610696SDavid.Hollister@Sun.COM ini_support = (sdr->sdr_attached_sata_host |
455710696SDavid.Hollister@Sun.COM (sdr->sdr_attached_smp_initiator << 1) |
455810696SDavid.Hollister@Sun.COM (sdr->sdr_attached_stp_initiator << 2) |
455910696SDavid.Hollister@Sun.COM (sdr->sdr_attached_ssp_initiator << 3));
456010696SDavid.Hollister@Sun.COM
456110696SDavid.Hollister@Sun.COM tgt_support = (sdr->sdr_attached_sata_device |
456210696SDavid.Hollister@Sun.COM (sdr->sdr_attached_smp_target << 1) |
456310696SDavid.Hollister@Sun.COM (sdr->sdr_attached_stp_target << 2) |
456410696SDavid.Hollister@Sun.COM (sdr->sdr_attached_ssp_target << 3));
456510696SDavid.Hollister@Sun.COM
456610696SDavid.Hollister@Sun.COM pmcs_wwn2barray(BE_64(sdr->sdr_sas_addr), sas_address);
456710696SDavid.Hollister@Sun.COM pmcs_wwn2barray(BE_64(sdr->sdr_attached_sas_addr), att_sas_address);
456810696SDavid.Hollister@Sun.COM
456912443Sdavid.hollister@oracle.com pptr->virtual = sdr->sdr_virtual_phy;
457012443Sdavid.hollister@oracle.com
457111601SDavid.Hollister@Sun.COM /*
457211601SDavid.Hollister@Sun.COM * Set the routing attribute regardless of the PHY type.
457311601SDavid.Hollister@Sun.COM */
457411601SDavid.Hollister@Sun.COM pptr->routing_attr = sdr->sdr_routing_attr;
457511601SDavid.Hollister@Sun.COM
457610696SDavid.Hollister@Sun.COM switch (sdr->sdr_attached_device_type) {
457710696SDavid.Hollister@Sun.COM case SAS_IF_DTYPE_ENDPOINT:
457811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
457910696SDavid.Hollister@Sun.COM "exp_content: %s atdt=0x%x lr=%x is=%x ts=%x SAS="
458010696SDavid.Hollister@Sun.COM SAS_ADDR_FMT " attSAS=" SAS_ADDR_FMT " atPHY=%x",
458110696SDavid.Hollister@Sun.COM pptr->path,
458210696SDavid.Hollister@Sun.COM sdr->sdr_attached_device_type,
458310696SDavid.Hollister@Sun.COM sdr->sdr_negotiated_logical_link_rate,
458410696SDavid.Hollister@Sun.COM ini_support,
458510696SDavid.Hollister@Sun.COM tgt_support,
458610696SDavid.Hollister@Sun.COM SAS_ADDR_PRT(sas_address),
458710696SDavid.Hollister@Sun.COM SAS_ADDR_PRT(att_sas_address),
458810696SDavid.Hollister@Sun.COM sdr->sdr_attached_phy_identifier);
458910696SDavid.Hollister@Sun.COM
459010696SDavid.Hollister@Sun.COM if (sdr->sdr_attached_sata_device ||
459110696SDavid.Hollister@Sun.COM sdr->sdr_attached_stp_target) {
459210696SDavid.Hollister@Sun.COM pptr->dtype = SATA;
459310696SDavid.Hollister@Sun.COM } else if (sdr->sdr_attached_ssp_target) {
459410696SDavid.Hollister@Sun.COM pptr->dtype = SAS;
459510696SDavid.Hollister@Sun.COM } else if (tgt_support || ini_support) {
459611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
459711048SDavid.Hollister@Sun.COM "%s: %s has tgt support=%x init support=(%x)",
459810696SDavid.Hollister@Sun.COM __func__, pptr->path, tgt_support, ini_support);
459910696SDavid.Hollister@Sun.COM }
460011601SDavid.Hollister@Sun.COM
460111601SDavid.Hollister@Sun.COM switch (pptr->routing_attr) {
460211601SDavid.Hollister@Sun.COM case SMP_ROUTING_SUBTRACTIVE:
460311601SDavid.Hollister@Sun.COM case SMP_ROUTING_TABLE:
460411601SDavid.Hollister@Sun.COM case SMP_ROUTING_DIRECT:
460511601SDavid.Hollister@Sun.COM pptr->routing_method = SMP_ROUTING_DIRECT;
460611601SDavid.Hollister@Sun.COM break;
460711601SDavid.Hollister@Sun.COM default:
460811601SDavid.Hollister@Sun.COM pptr->routing_method = 0xff; /* Invalid method */
460911601SDavid.Hollister@Sun.COM break;
461011601SDavid.Hollister@Sun.COM }
461111442SDavid.Hollister@Sun.COM pmcs_update_phy_pm_props(pptr, (1ULL << pptr->phynum),
461211442SDavid.Hollister@Sun.COM (1ULL << sdr->sdr_attached_phy_identifier), B_TRUE);
461310696SDavid.Hollister@Sun.COM break;
461410696SDavid.Hollister@Sun.COM case SAS_IF_DTYPE_EDGE:
461510696SDavid.Hollister@Sun.COM case SAS_IF_DTYPE_FANOUT:
461611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
461710696SDavid.Hollister@Sun.COM "exp_content: %s atdt=0x%x lr=%x is=%x ts=%x SAS="
461810696SDavid.Hollister@Sun.COM SAS_ADDR_FMT " attSAS=" SAS_ADDR_FMT " atPHY=%x",
461910696SDavid.Hollister@Sun.COM pptr->path,
462010696SDavid.Hollister@Sun.COM sdr->sdr_attached_device_type,
462110696SDavid.Hollister@Sun.COM sdr->sdr_negotiated_logical_link_rate,
462210696SDavid.Hollister@Sun.COM ini_support,
462310696SDavid.Hollister@Sun.COM tgt_support,
462410696SDavid.Hollister@Sun.COM SAS_ADDR_PRT(sas_address),
462510696SDavid.Hollister@Sun.COM SAS_ADDR_PRT(att_sas_address),
462610696SDavid.Hollister@Sun.COM sdr->sdr_attached_phy_identifier);
462710696SDavid.Hollister@Sun.COM if (sdr->sdr_attached_smp_target) {
462810696SDavid.Hollister@Sun.COM /*
462910696SDavid.Hollister@Sun.COM * Avoid configuring phys that just point back
463010696SDavid.Hollister@Sun.COM * at a parent phy
463110696SDavid.Hollister@Sun.COM */
463210696SDavid.Hollister@Sun.COM if (expander->parent &&
463310696SDavid.Hollister@Sun.COM memcmp(expander->parent->sas_address,
463410696SDavid.Hollister@Sun.COM att_sas_address,
463510696SDavid.Hollister@Sun.COM sizeof (expander->parent->sas_address)) == 0) {
463611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG3, pptr, NULL,
463710696SDavid.Hollister@Sun.COM "%s: skipping port back to parent "
463810696SDavid.Hollister@Sun.COM "expander (%s)", __func__, pptr->path);
463910696SDavid.Hollister@Sun.COM pptr->dtype = NOTHING;
464010696SDavid.Hollister@Sun.COM break;
464110696SDavid.Hollister@Sun.COM }
464210696SDavid.Hollister@Sun.COM pptr->dtype = EXPANDER;
464310696SDavid.Hollister@Sun.COM
464410696SDavid.Hollister@Sun.COM } else if (tgt_support || ini_support) {
464511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
464611048SDavid.Hollister@Sun.COM "%s has tgt support=%x init support=(%x)",
464710696SDavid.Hollister@Sun.COM pptr->path, tgt_support, ini_support);
464810696SDavid.Hollister@Sun.COM pptr->dtype = EXPANDER;
464910696SDavid.Hollister@Sun.COM }
465011601SDavid.Hollister@Sun.COM if (pptr->routing_attr == SMP_ROUTING_DIRECT) {
465111601SDavid.Hollister@Sun.COM pptr->routing_method = 0xff; /* Invalid method */
465211601SDavid.Hollister@Sun.COM } else {
465311601SDavid.Hollister@Sun.COM pptr->routing_method = pptr->routing_attr;
465411601SDavid.Hollister@Sun.COM }
465511442SDavid.Hollister@Sun.COM pmcs_update_phy_pm_props(pptr, (1ULL << pptr->phynum),
465611442SDavid.Hollister@Sun.COM (1ULL << sdr->sdr_attached_phy_identifier), B_TRUE);
465710696SDavid.Hollister@Sun.COM break;
465810696SDavid.Hollister@Sun.COM default:
465910696SDavid.Hollister@Sun.COM pptr->dtype = NOTHING;
466010696SDavid.Hollister@Sun.COM break;
466110696SDavid.Hollister@Sun.COM }
466210696SDavid.Hollister@Sun.COM if (pptr->dtype != NOTHING) {
466310696SDavid.Hollister@Sun.COM pmcs_phy_t *ctmp;
466410696SDavid.Hollister@Sun.COM
466510696SDavid.Hollister@Sun.COM /*
466610696SDavid.Hollister@Sun.COM * If the attached device is a SATA device and the expander
466710696SDavid.Hollister@Sun.COM * is (possibly) a SAS2 compliant expander, check for whether
466810696SDavid.Hollister@Sun.COM * there is a NAA=5 WWN field starting at this offset and
466910696SDavid.Hollister@Sun.COM * use that for the SAS Address for this device.
467010696SDavid.Hollister@Sun.COM */
467110696SDavid.Hollister@Sun.COM if (expander->tolerates_sas2 && pptr->dtype == SATA &&
467211693SDavid.Hollister@Sun.COM (roff[SAS_ATTACHED_NAME_OFFSET] >> 8) == NAA_IEEE_REG) {
467310696SDavid.Hollister@Sun.COM (void) memcpy(pptr->sas_address,
467410696SDavid.Hollister@Sun.COM &roff[SAS_ATTACHED_NAME_OFFSET], 8);
467510696SDavid.Hollister@Sun.COM } else {
467610696SDavid.Hollister@Sun.COM (void) memcpy(pptr->sas_address, att_sas_address, 8);
467710696SDavid.Hollister@Sun.COM }
467810696SDavid.Hollister@Sun.COM pptr->atdt = (sdr->sdr_attached_device_type);
467910696SDavid.Hollister@Sun.COM /*
468010696SDavid.Hollister@Sun.COM * Now run up from the expander's parent up to the top to
468110696SDavid.Hollister@Sun.COM * make sure we only use the least common link_rate.
468210696SDavid.Hollister@Sun.COM */
468310696SDavid.Hollister@Sun.COM for (ctmp = expander->parent; ctmp; ctmp = ctmp->parent) {
468410696SDavid.Hollister@Sun.COM if (ctmp->link_rate <
468510696SDavid.Hollister@Sun.COM sdr->sdr_negotiated_logical_link_rate) {
468611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
468710696SDavid.Hollister@Sun.COM "%s: derating link rate from %x to %x due "
468810696SDavid.Hollister@Sun.COM "to %s being slower", pptr->path,
468910696SDavid.Hollister@Sun.COM sdr->sdr_negotiated_logical_link_rate,
469010696SDavid.Hollister@Sun.COM ctmp->link_rate,
469110696SDavid.Hollister@Sun.COM ctmp->path);
469210696SDavid.Hollister@Sun.COM sdr->sdr_negotiated_logical_link_rate =
469310696SDavid.Hollister@Sun.COM ctmp->link_rate;
469410696SDavid.Hollister@Sun.COM }
469510696SDavid.Hollister@Sun.COM }
469610696SDavid.Hollister@Sun.COM pptr->link_rate = sdr->sdr_negotiated_logical_link_rate;
469710696SDavid.Hollister@Sun.COM pptr->state.prog_min_rate = sdr->sdr_prog_min_phys_link_rate;
469810696SDavid.Hollister@Sun.COM pptr->state.hw_min_rate = sdr->sdr_hw_min_phys_link_rate;
469910696SDavid.Hollister@Sun.COM pptr->state.prog_max_rate = sdr->sdr_prog_max_phys_link_rate;
470010696SDavid.Hollister@Sun.COM pptr->state.hw_max_rate = sdr->sdr_hw_max_phys_link_rate;
470110696SDavid.Hollister@Sun.COM PHY_CHANGED(pwp, pptr);
470210696SDavid.Hollister@Sun.COM } else {
470310696SDavid.Hollister@Sun.COM pmcs_clear_phy(pwp, pptr);
470410696SDavid.Hollister@Sun.COM }
470510696SDavid.Hollister@Sun.COM result = 1;
470610696SDavid.Hollister@Sun.COM out:
470710696SDavid.Hollister@Sun.COM return (result);
470810696SDavid.Hollister@Sun.COM }
470910696SDavid.Hollister@Sun.COM
471010696SDavid.Hollister@Sun.COM /*
471110696SDavid.Hollister@Sun.COM * Get a work structure and assign it a tag with type and serial number
471210696SDavid.Hollister@Sun.COM * If a structure is returned, it is returned locked.
471310696SDavid.Hollister@Sun.COM */
471410696SDavid.Hollister@Sun.COM pmcwork_t *
pmcs_gwork(pmcs_hw_t * pwp,uint32_t tag_type,pmcs_phy_t * phyp)471510696SDavid.Hollister@Sun.COM pmcs_gwork(pmcs_hw_t *pwp, uint32_t tag_type, pmcs_phy_t *phyp)
471610696SDavid.Hollister@Sun.COM {
471710696SDavid.Hollister@Sun.COM pmcwork_t *p;
471810696SDavid.Hollister@Sun.COM uint16_t snum;
471910696SDavid.Hollister@Sun.COM uint32_t off;
472010696SDavid.Hollister@Sun.COM
472110696SDavid.Hollister@Sun.COM mutex_enter(&pwp->wfree_lock);
472210696SDavid.Hollister@Sun.COM p = STAILQ_FIRST(&pwp->wf);
472310696SDavid.Hollister@Sun.COM if (p == NULL) {
472410696SDavid.Hollister@Sun.COM /*
472510696SDavid.Hollister@Sun.COM * If we couldn't get a work structure, it's time to bite
472610696SDavid.Hollister@Sun.COM * the bullet, grab the pfree_lock and copy over all the
472710696SDavid.Hollister@Sun.COM * work structures from the pending free list to the actual
472811545SRamana.Srikanth@Sun.COM * free list (assuming it's not also empty).
472910696SDavid.Hollister@Sun.COM */
473010696SDavid.Hollister@Sun.COM mutex_enter(&pwp->pfree_lock);
473111545SRamana.Srikanth@Sun.COM if (STAILQ_FIRST(&pwp->pf) == NULL) {
473211545SRamana.Srikanth@Sun.COM mutex_exit(&pwp->pfree_lock);
473311545SRamana.Srikanth@Sun.COM mutex_exit(&pwp->wfree_lock);
473411545SRamana.Srikanth@Sun.COM return (NULL);
473511545SRamana.Srikanth@Sun.COM }
473610696SDavid.Hollister@Sun.COM pwp->wf.stqh_first = pwp->pf.stqh_first;
473710696SDavid.Hollister@Sun.COM pwp->wf.stqh_last = pwp->pf.stqh_last;
473810696SDavid.Hollister@Sun.COM STAILQ_INIT(&pwp->pf);
473910696SDavid.Hollister@Sun.COM mutex_exit(&pwp->pfree_lock);
474010696SDavid.Hollister@Sun.COM
474110696SDavid.Hollister@Sun.COM p = STAILQ_FIRST(&pwp->wf);
474211545SRamana.Srikanth@Sun.COM ASSERT(p != NULL);
474310696SDavid.Hollister@Sun.COM }
474410696SDavid.Hollister@Sun.COM STAILQ_REMOVE(&pwp->wf, p, pmcwork, next);
474510696SDavid.Hollister@Sun.COM snum = pwp->wserno++;
474610696SDavid.Hollister@Sun.COM mutex_exit(&pwp->wfree_lock);
474710696SDavid.Hollister@Sun.COM
474810696SDavid.Hollister@Sun.COM off = p - pwp->work;
474910696SDavid.Hollister@Sun.COM
475010696SDavid.Hollister@Sun.COM mutex_enter(&p->lock);
475110696SDavid.Hollister@Sun.COM ASSERT(p->state == PMCS_WORK_STATE_NIL);
475210696SDavid.Hollister@Sun.COM ASSERT(p->htag == PMCS_TAG_FREE);
475310696SDavid.Hollister@Sun.COM p->htag = (tag_type << PMCS_TAG_TYPE_SHIFT) & PMCS_TAG_TYPE_MASK;
475410696SDavid.Hollister@Sun.COM p->htag |= ((snum << PMCS_TAG_SERNO_SHIFT) & PMCS_TAG_SERNO_MASK);
475510696SDavid.Hollister@Sun.COM p->htag |= ((off << PMCS_TAG_INDEX_SHIFT) & PMCS_TAG_INDEX_MASK);
475610696SDavid.Hollister@Sun.COM p->start = gethrtime();
475710696SDavid.Hollister@Sun.COM p->state = PMCS_WORK_STATE_READY;
475810696SDavid.Hollister@Sun.COM p->ssp_event = 0;
475910696SDavid.Hollister@Sun.COM p->dead = 0;
476013005Ssrikanth.suravajhala@oracle.com p->timer = 0;
476110696SDavid.Hollister@Sun.COM
476210696SDavid.Hollister@Sun.COM if (phyp) {
476310696SDavid.Hollister@Sun.COM p->phy = phyp;
476410696SDavid.Hollister@Sun.COM pmcs_inc_phy_ref_count(phyp);
476510696SDavid.Hollister@Sun.COM }
476610696SDavid.Hollister@Sun.COM
476710696SDavid.Hollister@Sun.COM return (p);
476810696SDavid.Hollister@Sun.COM }
476910696SDavid.Hollister@Sun.COM
477010696SDavid.Hollister@Sun.COM /*
477110696SDavid.Hollister@Sun.COM * Called with pwrk lock held. Returned with lock released.
477210696SDavid.Hollister@Sun.COM */
477310696SDavid.Hollister@Sun.COM void
pmcs_pwork(pmcs_hw_t * pwp,pmcwork_t * p)477410696SDavid.Hollister@Sun.COM pmcs_pwork(pmcs_hw_t *pwp, pmcwork_t *p)
477510696SDavid.Hollister@Sun.COM {
477610696SDavid.Hollister@Sun.COM ASSERT(p != NULL);
477710696SDavid.Hollister@Sun.COM ASSERT(mutex_owned(&p->lock));
477810696SDavid.Hollister@Sun.COM
477910696SDavid.Hollister@Sun.COM p->last_ptr = p->ptr;
478010696SDavid.Hollister@Sun.COM p->last_arg = p->arg;
478110696SDavid.Hollister@Sun.COM p->last_phy = p->phy;
478210696SDavid.Hollister@Sun.COM p->last_xp = p->xp;
478310696SDavid.Hollister@Sun.COM p->last_htag = p->htag;
478410696SDavid.Hollister@Sun.COM p->last_state = p->state;
478510696SDavid.Hollister@Sun.COM p->finish = gethrtime();
478610696SDavid.Hollister@Sun.COM
478710696SDavid.Hollister@Sun.COM if (p->phy) {
478810696SDavid.Hollister@Sun.COM pmcs_dec_phy_ref_count(p->phy);
478910696SDavid.Hollister@Sun.COM }
479010696SDavid.Hollister@Sun.COM
479110696SDavid.Hollister@Sun.COM p->state = PMCS_WORK_STATE_NIL;
479210696SDavid.Hollister@Sun.COM p->htag = PMCS_TAG_FREE;
479310696SDavid.Hollister@Sun.COM p->xp = NULL;
479410696SDavid.Hollister@Sun.COM p->ptr = NULL;
479510696SDavid.Hollister@Sun.COM p->arg = NULL;
479610696SDavid.Hollister@Sun.COM p->phy = NULL;
479711048SDavid.Hollister@Sun.COM p->abt_htag = 0;
479810696SDavid.Hollister@Sun.COM p->timer = 0;
479912807Ssrikanth.suravajhala@oracle.com p->onwire = 0;
4800*13103Ssrikanth.suravajhala@oracle.com p->ssp_event = 0;
480110696SDavid.Hollister@Sun.COM mutex_exit(&p->lock);
480210696SDavid.Hollister@Sun.COM
480310696SDavid.Hollister@Sun.COM if (mutex_tryenter(&pwp->wfree_lock) == 0) {
480410696SDavid.Hollister@Sun.COM mutex_enter(&pwp->pfree_lock);
480510696SDavid.Hollister@Sun.COM STAILQ_INSERT_TAIL(&pwp->pf, p, next);
480610696SDavid.Hollister@Sun.COM mutex_exit(&pwp->pfree_lock);
480710696SDavid.Hollister@Sun.COM } else {
480810696SDavid.Hollister@Sun.COM STAILQ_INSERT_TAIL(&pwp->wf, p, next);
480910696SDavid.Hollister@Sun.COM mutex_exit(&pwp->wfree_lock);
481010696SDavid.Hollister@Sun.COM }
481110696SDavid.Hollister@Sun.COM }
481210696SDavid.Hollister@Sun.COM
481310696SDavid.Hollister@Sun.COM /*
481410696SDavid.Hollister@Sun.COM * Find a work structure based upon a tag and make sure that the tag
481510696SDavid.Hollister@Sun.COM * serial number matches the work structure we've found.
481610696SDavid.Hollister@Sun.COM * If a structure is found, its lock is held upon return.
481712258Ssrikanth.suravajhala@oracle.com * If lock_phy is B_TRUE, then lock the phy also when returning the work struct
481810696SDavid.Hollister@Sun.COM */
481910696SDavid.Hollister@Sun.COM pmcwork_t *
pmcs_tag2wp(pmcs_hw_t * pwp,uint32_t htag,boolean_t lock_phy)482012258Ssrikanth.suravajhala@oracle.com pmcs_tag2wp(pmcs_hw_t *pwp, uint32_t htag, boolean_t lock_phy)
482110696SDavid.Hollister@Sun.COM {
482210696SDavid.Hollister@Sun.COM pmcwork_t *p;
482312668Ssrikanth.suravajhala@oracle.com pmcs_phy_t *phyp;
482410696SDavid.Hollister@Sun.COM uint32_t idx = PMCS_TAG_INDEX(htag);
482510696SDavid.Hollister@Sun.COM
482610696SDavid.Hollister@Sun.COM p = &pwp->work[idx];
482710696SDavid.Hollister@Sun.COM
482810696SDavid.Hollister@Sun.COM mutex_enter(&p->lock);
482910696SDavid.Hollister@Sun.COM if (p->htag == htag) {
483012258Ssrikanth.suravajhala@oracle.com if (lock_phy) {
483112668Ssrikanth.suravajhala@oracle.com phyp = p->phy;
483212668Ssrikanth.suravajhala@oracle.com if (phyp != NULL) {
483312668Ssrikanth.suravajhala@oracle.com /* phy lock should be held before work lock */
483412668Ssrikanth.suravajhala@oracle.com mutex_exit(&p->lock);
483512668Ssrikanth.suravajhala@oracle.com mutex_enter(&phyp->phy_lock);
483612668Ssrikanth.suravajhala@oracle.com mutex_enter(&p->lock);
483712668Ssrikanth.suravajhala@oracle.com }
483812668Ssrikanth.suravajhala@oracle.com /*
483912668Ssrikanth.suravajhala@oracle.com * Check htag again, in case the work got completed
484012668Ssrikanth.suravajhala@oracle.com * while we dropped the work lock and got the phy lock
484112668Ssrikanth.suravajhala@oracle.com */
484212668Ssrikanth.suravajhala@oracle.com if (p->htag != htag) {
484312668Ssrikanth.suravajhala@oracle.com if (phyp != NULL) {
484412668Ssrikanth.suravajhala@oracle.com mutex_exit(&p->lock);
484512668Ssrikanth.suravajhala@oracle.com mutex_exit(&phyp->phy_lock);
484612668Ssrikanth.suravajhala@oracle.com }
484712668Ssrikanth.suravajhala@oracle.com pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL, "%s: "
484812668Ssrikanth.suravajhala@oracle.com "HTAG (0x%x) found, but work (0x%p) "
484912668Ssrikanth.suravajhala@oracle.com "is already complete", __func__, htag,
485012668Ssrikanth.suravajhala@oracle.com (void *)p);
485112668Ssrikanth.suravajhala@oracle.com return (NULL);
485212668Ssrikanth.suravajhala@oracle.com }
485312258Ssrikanth.suravajhala@oracle.com }
485410696SDavid.Hollister@Sun.COM return (p);
485510696SDavid.Hollister@Sun.COM }
485610696SDavid.Hollister@Sun.COM mutex_exit(&p->lock);
485711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
485811048SDavid.Hollister@Sun.COM "INDEX 0x%x HTAG 0x%x got p->htag 0x%x", idx, htag, p->htag);
485910696SDavid.Hollister@Sun.COM return (NULL);
486010696SDavid.Hollister@Sun.COM }
486110696SDavid.Hollister@Sun.COM
486210696SDavid.Hollister@Sun.COM /*
486310696SDavid.Hollister@Sun.COM * Issue an abort for a command or for all commands.
486410696SDavid.Hollister@Sun.COM *
486510696SDavid.Hollister@Sun.COM * Since this can be called from interrupt context,
486610696SDavid.Hollister@Sun.COM * we don't wait for completion if wait is not set.
486710696SDavid.Hollister@Sun.COM *
486810696SDavid.Hollister@Sun.COM * Called with PHY lock held.
486910696SDavid.Hollister@Sun.COM */
487010696SDavid.Hollister@Sun.COM int
pmcs_abort(pmcs_hw_t * pwp,pmcs_phy_t * pptr,uint32_t tag,int all_cmds,int wait)487110696SDavid.Hollister@Sun.COM pmcs_abort(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint32_t tag, int all_cmds,
487210696SDavid.Hollister@Sun.COM int wait)
487310696SDavid.Hollister@Sun.COM {
487410696SDavid.Hollister@Sun.COM pmcwork_t *pwrk;
487510696SDavid.Hollister@Sun.COM pmcs_xscsi_t *tgt;
487610696SDavid.Hollister@Sun.COM uint32_t msg[PMCS_MSG_SIZE], *ptr;
487710696SDavid.Hollister@Sun.COM int result, abt_type;
487810696SDavid.Hollister@Sun.COM uint32_t abt_htag, status;
487910696SDavid.Hollister@Sun.COM
488010696SDavid.Hollister@Sun.COM if (pptr->abort_all_start) {
488111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, "%s: ABORT_ALL for "
488211048SDavid.Hollister@Sun.COM "(%s) already in progress.", __func__, pptr->path);
488310696SDavid.Hollister@Sun.COM return (EBUSY);
488410696SDavid.Hollister@Sun.COM }
488510696SDavid.Hollister@Sun.COM
488610696SDavid.Hollister@Sun.COM switch (pptr->dtype) {
488710696SDavid.Hollister@Sun.COM case SAS:
488810696SDavid.Hollister@Sun.COM abt_type = PMCIN_SSP_ABORT;
488910696SDavid.Hollister@Sun.COM break;
489010696SDavid.Hollister@Sun.COM case SATA:
489110696SDavid.Hollister@Sun.COM abt_type = PMCIN_SATA_ABORT;
489210696SDavid.Hollister@Sun.COM break;
489310696SDavid.Hollister@Sun.COM case EXPANDER:
489410696SDavid.Hollister@Sun.COM abt_type = PMCIN_SMP_ABORT;
489510696SDavid.Hollister@Sun.COM break;
489610696SDavid.Hollister@Sun.COM default:
489710696SDavid.Hollister@Sun.COM return (0);
489810696SDavid.Hollister@Sun.COM }
489910696SDavid.Hollister@Sun.COM
490010696SDavid.Hollister@Sun.COM pwrk = pmcs_gwork(pwp, wait ? PMCS_TAG_TYPE_WAIT : PMCS_TAG_TYPE_NONE,
490110696SDavid.Hollister@Sun.COM pptr);
490210696SDavid.Hollister@Sun.COM
490310696SDavid.Hollister@Sun.COM if (pwrk == NULL) {
490411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__);
490510696SDavid.Hollister@Sun.COM return (ENOMEM);
490610696SDavid.Hollister@Sun.COM }
490710696SDavid.Hollister@Sun.COM
490810696SDavid.Hollister@Sun.COM pwrk->dtype = pptr->dtype;
490912462Sjesse.butler@oracle.com pwrk->xp = pptr->target;
491012258Ssrikanth.suravajhala@oracle.com pwrk->htag |= PMCS_TAG_NONIO_CMD;
491110696SDavid.Hollister@Sun.COM if (wait) {
491210696SDavid.Hollister@Sun.COM pwrk->arg = msg;
491310696SDavid.Hollister@Sun.COM }
491410696SDavid.Hollister@Sun.COM if (pptr->valid_device_id == 0) {
491510696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
491611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
491711048SDavid.Hollister@Sun.COM "%s: Invalid DeviceID", __func__);
491810696SDavid.Hollister@Sun.COM return (ENODEV);
491910696SDavid.Hollister@Sun.COM }
492010696SDavid.Hollister@Sun.COM msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, abt_type));
492110696SDavid.Hollister@Sun.COM msg[1] = LE_32(pwrk->htag);
492210696SDavid.Hollister@Sun.COM msg[2] = LE_32(pptr->device_id);
492310696SDavid.Hollister@Sun.COM if (all_cmds) {
492410696SDavid.Hollister@Sun.COM msg[3] = 0;
492510696SDavid.Hollister@Sun.COM msg[4] = LE_32(1);
492610696SDavid.Hollister@Sun.COM pwrk->ptr = NULL;
492712506Sjesse.butler@oracle.com pwrk->abt_htag = PMCS_ABT_HTAG_ALL;
492810696SDavid.Hollister@Sun.COM pptr->abort_all_start = gethrtime();
492910696SDavid.Hollister@Sun.COM } else {
493010696SDavid.Hollister@Sun.COM msg[3] = LE_32(tag);
493110696SDavid.Hollister@Sun.COM msg[4] = 0;
493211048SDavid.Hollister@Sun.COM pwrk->abt_htag = tag;
493310696SDavid.Hollister@Sun.COM }
493410696SDavid.Hollister@Sun.COM mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
493510696SDavid.Hollister@Sun.COM ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
493610696SDavid.Hollister@Sun.COM if (ptr == NULL) {
493710696SDavid.Hollister@Sun.COM mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
493810696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
493912462Sjesse.butler@oracle.com pptr->abort_all_start = 0;
494011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__);
494110696SDavid.Hollister@Sun.COM return (ENOMEM);
494210696SDavid.Hollister@Sun.COM }
494310696SDavid.Hollister@Sun.COM
494410696SDavid.Hollister@Sun.COM COPY_MESSAGE(ptr, msg, 5);
494510696SDavid.Hollister@Sun.COM if (all_cmds) {
494611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
494710696SDavid.Hollister@Sun.COM "%s: aborting all commands for %s device %s. (htag=0x%x)",
494810696SDavid.Hollister@Sun.COM __func__, pmcs_get_typename(pptr->dtype), pptr->path,
494910696SDavid.Hollister@Sun.COM msg[1]);
495010696SDavid.Hollister@Sun.COM } else {
495111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
495210696SDavid.Hollister@Sun.COM "%s: aborting tag 0x%x for %s device %s. (htag=0x%x)",
495310696SDavid.Hollister@Sun.COM __func__, tag, pmcs_get_typename(pptr->dtype), pptr->path,
495410696SDavid.Hollister@Sun.COM msg[1]);
495510696SDavid.Hollister@Sun.COM }
495610696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_ONCHIP;
495710696SDavid.Hollister@Sun.COM
495810696SDavid.Hollister@Sun.COM INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
495910696SDavid.Hollister@Sun.COM if (!wait) {
496010696SDavid.Hollister@Sun.COM mutex_exit(&pwrk->lock);
496110696SDavid.Hollister@Sun.COM return (0);
496210696SDavid.Hollister@Sun.COM }
496310696SDavid.Hollister@Sun.COM
496410696SDavid.Hollister@Sun.COM abt_htag = pwrk->htag;
496512462Sjesse.butler@oracle.com pmcs_unlock_phy(pptr);
496610696SDavid.Hollister@Sun.COM WAIT_FOR(pwrk, 1000, result);
496710696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
496812462Sjesse.butler@oracle.com pmcs_lock_phy(pptr);
496912462Sjesse.butler@oracle.com tgt = pptr->target;
497010696SDavid.Hollister@Sun.COM
497110696SDavid.Hollister@Sun.COM if (all_cmds) {
497210696SDavid.Hollister@Sun.COM pptr->abort_all_start = 0;
497310696SDavid.Hollister@Sun.COM cv_signal(&pptr->abort_all_cv);
497410696SDavid.Hollister@Sun.COM }
497510696SDavid.Hollister@Sun.COM
497610696SDavid.Hollister@Sun.COM if (result) {
497712506Sjesse.butler@oracle.com if (all_cmds) {
497812506Sjesse.butler@oracle.com pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
497912506Sjesse.butler@oracle.com "%s: Abort all request timed out", __func__);
498012506Sjesse.butler@oracle.com } else {
498112506Sjesse.butler@oracle.com pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
498212506Sjesse.butler@oracle.com "%s: Abort (htag 0x%08x) request timed out",
498312506Sjesse.butler@oracle.com __func__, abt_htag);
498412506Sjesse.butler@oracle.com }
498510696SDavid.Hollister@Sun.COM if (tgt != NULL) {
498610696SDavid.Hollister@Sun.COM mutex_enter(&tgt->statlock);
498710696SDavid.Hollister@Sun.COM if ((tgt->dev_state != PMCS_DEVICE_STATE_IN_RECOVERY) &&
498810696SDavid.Hollister@Sun.COM (tgt->dev_state !=
498910696SDavid.Hollister@Sun.COM PMCS_DEVICE_STATE_NON_OPERATIONAL)) {
499011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
499110696SDavid.Hollister@Sun.COM "%s: Trying DS error recovery for tgt 0x%p",
499210696SDavid.Hollister@Sun.COM __func__, (void *)tgt);
499310696SDavid.Hollister@Sun.COM (void) pmcs_send_err_recovery_cmd(pwp,
499411090SDavid.Hollister@Sun.COM PMCS_DEVICE_STATE_IN_RECOVERY, pptr, tgt);
499510696SDavid.Hollister@Sun.COM }
499610696SDavid.Hollister@Sun.COM mutex_exit(&tgt->statlock);
499710696SDavid.Hollister@Sun.COM }
499810696SDavid.Hollister@Sun.COM return (ETIMEDOUT);
499910696SDavid.Hollister@Sun.COM }
500010696SDavid.Hollister@Sun.COM
500110696SDavid.Hollister@Sun.COM status = LE_32(msg[2]);
500210696SDavid.Hollister@Sun.COM if (status != PMCOUT_STATUS_OK) {
500310696SDavid.Hollister@Sun.COM /*
500410696SDavid.Hollister@Sun.COM * The only non-success status are IO_NOT_VALID &
500510696SDavid.Hollister@Sun.COM * IO_ABORT_IN_PROGRESS.
500610696SDavid.Hollister@Sun.COM * In case of IO_ABORT_IN_PROGRESS, the other ABORT cmd's
500710696SDavid.Hollister@Sun.COM * status is of concern and this duplicate cmd status can
500810696SDavid.Hollister@Sun.COM * be ignored.
500910696SDavid.Hollister@Sun.COM * If IO_NOT_VALID, that's not an error per-se.
501010696SDavid.Hollister@Sun.COM * For abort of single I/O complete the command anyway.
501110696SDavid.Hollister@Sun.COM * If, however, we were aborting all, that is a problem
501210696SDavid.Hollister@Sun.COM * as IO_NOT_VALID really means that the IO or device is
501310696SDavid.Hollister@Sun.COM * not there. So, discovery process will take of the cleanup.
501410696SDavid.Hollister@Sun.COM */
501511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
501611048SDavid.Hollister@Sun.COM "%s: abort result 0x%x", __func__, LE_32(msg[2]));
501710696SDavid.Hollister@Sun.COM if (all_cmds) {
501810696SDavid.Hollister@Sun.COM PHY_CHANGED(pwp, pptr);
501910696SDavid.Hollister@Sun.COM RESTART_DISCOVERY(pwp);
502010696SDavid.Hollister@Sun.COM } else {
502110696SDavid.Hollister@Sun.COM return (EINVAL);
502210696SDavid.Hollister@Sun.COM }
502310696SDavid.Hollister@Sun.COM
502410696SDavid.Hollister@Sun.COM return (0);
502510696SDavid.Hollister@Sun.COM }
502610696SDavid.Hollister@Sun.COM
502710696SDavid.Hollister@Sun.COM if (tgt != NULL) {
502810696SDavid.Hollister@Sun.COM mutex_enter(&tgt->statlock);
502910696SDavid.Hollister@Sun.COM if (tgt->dev_state == PMCS_DEVICE_STATE_IN_RECOVERY) {
503011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
503110696SDavid.Hollister@Sun.COM "%s: Restoring OPERATIONAL dev_state for tgt 0x%p",
503210696SDavid.Hollister@Sun.COM __func__, (void *)tgt);
503310696SDavid.Hollister@Sun.COM (void) pmcs_send_err_recovery_cmd(pwp,
503411090SDavid.Hollister@Sun.COM PMCS_DEVICE_STATE_OPERATIONAL, pptr, tgt);
503510696SDavid.Hollister@Sun.COM }
503610696SDavid.Hollister@Sun.COM mutex_exit(&tgt->statlock);
503710696SDavid.Hollister@Sun.COM }
503810696SDavid.Hollister@Sun.COM
503910696SDavid.Hollister@Sun.COM return (0);
504010696SDavid.Hollister@Sun.COM }
504110696SDavid.Hollister@Sun.COM
504210696SDavid.Hollister@Sun.COM /*
504310696SDavid.Hollister@Sun.COM * Issue a task management function to an SSP device.
504410696SDavid.Hollister@Sun.COM *
504510696SDavid.Hollister@Sun.COM * Called with PHY lock held.
504610696SDavid.Hollister@Sun.COM * statlock CANNOT be held upon entry.
504710696SDavid.Hollister@Sun.COM */
504810696SDavid.Hollister@Sun.COM int
pmcs_ssp_tmf(pmcs_hw_t * pwp,pmcs_phy_t * pptr,uint8_t tmf,uint32_t tag,uint64_t lun,uint32_t * response)504910696SDavid.Hollister@Sun.COM pmcs_ssp_tmf(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint8_t tmf, uint32_t tag,
505010696SDavid.Hollister@Sun.COM uint64_t lun, uint32_t *response)
505110696SDavid.Hollister@Sun.COM {
505210696SDavid.Hollister@Sun.COM int result, ds;
505310696SDavid.Hollister@Sun.COM uint8_t local[PMCS_QENTRY_SIZE << 1], *xd;
505410696SDavid.Hollister@Sun.COM sas_ssp_rsp_iu_t *rptr = (void *)local;
505510696SDavid.Hollister@Sun.COM static const uint8_t ssp_rsp_evec[] = {
505610696SDavid.Hollister@Sun.COM 0x58, 0x61, 0x56, 0x72, 0x00
505710696SDavid.Hollister@Sun.COM };
505810696SDavid.Hollister@Sun.COM uint32_t msg[PMCS_MSG_SIZE], *ptr, status;
505910696SDavid.Hollister@Sun.COM struct pmcwork *pwrk;
506010696SDavid.Hollister@Sun.COM pmcs_xscsi_t *xp;
506110696SDavid.Hollister@Sun.COM
506210696SDavid.Hollister@Sun.COM pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
506310696SDavid.Hollister@Sun.COM if (pwrk == NULL) {
506411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nowrk, __func__);
506510696SDavid.Hollister@Sun.COM return (ENOMEM);
506610696SDavid.Hollister@Sun.COM }
506710696SDavid.Hollister@Sun.COM /*
506810696SDavid.Hollister@Sun.COM * NB: We use the PMCS_OQ_GENERAL outbound queue
506910696SDavid.Hollister@Sun.COM * NB: so as to not get entangled in normal I/O
507010696SDavid.Hollister@Sun.COM * NB: processing.
507110696SDavid.Hollister@Sun.COM */
507212258Ssrikanth.suravajhala@oracle.com pwrk->htag |= PMCS_TAG_NONIO_CMD;
507310696SDavid.Hollister@Sun.COM msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
507410696SDavid.Hollister@Sun.COM PMCIN_SSP_INI_TM_START));
507510696SDavid.Hollister@Sun.COM msg[1] = LE_32(pwrk->htag);
507610696SDavid.Hollister@Sun.COM msg[2] = LE_32(pptr->device_id);
507710696SDavid.Hollister@Sun.COM if (tmf == SAS_ABORT_TASK || tmf == SAS_QUERY_TASK) {
507810696SDavid.Hollister@Sun.COM msg[3] = LE_32(tag);
507910696SDavid.Hollister@Sun.COM } else {
508010696SDavid.Hollister@Sun.COM msg[3] = 0;
508110696SDavid.Hollister@Sun.COM }
508210696SDavid.Hollister@Sun.COM msg[4] = LE_32(tmf);
508310696SDavid.Hollister@Sun.COM msg[5] = BE_32((uint32_t)lun);
508410696SDavid.Hollister@Sun.COM msg[6] = BE_32((uint32_t)(lun >> 32));
508510696SDavid.Hollister@Sun.COM msg[7] = LE_32(PMCIN_MESSAGE_REPORT);
508610696SDavid.Hollister@Sun.COM
508710696SDavid.Hollister@Sun.COM mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
508810696SDavid.Hollister@Sun.COM ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
508910696SDavid.Hollister@Sun.COM if (ptr == NULL) {
509010696SDavid.Hollister@Sun.COM mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
509110696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
509211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_ERR, pptr, NULL, pmcs_nomsg, __func__);
509310696SDavid.Hollister@Sun.COM return (ENOMEM);
509410696SDavid.Hollister@Sun.COM }
509510696SDavid.Hollister@Sun.COM COPY_MESSAGE(ptr, msg, 7);
509610696SDavid.Hollister@Sun.COM pwrk->arg = msg;
509710696SDavid.Hollister@Sun.COM pwrk->dtype = pptr->dtype;
509810696SDavid.Hollister@Sun.COM xp = pptr->target;
509911355SDavid.Hollister@Sun.COM pwrk->xp = xp;
510011355SDavid.Hollister@Sun.COM
510110696SDavid.Hollister@Sun.COM if (xp != NULL) {
510210696SDavid.Hollister@Sun.COM mutex_enter(&xp->statlock);
510310696SDavid.Hollister@Sun.COM if (xp->dev_state == PMCS_DEVICE_STATE_NON_OPERATIONAL) {
510410696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
510510696SDavid.Hollister@Sun.COM mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
510610696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
510711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, "%s: Not "
510811048SDavid.Hollister@Sun.COM "sending '%s' because DS is '%s'", __func__,
510911048SDavid.Hollister@Sun.COM pmcs_tmf2str(tmf), pmcs_status_str
511010696SDavid.Hollister@Sun.COM (PMCOUT_STATUS_IO_DS_NON_OPERATIONAL));
511110696SDavid.Hollister@Sun.COM return (EIO);
511210696SDavid.Hollister@Sun.COM }
511310696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
511410696SDavid.Hollister@Sun.COM }
511510696SDavid.Hollister@Sun.COM
511611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
511710696SDavid.Hollister@Sun.COM "%s: sending '%s' to %s (lun %llu) tag 0x%x", __func__,
511810696SDavid.Hollister@Sun.COM pmcs_tmf2str(tmf), pptr->path, (unsigned long long) lun, tag);
511910696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_ONCHIP;
512010696SDavid.Hollister@Sun.COM INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
512110696SDavid.Hollister@Sun.COM
512210696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
512310696SDavid.Hollister@Sun.COM /*
512410696SDavid.Hollister@Sun.COM * This is a command sent to the target device, so it can take
512510696SDavid.Hollister@Sun.COM * significant amount of time to complete when path & device is busy.
512610696SDavid.Hollister@Sun.COM * Set a timeout to 20 seconds
512710696SDavid.Hollister@Sun.COM */
512810696SDavid.Hollister@Sun.COM WAIT_FOR(pwrk, 20000, result);
512912462Sjesse.butler@oracle.com pmcs_pwork(pwp, pwrk);
513010696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
513112462Sjesse.butler@oracle.com xp = pptr->target;
513210696SDavid.Hollister@Sun.COM
513310696SDavid.Hollister@Sun.COM if (result) {
513410696SDavid.Hollister@Sun.COM if (xp == NULL) {
513510696SDavid.Hollister@Sun.COM return (ETIMEDOUT);
513610696SDavid.Hollister@Sun.COM }
513710696SDavid.Hollister@Sun.COM
513810696SDavid.Hollister@Sun.COM mutex_enter(&xp->statlock);
513910696SDavid.Hollister@Sun.COM pmcs_start_dev_state_recovery(xp, pptr);
514010696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
514110696SDavid.Hollister@Sun.COM return (ETIMEDOUT);
514210696SDavid.Hollister@Sun.COM }
514310696SDavid.Hollister@Sun.COM
514410696SDavid.Hollister@Sun.COM status = LE_32(msg[2]);
514510696SDavid.Hollister@Sun.COM if (status != PMCOUT_STATUS_OK) {
514611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
514710696SDavid.Hollister@Sun.COM "%s: status %s for TMF %s action to %s, lun %llu",
514810696SDavid.Hollister@Sun.COM __func__, pmcs_status_str(status), pmcs_tmf2str(tmf),
514910696SDavid.Hollister@Sun.COM pptr->path, (unsigned long long) lun);
515010696SDavid.Hollister@Sun.COM if ((status == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) ||
515110696SDavid.Hollister@Sun.COM (status == PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK) ||
515210696SDavid.Hollister@Sun.COM (status == PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS)) {
515310696SDavid.Hollister@Sun.COM ds = PMCS_DEVICE_STATE_NON_OPERATIONAL;
515410696SDavid.Hollister@Sun.COM } else if (status == PMCOUT_STATUS_IO_DS_IN_RECOVERY) {
515510696SDavid.Hollister@Sun.COM /*
515610696SDavid.Hollister@Sun.COM * If the status is IN_RECOVERY, it's an indication
515710696SDavid.Hollister@Sun.COM * that it's now time for us to request to have the
515810696SDavid.Hollister@Sun.COM * device state set to OPERATIONAL since we're the ones
515910696SDavid.Hollister@Sun.COM * that requested recovery to begin with.
516010696SDavid.Hollister@Sun.COM */
516110696SDavid.Hollister@Sun.COM ds = PMCS_DEVICE_STATE_OPERATIONAL;
516210696SDavid.Hollister@Sun.COM } else {
516310696SDavid.Hollister@Sun.COM ds = PMCS_DEVICE_STATE_IN_RECOVERY;
516410696SDavid.Hollister@Sun.COM }
516510696SDavid.Hollister@Sun.COM if (xp != NULL) {
516610696SDavid.Hollister@Sun.COM mutex_enter(&xp->statlock);
516710696SDavid.Hollister@Sun.COM if (xp->dev_state != ds) {
516811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
516910696SDavid.Hollister@Sun.COM "%s: Sending err recovery cmd"
517010696SDavid.Hollister@Sun.COM " for tgt 0x%p (status = %s)",
517110696SDavid.Hollister@Sun.COM __func__, (void *)xp,
517210696SDavid.Hollister@Sun.COM pmcs_status_str(status));
517311090SDavid.Hollister@Sun.COM (void) pmcs_send_err_recovery_cmd(pwp, ds,
517411090SDavid.Hollister@Sun.COM pptr, xp);
517510696SDavid.Hollister@Sun.COM }
517610696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
517710696SDavid.Hollister@Sun.COM }
517810696SDavid.Hollister@Sun.COM return (EIO);
517910696SDavid.Hollister@Sun.COM } else {
518010696SDavid.Hollister@Sun.COM ds = PMCS_DEVICE_STATE_OPERATIONAL;
518110696SDavid.Hollister@Sun.COM if (xp != NULL) {
518210696SDavid.Hollister@Sun.COM mutex_enter(&xp->statlock);
518310696SDavid.Hollister@Sun.COM if (xp->dev_state != ds) {
518411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
518510696SDavid.Hollister@Sun.COM "%s: Sending err recovery cmd"
518610696SDavid.Hollister@Sun.COM " for tgt 0x%p (status = %s)",
518710696SDavid.Hollister@Sun.COM __func__, (void *)xp,
518810696SDavid.Hollister@Sun.COM pmcs_status_str(status));
518911090SDavid.Hollister@Sun.COM (void) pmcs_send_err_recovery_cmd(pwp, ds,
519011090SDavid.Hollister@Sun.COM pptr, xp);
519110696SDavid.Hollister@Sun.COM }
519210696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
519310696SDavid.Hollister@Sun.COM }
519410696SDavid.Hollister@Sun.COM }
519510696SDavid.Hollister@Sun.COM if (LE_32(msg[3]) == 0) {
519611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
519711048SDavid.Hollister@Sun.COM "TMF completed with no response");
519810696SDavid.Hollister@Sun.COM return (EIO);
519910696SDavid.Hollister@Sun.COM }
520010696SDavid.Hollister@Sun.COM pmcs_endian_transform(pwp, local, &msg[5], ssp_rsp_evec);
520110696SDavid.Hollister@Sun.COM xd = (uint8_t *)(&msg[5]);
520210696SDavid.Hollister@Sun.COM xd += SAS_RSP_HDR_SIZE;
520310696SDavid.Hollister@Sun.COM if (rptr->datapres != SAS_RSP_DATAPRES_RESPONSE_DATA) {
520411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
520510696SDavid.Hollister@Sun.COM "%s: TMF response not RESPONSE DATA (0x%x)",
520610696SDavid.Hollister@Sun.COM __func__, rptr->datapres);
520710696SDavid.Hollister@Sun.COM return (EIO);
520810696SDavid.Hollister@Sun.COM }
520910696SDavid.Hollister@Sun.COM if (rptr->response_data_length != 4) {
521010696SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
521110696SDavid.Hollister@Sun.COM "Bad SAS RESPONSE DATA LENGTH", msg);
521210696SDavid.Hollister@Sun.COM return (EIO);
521310696SDavid.Hollister@Sun.COM }
521410696SDavid.Hollister@Sun.COM (void) memcpy(&status, xd, sizeof (uint32_t));
521510696SDavid.Hollister@Sun.COM status = BE_32(status);
521610696SDavid.Hollister@Sun.COM if (response != NULL)
521710696SDavid.Hollister@Sun.COM *response = status;
521810696SDavid.Hollister@Sun.COM /*
521910696SDavid.Hollister@Sun.COM * The status is actually in the low-order byte. The upper three
522010696SDavid.Hollister@Sun.COM * bytes contain additional information for the TMFs that support them.
522110696SDavid.Hollister@Sun.COM * However, at this time we do not issue any of those. In the other
522210696SDavid.Hollister@Sun.COM * cases, the upper three bytes are supposed to be 0, but it appears
522310696SDavid.Hollister@Sun.COM * they aren't always. Just mask them off.
522410696SDavid.Hollister@Sun.COM */
522510696SDavid.Hollister@Sun.COM switch (status & 0xff) {
522610696SDavid.Hollister@Sun.COM case SAS_RSP_TMF_COMPLETE:
522711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
522811048SDavid.Hollister@Sun.COM "%s: TMF complete", __func__);
522910696SDavid.Hollister@Sun.COM result = 0;
523010696SDavid.Hollister@Sun.COM break;
523110696SDavid.Hollister@Sun.COM case SAS_RSP_TMF_SUCCEEDED:
523211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
523311048SDavid.Hollister@Sun.COM "%s: TMF succeeded", __func__);
523410696SDavid.Hollister@Sun.COM result = 0;
523510696SDavid.Hollister@Sun.COM break;
523610696SDavid.Hollister@Sun.COM case SAS_RSP_INVALID_FRAME:
523711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
523810696SDavid.Hollister@Sun.COM "%s: TMF returned INVALID FRAME", __func__);
523910696SDavid.Hollister@Sun.COM result = EIO;
524010696SDavid.Hollister@Sun.COM break;
524110696SDavid.Hollister@Sun.COM case SAS_RSP_TMF_NOT_SUPPORTED:
524211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
524310696SDavid.Hollister@Sun.COM "%s: TMF returned TMF NOT SUPPORTED", __func__);
524410696SDavid.Hollister@Sun.COM result = EIO;
524510696SDavid.Hollister@Sun.COM break;
524610696SDavid.Hollister@Sun.COM case SAS_RSP_TMF_FAILED:
524711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
524810696SDavid.Hollister@Sun.COM "%s: TMF returned TMF FAILED", __func__);
524910696SDavid.Hollister@Sun.COM result = EIO;
525010696SDavid.Hollister@Sun.COM break;
525110696SDavid.Hollister@Sun.COM case SAS_RSP_TMF_INCORRECT_LUN:
525211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
525310696SDavid.Hollister@Sun.COM "%s: TMF returned INCORRECT LUN", __func__);
525410696SDavid.Hollister@Sun.COM result = EIO;
525510696SDavid.Hollister@Sun.COM break;
525610696SDavid.Hollister@Sun.COM case SAS_RSP_OVERLAPPED_OIPTTA:
525711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
525810696SDavid.Hollister@Sun.COM "%s: TMF returned OVERLAPPED INITIATOR PORT TRANSFER TAG "
525910696SDavid.Hollister@Sun.COM "ATTEMPTED", __func__);
526010696SDavid.Hollister@Sun.COM result = EIO;
526110696SDavid.Hollister@Sun.COM break;
526210696SDavid.Hollister@Sun.COM default:
526311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
526410696SDavid.Hollister@Sun.COM "%s: TMF returned unknown code 0x%x", __func__, status);
526510696SDavid.Hollister@Sun.COM result = EIO;
526610696SDavid.Hollister@Sun.COM break;
526710696SDavid.Hollister@Sun.COM }
526810696SDavid.Hollister@Sun.COM return (result);
526910696SDavid.Hollister@Sun.COM }
527010696SDavid.Hollister@Sun.COM
527110696SDavid.Hollister@Sun.COM /*
527210696SDavid.Hollister@Sun.COM * Called with PHY lock held and scratch acquired
527310696SDavid.Hollister@Sun.COM */
527410696SDavid.Hollister@Sun.COM int
pmcs_sata_abort_ncq(pmcs_hw_t * pwp,pmcs_phy_t * pptr)527510696SDavid.Hollister@Sun.COM pmcs_sata_abort_ncq(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
527610696SDavid.Hollister@Sun.COM {
527710696SDavid.Hollister@Sun.COM const char *utag_fail_fmt = "%s: untagged NCQ command failure";
527810696SDavid.Hollister@Sun.COM const char *tag_fail_fmt = "%s: NCQ command failure (tag 0x%x)";
527910696SDavid.Hollister@Sun.COM uint32_t msg[PMCS_QENTRY_SIZE], *ptr, result, status;
528010696SDavid.Hollister@Sun.COM uint8_t *fp = pwp->scratch, ds;
528110696SDavid.Hollister@Sun.COM fis_t fis;
528210696SDavid.Hollister@Sun.COM pmcwork_t *pwrk;
528310696SDavid.Hollister@Sun.COM pmcs_xscsi_t *tgt;
528410696SDavid.Hollister@Sun.COM
528510696SDavid.Hollister@Sun.COM pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
528610696SDavid.Hollister@Sun.COM if (pwrk == NULL) {
528710696SDavid.Hollister@Sun.COM return (ENOMEM);
528810696SDavid.Hollister@Sun.COM }
528912258Ssrikanth.suravajhala@oracle.com pwrk->htag |= PMCS_TAG_NONIO_CMD;
529010696SDavid.Hollister@Sun.COM msg[0] = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE,
529110696SDavid.Hollister@Sun.COM PMCIN_SATA_HOST_IO_START));
529210696SDavid.Hollister@Sun.COM msg[1] = LE_32(pwrk->htag);
529310696SDavid.Hollister@Sun.COM msg[2] = LE_32(pptr->device_id);
529410696SDavid.Hollister@Sun.COM msg[3] = LE_32(512);
529510696SDavid.Hollister@Sun.COM msg[4] = LE_32(SATA_PROTOCOL_PIO | PMCIN_DATADIR_2_INI);
529610696SDavid.Hollister@Sun.COM msg[5] = LE_32((READ_LOG_EXT << 16) | (C_BIT << 8) | FIS_REG_H2DEV);
529710696SDavid.Hollister@Sun.COM msg[6] = LE_32(0x10);
529810696SDavid.Hollister@Sun.COM msg[8] = LE_32(1);
529910696SDavid.Hollister@Sun.COM msg[9] = 0;
530010696SDavid.Hollister@Sun.COM msg[10] = 0;
530110696SDavid.Hollister@Sun.COM msg[11] = 0;
530210696SDavid.Hollister@Sun.COM msg[12] = LE_32(DWORD0(pwp->scratch_dma));
530310696SDavid.Hollister@Sun.COM msg[13] = LE_32(DWORD1(pwp->scratch_dma));
530410696SDavid.Hollister@Sun.COM msg[14] = LE_32(512);
530510696SDavid.Hollister@Sun.COM msg[15] = 0;
530610696SDavid.Hollister@Sun.COM
530710696SDavid.Hollister@Sun.COM pwrk->arg = msg;
530810696SDavid.Hollister@Sun.COM pwrk->dtype = pptr->dtype;
530912462Sjesse.butler@oracle.com pwrk->xp = pptr->target;
531010696SDavid.Hollister@Sun.COM
531110696SDavid.Hollister@Sun.COM mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
531210696SDavid.Hollister@Sun.COM ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
531310696SDavid.Hollister@Sun.COM if (ptr == NULL) {
531410696SDavid.Hollister@Sun.COM mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
531510696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
531610696SDavid.Hollister@Sun.COM return (ENOMEM);
531710696SDavid.Hollister@Sun.COM }
531810696SDavid.Hollister@Sun.COM COPY_MESSAGE(ptr, msg, PMCS_QENTRY_SIZE);
531910696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_ONCHIP;
532010696SDavid.Hollister@Sun.COM INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
532110696SDavid.Hollister@Sun.COM
532210696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
532310696SDavid.Hollister@Sun.COM WAIT_FOR(pwrk, 250, result);
532412462Sjesse.butler@oracle.com pmcs_pwork(pwp, pwrk);
532510696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
532610696SDavid.Hollister@Sun.COM
532711048SDavid.Hollister@Sun.COM tgt = pptr->target;
532810696SDavid.Hollister@Sun.COM if (result) {
532911267SJesse.Butler@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, pmcs_timeo, __func__);
533010696SDavid.Hollister@Sun.COM return (EIO);
533110696SDavid.Hollister@Sun.COM }
533210696SDavid.Hollister@Sun.COM status = LE_32(msg[2]);
533310696SDavid.Hollister@Sun.COM if (status != PMCOUT_STATUS_OK || LE_32(msg[3])) {
533410696SDavid.Hollister@Sun.COM if (tgt == NULL) {
533511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
533610696SDavid.Hollister@Sun.COM "%s: cannot find target for phy 0x%p for "
533710696SDavid.Hollister@Sun.COM "dev state recovery", __func__, (void *)pptr);
533810696SDavid.Hollister@Sun.COM return (EIO);
533910696SDavid.Hollister@Sun.COM }
534010696SDavid.Hollister@Sun.COM
534110696SDavid.Hollister@Sun.COM mutex_enter(&tgt->statlock);
534210696SDavid.Hollister@Sun.COM
534310696SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG, "READ LOG EXT", msg);
534410696SDavid.Hollister@Sun.COM if ((status == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) ||
534510696SDavid.Hollister@Sun.COM (status == PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK) ||
534610696SDavid.Hollister@Sun.COM (status == PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS)) {
534710696SDavid.Hollister@Sun.COM ds = PMCS_DEVICE_STATE_NON_OPERATIONAL;
534810696SDavid.Hollister@Sun.COM } else {
534910696SDavid.Hollister@Sun.COM ds = PMCS_DEVICE_STATE_IN_RECOVERY;
535010696SDavid.Hollister@Sun.COM }
535110696SDavid.Hollister@Sun.COM if (tgt->dev_state != ds) {
535211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt, "%s: Trying "
535311048SDavid.Hollister@Sun.COM "SATA DS Recovery for tgt(0x%p) for status(%s)",
535410696SDavid.Hollister@Sun.COM __func__, (void *)tgt, pmcs_status_str(status));
535511090SDavid.Hollister@Sun.COM (void) pmcs_send_err_recovery_cmd(pwp, ds, pptr, tgt);
535610696SDavid.Hollister@Sun.COM }
535710696SDavid.Hollister@Sun.COM
535810696SDavid.Hollister@Sun.COM mutex_exit(&tgt->statlock);
535910696SDavid.Hollister@Sun.COM return (EIO);
536010696SDavid.Hollister@Sun.COM }
536110696SDavid.Hollister@Sun.COM fis[0] = (fp[4] << 24) | (fp[3] << 16) | (fp[2] << 8) | FIS_REG_D2H;
536210696SDavid.Hollister@Sun.COM fis[1] = (fp[8] << 24) | (fp[7] << 16) | (fp[6] << 8) | fp[5];
536310696SDavid.Hollister@Sun.COM fis[2] = (fp[12] << 24) | (fp[11] << 16) | (fp[10] << 8) | fp[9];
536410696SDavid.Hollister@Sun.COM fis[3] = (fp[16] << 24) | (fp[15] << 16) | (fp[14] << 8) | fp[13];
536510696SDavid.Hollister@Sun.COM fis[4] = 0;
536610696SDavid.Hollister@Sun.COM if (fp[0] & 0x80) {
536711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
536811048SDavid.Hollister@Sun.COM utag_fail_fmt, __func__);
536910696SDavid.Hollister@Sun.COM } else {
537011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, tgt,
537111048SDavid.Hollister@Sun.COM tag_fail_fmt, __func__, fp[0] & 0x1f);
537210696SDavid.Hollister@Sun.COM }
537310696SDavid.Hollister@Sun.COM pmcs_fis_dump(pwp, fis);
537410696SDavid.Hollister@Sun.COM pptr->need_rl_ext = 0;
537510696SDavid.Hollister@Sun.COM return (0);
537610696SDavid.Hollister@Sun.COM }
537710696SDavid.Hollister@Sun.COM
537810696SDavid.Hollister@Sun.COM /*
537910696SDavid.Hollister@Sun.COM * Transform a structure from CPU to Device endian format, or
538010696SDavid.Hollister@Sun.COM * vice versa, based upon a transformation vector.
538110696SDavid.Hollister@Sun.COM *
538210696SDavid.Hollister@Sun.COM * A transformation vector is an array of bytes, each byte
538310696SDavid.Hollister@Sun.COM * of which is defined thusly:
538410696SDavid.Hollister@Sun.COM *
538510696SDavid.Hollister@Sun.COM * bit 7: from CPU to desired endian, otherwise from desired endian
538610696SDavid.Hollister@Sun.COM * to CPU format
538710696SDavid.Hollister@Sun.COM * bit 6: Big Endian, else Little Endian
538810696SDavid.Hollister@Sun.COM * bits 5-4:
538910696SDavid.Hollister@Sun.COM * 00 Undefined
539010696SDavid.Hollister@Sun.COM * 01 One Byte quantities
539110696SDavid.Hollister@Sun.COM * 02 Two Byte quantities
539210696SDavid.Hollister@Sun.COM * 03 Four Byte quantities
539310696SDavid.Hollister@Sun.COM *
539410696SDavid.Hollister@Sun.COM * bits 3-0:
539510696SDavid.Hollister@Sun.COM * 00 Undefined
539610696SDavid.Hollister@Sun.COM * Number of quantities to transform
539710696SDavid.Hollister@Sun.COM *
539810696SDavid.Hollister@Sun.COM * The vector is terminated by a 0 value.
539910696SDavid.Hollister@Sun.COM */
540010696SDavid.Hollister@Sun.COM
540110696SDavid.Hollister@Sun.COM void
pmcs_endian_transform(pmcs_hw_t * pwp,void * orig_out,void * orig_in,const uint8_t * xfvec)540210696SDavid.Hollister@Sun.COM pmcs_endian_transform(pmcs_hw_t *pwp, void *orig_out, void *orig_in,
540310696SDavid.Hollister@Sun.COM const uint8_t *xfvec)
540410696SDavid.Hollister@Sun.COM {
540510696SDavid.Hollister@Sun.COM uint8_t c, *out = orig_out, *in = orig_in;
540610696SDavid.Hollister@Sun.COM
540710696SDavid.Hollister@Sun.COM if (xfvec == NULL) {
540811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
540911048SDavid.Hollister@Sun.COM "%s: null xfvec", __func__);
541010696SDavid.Hollister@Sun.COM return;
541110696SDavid.Hollister@Sun.COM }
541210696SDavid.Hollister@Sun.COM if (out == NULL) {
541311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
541411048SDavid.Hollister@Sun.COM "%s: null out", __func__);
541510696SDavid.Hollister@Sun.COM return;
541610696SDavid.Hollister@Sun.COM }
541710696SDavid.Hollister@Sun.COM if (in == NULL) {
541811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
541911048SDavid.Hollister@Sun.COM "%s: null in", __func__);
542010696SDavid.Hollister@Sun.COM return;
542110696SDavid.Hollister@Sun.COM }
542210696SDavid.Hollister@Sun.COM while ((c = *xfvec++) != 0) {
542310696SDavid.Hollister@Sun.COM int nbyt = (c & 0xf);
542410696SDavid.Hollister@Sun.COM int size = (c >> 4) & 0x3;
542510696SDavid.Hollister@Sun.COM int bige = (c >> 4) & 0x4;
542610696SDavid.Hollister@Sun.COM
542710696SDavid.Hollister@Sun.COM switch (size) {
542810696SDavid.Hollister@Sun.COM case 1:
542910696SDavid.Hollister@Sun.COM {
543010696SDavid.Hollister@Sun.COM while (nbyt-- > 0) {
543110696SDavid.Hollister@Sun.COM *out++ = *in++;
543210696SDavid.Hollister@Sun.COM }
543310696SDavid.Hollister@Sun.COM break;
543410696SDavid.Hollister@Sun.COM }
543510696SDavid.Hollister@Sun.COM case 2:
543610696SDavid.Hollister@Sun.COM {
543710696SDavid.Hollister@Sun.COM uint16_t tmp;
543810696SDavid.Hollister@Sun.COM while (nbyt-- > 0) {
543910696SDavid.Hollister@Sun.COM (void) memcpy(&tmp, in, sizeof (uint16_t));
544010696SDavid.Hollister@Sun.COM if (bige) {
544110696SDavid.Hollister@Sun.COM tmp = BE_16(tmp);
544210696SDavid.Hollister@Sun.COM } else {
544310696SDavid.Hollister@Sun.COM tmp = LE_16(tmp);
544410696SDavid.Hollister@Sun.COM }
544510696SDavid.Hollister@Sun.COM (void) memcpy(out, &tmp, sizeof (uint16_t));
544610696SDavid.Hollister@Sun.COM out += sizeof (uint16_t);
544710696SDavid.Hollister@Sun.COM in += sizeof (uint16_t);
544810696SDavid.Hollister@Sun.COM }
544910696SDavid.Hollister@Sun.COM break;
545010696SDavid.Hollister@Sun.COM }
545110696SDavid.Hollister@Sun.COM case 3:
545210696SDavid.Hollister@Sun.COM {
545310696SDavid.Hollister@Sun.COM uint32_t tmp;
545410696SDavid.Hollister@Sun.COM while (nbyt-- > 0) {
545510696SDavid.Hollister@Sun.COM (void) memcpy(&tmp, in, sizeof (uint32_t));
545610696SDavid.Hollister@Sun.COM if (bige) {
545710696SDavid.Hollister@Sun.COM tmp = BE_32(tmp);
545810696SDavid.Hollister@Sun.COM } else {
545910696SDavid.Hollister@Sun.COM tmp = LE_32(tmp);
546010696SDavid.Hollister@Sun.COM }
546110696SDavid.Hollister@Sun.COM (void) memcpy(out, &tmp, sizeof (uint32_t));
546210696SDavid.Hollister@Sun.COM out += sizeof (uint32_t);
546310696SDavid.Hollister@Sun.COM in += sizeof (uint32_t);
546410696SDavid.Hollister@Sun.COM }
546510696SDavid.Hollister@Sun.COM break;
546610696SDavid.Hollister@Sun.COM }
546710696SDavid.Hollister@Sun.COM default:
546811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
546911048SDavid.Hollister@Sun.COM "%s: bad size", __func__);
547010696SDavid.Hollister@Sun.COM return;
547110696SDavid.Hollister@Sun.COM }
547210696SDavid.Hollister@Sun.COM }
547310696SDavid.Hollister@Sun.COM }
547410696SDavid.Hollister@Sun.COM
547510696SDavid.Hollister@Sun.COM const char *
pmcs_get_rate(unsigned int linkrt)547610696SDavid.Hollister@Sun.COM pmcs_get_rate(unsigned int linkrt)
547710696SDavid.Hollister@Sun.COM {
547810696SDavid.Hollister@Sun.COM const char *rate;
547910696SDavid.Hollister@Sun.COM switch (linkrt) {
548010696SDavid.Hollister@Sun.COM case SAS_LINK_RATE_1_5GBIT:
548110696SDavid.Hollister@Sun.COM rate = "1.5";
548210696SDavid.Hollister@Sun.COM break;
548310696SDavid.Hollister@Sun.COM case SAS_LINK_RATE_3GBIT:
548410696SDavid.Hollister@Sun.COM rate = "3.0";
548510696SDavid.Hollister@Sun.COM break;
548610696SDavid.Hollister@Sun.COM case SAS_LINK_RATE_6GBIT:
548710696SDavid.Hollister@Sun.COM rate = "6.0";
548810696SDavid.Hollister@Sun.COM break;
548910696SDavid.Hollister@Sun.COM default:
549010696SDavid.Hollister@Sun.COM rate = "???";
549110696SDavid.Hollister@Sun.COM break;
549210696SDavid.Hollister@Sun.COM }
549310696SDavid.Hollister@Sun.COM return (rate);
549410696SDavid.Hollister@Sun.COM }
549510696SDavid.Hollister@Sun.COM
549610696SDavid.Hollister@Sun.COM const char *
pmcs_get_typename(pmcs_dtype_t type)549710696SDavid.Hollister@Sun.COM pmcs_get_typename(pmcs_dtype_t type)
549810696SDavid.Hollister@Sun.COM {
549910696SDavid.Hollister@Sun.COM switch (type) {
550010696SDavid.Hollister@Sun.COM case NOTHING:
550110696SDavid.Hollister@Sun.COM return ("NIL");
550210696SDavid.Hollister@Sun.COM case SATA:
550310696SDavid.Hollister@Sun.COM return ("SATA");
550410696SDavid.Hollister@Sun.COM case SAS:
550510696SDavid.Hollister@Sun.COM return ("SSP");
550610696SDavid.Hollister@Sun.COM case EXPANDER:
550710696SDavid.Hollister@Sun.COM return ("EXPANDER");
550810696SDavid.Hollister@Sun.COM }
550910696SDavid.Hollister@Sun.COM return ("????");
551010696SDavid.Hollister@Sun.COM }
551110696SDavid.Hollister@Sun.COM
551210696SDavid.Hollister@Sun.COM const char *
pmcs_tmf2str(int tmf)551310696SDavid.Hollister@Sun.COM pmcs_tmf2str(int tmf)
551410696SDavid.Hollister@Sun.COM {
551510696SDavid.Hollister@Sun.COM switch (tmf) {
551610696SDavid.Hollister@Sun.COM case SAS_ABORT_TASK:
551710696SDavid.Hollister@Sun.COM return ("Abort Task");
551810696SDavid.Hollister@Sun.COM case SAS_ABORT_TASK_SET:
551910696SDavid.Hollister@Sun.COM return ("Abort Task Set");
552010696SDavid.Hollister@Sun.COM case SAS_CLEAR_TASK_SET:
552110696SDavid.Hollister@Sun.COM return ("Clear Task Set");
552210696SDavid.Hollister@Sun.COM case SAS_LOGICAL_UNIT_RESET:
552310696SDavid.Hollister@Sun.COM return ("Logical Unit Reset");
552410696SDavid.Hollister@Sun.COM case SAS_I_T_NEXUS_RESET:
552510696SDavid.Hollister@Sun.COM return ("I_T Nexus Reset");
552610696SDavid.Hollister@Sun.COM case SAS_CLEAR_ACA:
552710696SDavid.Hollister@Sun.COM return ("Clear ACA");
552810696SDavid.Hollister@Sun.COM case SAS_QUERY_TASK:
552910696SDavid.Hollister@Sun.COM return ("Query Task");
553010696SDavid.Hollister@Sun.COM case SAS_QUERY_TASK_SET:
553110696SDavid.Hollister@Sun.COM return ("Query Task Set");
553210696SDavid.Hollister@Sun.COM case SAS_QUERY_UNIT_ATTENTION:
553310696SDavid.Hollister@Sun.COM return ("Query Unit Attention");
553410696SDavid.Hollister@Sun.COM default:
553510696SDavid.Hollister@Sun.COM return ("Unknown");
553610696SDavid.Hollister@Sun.COM }
553710696SDavid.Hollister@Sun.COM }
553810696SDavid.Hollister@Sun.COM
553910696SDavid.Hollister@Sun.COM const char *
pmcs_status_str(uint32_t status)554010696SDavid.Hollister@Sun.COM pmcs_status_str(uint32_t status)
554110696SDavid.Hollister@Sun.COM {
554210696SDavid.Hollister@Sun.COM switch (status) {
554310696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OK:
554410696SDavid.Hollister@Sun.COM return ("OK");
554510696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_ABORTED:
554610696SDavid.Hollister@Sun.COM return ("ABORTED");
554710696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OVERFLOW:
554810696SDavid.Hollister@Sun.COM return ("OVERFLOW");
554910696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_UNDERFLOW:
555010696SDavid.Hollister@Sun.COM return ("UNDERFLOW");
555110696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_FAILED:
555210696SDavid.Hollister@Sun.COM return ("FAILED");
555310696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_ABORT_RESET:
555410696SDavid.Hollister@Sun.COM return ("ABORT_RESET");
555510696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_NOT_VALID:
555610696SDavid.Hollister@Sun.COM return ("IO_NOT_VALID");
555710696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_NO_DEVICE:
555810696SDavid.Hollister@Sun.COM return ("NO_DEVICE");
555910696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_ILLEGAL_PARAMETER:
556010696SDavid.Hollister@Sun.COM return ("ILLEGAL_PARAMETER");
556110696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_LINK_FAILURE:
556210696SDavid.Hollister@Sun.COM return ("LINK_FAILURE");
556310696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_PROG_ERROR:
556410696SDavid.Hollister@Sun.COM return ("PROG_ERROR");
556510696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_EDC_IN_ERROR:
556610696SDavid.Hollister@Sun.COM return ("EDC_IN_ERROR");
556710696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_EDC_OUT_ERROR:
556810696SDavid.Hollister@Sun.COM return ("EDC_OUT_ERROR");
556910696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_ERROR_HW_TIMEOUT:
557010696SDavid.Hollister@Sun.COM return ("ERROR_HW_TIMEOUT");
557110696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERR_BREAK:
557210696SDavid.Hollister@Sun.COM return ("XFER_ERR_BREAK");
557310696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
557410696SDavid.Hollister@Sun.COM return ("XFER_ERR_PHY_NOT_READY");
557510696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED:
557610696SDavid.Hollister@Sun.COM return ("OPEN_CNX_PROTOCOL_NOT_SUPPORTED");
557710696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION:
557810696SDavid.Hollister@Sun.COM return ("OPEN_CNX_ERROR_ZONE_VIOLATION");
557910696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK:
558010696SDavid.Hollister@Sun.COM return ("OPEN_CNX_ERROR_BREAK");
558110696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
558210696SDavid.Hollister@Sun.COM return ("OPEN_CNX_ERROR_IT_NEXUS_LOSS");
558310696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION:
558410696SDavid.Hollister@Sun.COM return ("OPENCNX_ERROR_BAD_DESTINATION");
558510696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
558610696SDavid.Hollister@Sun.COM return ("OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED");
558710696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
558810696SDavid.Hollister@Sun.COM return ("OPEN_CNX_ERROR_STP_RESOURCES_BUSY");
558910696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION:
559010696SDavid.Hollister@Sun.COM return ("OPEN_CNX_ERROR_WRONG_DESTINATION");
559111601SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_ERROR:
559211601SDavid.Hollister@Sun.COM return ("OPEN_CNX_ERROR_UNKNOWN_ERROR");
559310696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED:
559410696SDavid.Hollister@Sun.COM return ("IO_XFER_ERROR_NAK_RECEIVED");
559510696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_ACK_NAK_TIMEOUT:
559610696SDavid.Hollister@Sun.COM return ("XFER_ERROR_ACK_NAK_TIMEOUT");
559710696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_PEER_ABORTED:
559810696SDavid.Hollister@Sun.COM return ("XFER_ERROR_PEER_ABORTED");
559910696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_RX_FRAME:
560010696SDavid.Hollister@Sun.COM return ("XFER_ERROR_RX_FRAME");
560110696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_XFER_ERROR_DMA:
560210696SDavid.Hollister@Sun.COM return ("IO_XFER_ERROR_DMA");
560310696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_CREDIT_TIMEOUT:
560410696SDavid.Hollister@Sun.COM return ("XFER_ERROR_CREDIT_TIMEOUT");
560510696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_SATA_LINK_TIMEOUT:
560610696SDavid.Hollister@Sun.COM return ("XFER_ERROR_SATA_LINK_TIMEOUT");
560710696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_SATA:
560810696SDavid.Hollister@Sun.COM return ("XFER_ERROR_SATA");
560910696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_REJECTED_NCQ_MODE:
561010696SDavid.Hollister@Sun.COM return ("XFER_ERROR_REJECTED_NCQ_MODE");
561110696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_ABORTED_DUE_TO_SRST:
561210696SDavid.Hollister@Sun.COM return ("XFER_ERROR_ABORTED_DUE_TO_SRST");
561310696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_ABORTED_NCQ_MODE:
561410696SDavid.Hollister@Sun.COM return ("XFER_ERROR_ABORTED_NCQ_MODE");
561510696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
561610696SDavid.Hollister@Sun.COM return ("IO_XFER_OPEN_RETRY_TIMEOUT");
561710696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR:
561810696SDavid.Hollister@Sun.COM return ("SMP_RESP_CONNECTION_ERROR");
561910696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_UNEXPECTED_PHASE:
562010696SDavid.Hollister@Sun.COM return ("XFER_ERROR_UNEXPECTED_PHASE");
562110696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_RDY_OVERRUN:
562210696SDavid.Hollister@Sun.COM return ("XFER_ERROR_RDY_OVERRUN");
562310696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_RDY_NOT_EXPECTED:
562410696SDavid.Hollister@Sun.COM return ("XFER_ERROR_RDY_NOT_EXPECTED");
562510696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT:
562610696SDavid.Hollister@Sun.COM return ("XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT");
562710696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK:
562810696SDavid.Hollister@Sun.COM return ("XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK");
562910696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK:
563010696SDavid.Hollister@Sun.COM return ("XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK");
563110696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_OFFSET_MISMATCH:
563210696SDavid.Hollister@Sun.COM return ("XFER_ERROR_OFFSET_MISMATCH");
563310696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_ZERO_DATA_LEN:
563410696SDavid.Hollister@Sun.COM return ("XFER_ERROR_ZERO_DATA_LEN");
563510696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED:
563610696SDavid.Hollister@Sun.COM return ("XFER_CMD_FRAME_ISSUED");
563710696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE:
563810696SDavid.Hollister@Sun.COM return ("ERROR_INTERNAL_SMP_RESOURCE");
563910696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_PORT_IN_RESET:
564010696SDavid.Hollister@Sun.COM return ("IO_PORT_IN_RESET");
564110696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL:
564210696SDavid.Hollister@Sun.COM return ("DEVICE STATE NON-OPERATIONAL");
564310696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_DS_IN_RECOVERY:
564410696SDavid.Hollister@Sun.COM return ("DEVICE STATE IN RECOVERY");
564511693SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
564611693SDavid.Hollister@Sun.COM return ("OPEN CNX ERR HW RESOURCE BUSY");
564710696SDavid.Hollister@Sun.COM default:
564810696SDavid.Hollister@Sun.COM return (NULL);
564910696SDavid.Hollister@Sun.COM }
565010696SDavid.Hollister@Sun.COM }
565110696SDavid.Hollister@Sun.COM
565210696SDavid.Hollister@Sun.COM uint64_t
pmcs_barray2wwn(uint8_t ba[8])565310696SDavid.Hollister@Sun.COM pmcs_barray2wwn(uint8_t ba[8])
565410696SDavid.Hollister@Sun.COM {
565510696SDavid.Hollister@Sun.COM uint64_t result = 0;
565610696SDavid.Hollister@Sun.COM int i;
565710696SDavid.Hollister@Sun.COM
565810696SDavid.Hollister@Sun.COM for (i = 0; i < 8; i++) {
565910696SDavid.Hollister@Sun.COM result <<= 8;
566010696SDavid.Hollister@Sun.COM result |= ba[i];
566110696SDavid.Hollister@Sun.COM }
566210696SDavid.Hollister@Sun.COM return (result);
566310696SDavid.Hollister@Sun.COM }
566410696SDavid.Hollister@Sun.COM
566510696SDavid.Hollister@Sun.COM void
pmcs_wwn2barray(uint64_t wwn,uint8_t ba[8])566610696SDavid.Hollister@Sun.COM pmcs_wwn2barray(uint64_t wwn, uint8_t ba[8])
566710696SDavid.Hollister@Sun.COM {
566810696SDavid.Hollister@Sun.COM int i;
566910696SDavid.Hollister@Sun.COM for (i = 0; i < 8; i++) {
567010696SDavid.Hollister@Sun.COM ba[7 - i] = wwn & 0xff;
567110696SDavid.Hollister@Sun.COM wwn >>= 8;
567210696SDavid.Hollister@Sun.COM }
567310696SDavid.Hollister@Sun.COM }
567410696SDavid.Hollister@Sun.COM
567510696SDavid.Hollister@Sun.COM void
pmcs_report_fwversion(pmcs_hw_t * pwp)567610696SDavid.Hollister@Sun.COM pmcs_report_fwversion(pmcs_hw_t *pwp)
567710696SDavid.Hollister@Sun.COM {
567810696SDavid.Hollister@Sun.COM const char *fwsupport;
567910696SDavid.Hollister@Sun.COM switch (PMCS_FW_TYPE(pwp)) {
568010696SDavid.Hollister@Sun.COM case PMCS_FW_TYPE_RELEASED:
568110696SDavid.Hollister@Sun.COM fwsupport = "Released";
568210696SDavid.Hollister@Sun.COM break;
568310696SDavid.Hollister@Sun.COM case PMCS_FW_TYPE_DEVELOPMENT:
568410696SDavid.Hollister@Sun.COM fwsupport = "Development";
568510696SDavid.Hollister@Sun.COM break;
568610696SDavid.Hollister@Sun.COM case PMCS_FW_TYPE_ALPHA:
568710696SDavid.Hollister@Sun.COM fwsupport = "Alpha";
568810696SDavid.Hollister@Sun.COM break;
568910696SDavid.Hollister@Sun.COM case PMCS_FW_TYPE_BETA:
569010696SDavid.Hollister@Sun.COM fwsupport = "Beta";
569110696SDavid.Hollister@Sun.COM break;
569210696SDavid.Hollister@Sun.COM default:
569310696SDavid.Hollister@Sun.COM fwsupport = "Special";
569410696SDavid.Hollister@Sun.COM break;
569510696SDavid.Hollister@Sun.COM }
569611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
569711980SDavid.Hollister@Sun.COM "Chip Revision: %c; F/W Revision %x.%x.%x %s (ILA rev %08x)",
569811980SDavid.Hollister@Sun.COM 'A' + pwp->chiprev, PMCS_FW_MAJOR(pwp), PMCS_FW_MINOR(pwp),
569911980SDavid.Hollister@Sun.COM PMCS_FW_MICRO(pwp), fwsupport, pwp->ila_ver);
570010696SDavid.Hollister@Sun.COM }
570110696SDavid.Hollister@Sun.COM
570210696SDavid.Hollister@Sun.COM void
pmcs_phy_name(pmcs_hw_t * pwp,pmcs_phy_t * pptr,char * obuf,size_t olen)570310696SDavid.Hollister@Sun.COM pmcs_phy_name(pmcs_hw_t *pwp, pmcs_phy_t *pptr, char *obuf, size_t olen)
570410696SDavid.Hollister@Sun.COM {
570510696SDavid.Hollister@Sun.COM if (pptr->parent) {
570610696SDavid.Hollister@Sun.COM pmcs_phy_name(pwp, pptr->parent, obuf, olen);
570710696SDavid.Hollister@Sun.COM (void) snprintf(obuf, olen, "%s.%02x", obuf, pptr->phynum);
570810696SDavid.Hollister@Sun.COM } else {
570910696SDavid.Hollister@Sun.COM (void) snprintf(obuf, olen, "pp%02x", pptr->phynum);
571010696SDavid.Hollister@Sun.COM }
571110696SDavid.Hollister@Sun.COM }
571210696SDavid.Hollister@Sun.COM
571310696SDavid.Hollister@Sun.COM /*
571410696SDavid.Hollister@Sun.COM * This function is called as a sanity check to ensure that a newly registered
571510696SDavid.Hollister@Sun.COM * PHY doesn't have a device_id that exists with another registered PHY.
571610696SDavid.Hollister@Sun.COM */
571710696SDavid.Hollister@Sun.COM static boolean_t
pmcs_validate_devid(pmcs_phy_t * parent,pmcs_phy_t * phyp,uint32_t device_id)571810696SDavid.Hollister@Sun.COM pmcs_validate_devid(pmcs_phy_t *parent, pmcs_phy_t *phyp, uint32_t device_id)
571910696SDavid.Hollister@Sun.COM {
572011601SDavid.Hollister@Sun.COM pmcs_phy_t *pptr, *pchild;
572110696SDavid.Hollister@Sun.COM boolean_t rval;
572210696SDavid.Hollister@Sun.COM
572310696SDavid.Hollister@Sun.COM pptr = parent;
572410696SDavid.Hollister@Sun.COM
572510696SDavid.Hollister@Sun.COM while (pptr) {
572610696SDavid.Hollister@Sun.COM if (pptr->valid_device_id && (pptr != phyp) &&
572710696SDavid.Hollister@Sun.COM (pptr->device_id == device_id)) {
572811601SDavid.Hollister@Sun.COM /*
572911601SDavid.Hollister@Sun.COM * This can still be OK if both of these PHYs actually
573011601SDavid.Hollister@Sun.COM * represent the same device (e.g. expander). It could
573111601SDavid.Hollister@Sun.COM * be a case of a new "primary" PHY. If the SAS address
573211601SDavid.Hollister@Sun.COM * is the same and they have the same parent, we'll
573311601SDavid.Hollister@Sun.COM * accept this if the PHY to be registered is the
573411601SDavid.Hollister@Sun.COM * primary.
573511601SDavid.Hollister@Sun.COM */
573611601SDavid.Hollister@Sun.COM if ((phyp->parent == pptr->parent) &&
573711601SDavid.Hollister@Sun.COM (memcmp(phyp->sas_address,
573811601SDavid.Hollister@Sun.COM pptr->sas_address, 8) == 0) && (phyp->width > 1)) {
573911601SDavid.Hollister@Sun.COM /*
574011601SDavid.Hollister@Sun.COM * Move children over to the new primary and
574111601SDavid.Hollister@Sun.COM * update both PHYs
574211601SDavid.Hollister@Sun.COM */
574311601SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
574411601SDavid.Hollister@Sun.COM phyp->children = pptr->children;
574511601SDavid.Hollister@Sun.COM pchild = phyp->children;
574611601SDavid.Hollister@Sun.COM while (pchild) {
574711601SDavid.Hollister@Sun.COM pchild->parent = phyp;
574811601SDavid.Hollister@Sun.COM pchild = pchild->sibling;
574911601SDavid.Hollister@Sun.COM }
575011601SDavid.Hollister@Sun.COM phyp->subsidiary = 0;
575111601SDavid.Hollister@Sun.COM phyp->ncphy = pptr->ncphy;
575211601SDavid.Hollister@Sun.COM /*
575311601SDavid.Hollister@Sun.COM * device_id, valid_device_id, and configured
575411601SDavid.Hollister@Sun.COM * will be set by the caller
575511601SDavid.Hollister@Sun.COM */
575611601SDavid.Hollister@Sun.COM pptr->children = NULL;
575711601SDavid.Hollister@Sun.COM pptr->subsidiary = 1;
575811601SDavid.Hollister@Sun.COM pptr->ncphy = 0;
575911601SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
576011601SDavid.Hollister@Sun.COM pmcs_prt(pptr->pwp, PMCS_PRT_DEBUG, pptr, NULL,
576111601SDavid.Hollister@Sun.COM "%s: Moving device_id %d from PHY %s to %s",
576211601SDavid.Hollister@Sun.COM __func__, device_id, pptr->path,
576311601SDavid.Hollister@Sun.COM phyp->path);
576411601SDavid.Hollister@Sun.COM return (B_TRUE);
576511601SDavid.Hollister@Sun.COM }
576611048SDavid.Hollister@Sun.COM pmcs_prt(pptr->pwp, PMCS_PRT_DEBUG, pptr, NULL,
576710696SDavid.Hollister@Sun.COM "%s: phy %s already exists as %s with "
576810696SDavid.Hollister@Sun.COM "device id 0x%x", __func__, phyp->path,
576910696SDavid.Hollister@Sun.COM pptr->path, device_id);
577010696SDavid.Hollister@Sun.COM return (B_FALSE);
577110696SDavid.Hollister@Sun.COM }
577210696SDavid.Hollister@Sun.COM
577310696SDavid.Hollister@Sun.COM if (pptr->children) {
577410696SDavid.Hollister@Sun.COM rval = pmcs_validate_devid(pptr->children, phyp,
577510696SDavid.Hollister@Sun.COM device_id);
577610696SDavid.Hollister@Sun.COM if (rval == B_FALSE) {
577710696SDavid.Hollister@Sun.COM return (rval);
577810696SDavid.Hollister@Sun.COM }
577910696SDavid.Hollister@Sun.COM }
578010696SDavid.Hollister@Sun.COM
578110696SDavid.Hollister@Sun.COM pptr = pptr->sibling;
578210696SDavid.Hollister@Sun.COM }
578310696SDavid.Hollister@Sun.COM
578410696SDavid.Hollister@Sun.COM /* This PHY and device_id are valid */
578510696SDavid.Hollister@Sun.COM return (B_TRUE);
578610696SDavid.Hollister@Sun.COM }
578710696SDavid.Hollister@Sun.COM
578810696SDavid.Hollister@Sun.COM /*
578910696SDavid.Hollister@Sun.COM * If the PHY is found, it is returned locked
579010696SDavid.Hollister@Sun.COM */
579110696SDavid.Hollister@Sun.COM static pmcs_phy_t *
pmcs_find_phy_by_wwn_impl(pmcs_phy_t * phyp,uint8_t * wwn)579210696SDavid.Hollister@Sun.COM pmcs_find_phy_by_wwn_impl(pmcs_phy_t *phyp, uint8_t *wwn)
579310696SDavid.Hollister@Sun.COM {
579410696SDavid.Hollister@Sun.COM pmcs_phy_t *matched_phy, *cphyp, *nphyp;
579510696SDavid.Hollister@Sun.COM
579610696SDavid.Hollister@Sun.COM ASSERT(!mutex_owned(&phyp->phy_lock));
579710696SDavid.Hollister@Sun.COM
579810696SDavid.Hollister@Sun.COM while (phyp) {
579910696SDavid.Hollister@Sun.COM pmcs_lock_phy(phyp);
580010696SDavid.Hollister@Sun.COM
580110696SDavid.Hollister@Sun.COM if (phyp->valid_device_id) {
580210696SDavid.Hollister@Sun.COM if (memcmp(phyp->sas_address, wwn, 8) == 0) {
580310696SDavid.Hollister@Sun.COM return (phyp);
580410696SDavid.Hollister@Sun.COM }
580510696SDavid.Hollister@Sun.COM }
580610696SDavid.Hollister@Sun.COM
580710696SDavid.Hollister@Sun.COM if (phyp->children) {
580810696SDavid.Hollister@Sun.COM cphyp = phyp->children;
580910696SDavid.Hollister@Sun.COM pmcs_unlock_phy(phyp);
581010696SDavid.Hollister@Sun.COM matched_phy = pmcs_find_phy_by_wwn_impl(cphyp, wwn);
581110696SDavid.Hollister@Sun.COM if (matched_phy) {
581210696SDavid.Hollister@Sun.COM ASSERT(mutex_owned(&matched_phy->phy_lock));
581310696SDavid.Hollister@Sun.COM return (matched_phy);
581410696SDavid.Hollister@Sun.COM }
581510696SDavid.Hollister@Sun.COM pmcs_lock_phy(phyp);
581610696SDavid.Hollister@Sun.COM }
581710696SDavid.Hollister@Sun.COM
581810696SDavid.Hollister@Sun.COM /*
581910696SDavid.Hollister@Sun.COM * Only iterate through non-root PHYs
582010696SDavid.Hollister@Sun.COM */
582110696SDavid.Hollister@Sun.COM if (IS_ROOT_PHY(phyp)) {
582210696SDavid.Hollister@Sun.COM pmcs_unlock_phy(phyp);
582310696SDavid.Hollister@Sun.COM phyp = NULL;
582410696SDavid.Hollister@Sun.COM } else {
582510696SDavid.Hollister@Sun.COM nphyp = phyp->sibling;
582610696SDavid.Hollister@Sun.COM pmcs_unlock_phy(phyp);
582710696SDavid.Hollister@Sun.COM phyp = nphyp;
582810696SDavid.Hollister@Sun.COM }
582910696SDavid.Hollister@Sun.COM }
583010696SDavid.Hollister@Sun.COM
583110696SDavid.Hollister@Sun.COM return (NULL);
583210696SDavid.Hollister@Sun.COM }
583310696SDavid.Hollister@Sun.COM
583410696SDavid.Hollister@Sun.COM pmcs_phy_t *
pmcs_find_phy_by_wwn(pmcs_hw_t * pwp,uint64_t wwn)583510696SDavid.Hollister@Sun.COM pmcs_find_phy_by_wwn(pmcs_hw_t *pwp, uint64_t wwn)
583610696SDavid.Hollister@Sun.COM {
583710696SDavid.Hollister@Sun.COM uint8_t ebstr[8];
583810696SDavid.Hollister@Sun.COM pmcs_phy_t *pptr, *matched_phy;
583910696SDavid.Hollister@Sun.COM
584010696SDavid.Hollister@Sun.COM pmcs_wwn2barray(wwn, ebstr);
584110696SDavid.Hollister@Sun.COM
584210696SDavid.Hollister@Sun.COM pptr = pwp->root_phys;
584310696SDavid.Hollister@Sun.COM while (pptr) {
584410696SDavid.Hollister@Sun.COM matched_phy = pmcs_find_phy_by_wwn_impl(pptr, ebstr);
584510696SDavid.Hollister@Sun.COM if (matched_phy) {
584610696SDavid.Hollister@Sun.COM ASSERT(mutex_owned(&matched_phy->phy_lock));
584710696SDavid.Hollister@Sun.COM return (matched_phy);
584810696SDavid.Hollister@Sun.COM }
584910696SDavid.Hollister@Sun.COM
585010696SDavid.Hollister@Sun.COM pptr = pptr->sibling;
585110696SDavid.Hollister@Sun.COM }
585210696SDavid.Hollister@Sun.COM
585310696SDavid.Hollister@Sun.COM return (NULL);
585410696SDavid.Hollister@Sun.COM }
585510696SDavid.Hollister@Sun.COM
585610696SDavid.Hollister@Sun.COM
585710696SDavid.Hollister@Sun.COM /*
585810696SDavid.Hollister@Sun.COM * pmcs_find_phy_by_sas_address
585910696SDavid.Hollister@Sun.COM *
586010696SDavid.Hollister@Sun.COM * Find a PHY that both matches "sas_addr" and is on "iport".
586110696SDavid.Hollister@Sun.COM * If a matching PHY is found, it is returned locked.
586210696SDavid.Hollister@Sun.COM */
586310696SDavid.Hollister@Sun.COM pmcs_phy_t *
pmcs_find_phy_by_sas_address(pmcs_hw_t * pwp,pmcs_iport_t * iport,pmcs_phy_t * root,char * sas_addr)586410696SDavid.Hollister@Sun.COM pmcs_find_phy_by_sas_address(pmcs_hw_t *pwp, pmcs_iport_t *iport,
586510696SDavid.Hollister@Sun.COM pmcs_phy_t *root, char *sas_addr)
586610696SDavid.Hollister@Sun.COM {
586710696SDavid.Hollister@Sun.COM int ua_form = 1;
586810696SDavid.Hollister@Sun.COM uint64_t wwn;
586910696SDavid.Hollister@Sun.COM char addr[PMCS_MAX_UA_SIZE];
587010696SDavid.Hollister@Sun.COM pmcs_phy_t *pptr, *pnext, *pchild;
587110696SDavid.Hollister@Sun.COM
587210696SDavid.Hollister@Sun.COM if (root == NULL) {
587310696SDavid.Hollister@Sun.COM pptr = pwp->root_phys;
587410696SDavid.Hollister@Sun.COM } else {
587510696SDavid.Hollister@Sun.COM pptr = root;
587610696SDavid.Hollister@Sun.COM }
587710696SDavid.Hollister@Sun.COM
587810696SDavid.Hollister@Sun.COM while (pptr) {
587910696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
588010696SDavid.Hollister@Sun.COM /*
588110696SDavid.Hollister@Sun.COM * If the PHY is dead or does not have a valid device ID,
588210696SDavid.Hollister@Sun.COM * skip it.
588310696SDavid.Hollister@Sun.COM */
588410696SDavid.Hollister@Sun.COM if ((pptr->dead) || (!pptr->valid_device_id)) {
588510696SDavid.Hollister@Sun.COM goto next_phy;
588610696SDavid.Hollister@Sun.COM }
588710696SDavid.Hollister@Sun.COM
588810696SDavid.Hollister@Sun.COM if (pptr->iport != iport) {
588910696SDavid.Hollister@Sun.COM goto next_phy;
589010696SDavid.Hollister@Sun.COM }
589110696SDavid.Hollister@Sun.COM
589210696SDavid.Hollister@Sun.COM wwn = pmcs_barray2wwn(pptr->sas_address);
589310696SDavid.Hollister@Sun.COM (void *) scsi_wwn_to_wwnstr(wwn, ua_form, addr);
589410696SDavid.Hollister@Sun.COM if (strncmp(addr, sas_addr, strlen(addr)) == 0) {
589510696SDavid.Hollister@Sun.COM return (pptr);
589610696SDavid.Hollister@Sun.COM }
589710696SDavid.Hollister@Sun.COM
589810696SDavid.Hollister@Sun.COM if (pptr->children) {
589910696SDavid.Hollister@Sun.COM pchild = pptr->children;
590010696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
590110696SDavid.Hollister@Sun.COM pnext = pmcs_find_phy_by_sas_address(pwp, iport, pchild,
590210696SDavid.Hollister@Sun.COM sas_addr);
590310696SDavid.Hollister@Sun.COM if (pnext) {
590410696SDavid.Hollister@Sun.COM return (pnext);
590510696SDavid.Hollister@Sun.COM }
590610696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
590710696SDavid.Hollister@Sun.COM }
590810696SDavid.Hollister@Sun.COM
590910696SDavid.Hollister@Sun.COM next_phy:
591010696SDavid.Hollister@Sun.COM pnext = pptr->sibling;
591110696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
591210696SDavid.Hollister@Sun.COM pptr = pnext;
591310696SDavid.Hollister@Sun.COM }
591410696SDavid.Hollister@Sun.COM
591510696SDavid.Hollister@Sun.COM return (NULL);
591610696SDavid.Hollister@Sun.COM }
591710696SDavid.Hollister@Sun.COM
591810696SDavid.Hollister@Sun.COM void
pmcs_fis_dump(pmcs_hw_t * pwp,fis_t fis)591910696SDavid.Hollister@Sun.COM pmcs_fis_dump(pmcs_hw_t *pwp, fis_t fis)
592010696SDavid.Hollister@Sun.COM {
592110696SDavid.Hollister@Sun.COM switch (fis[0] & 0xff) {
592210696SDavid.Hollister@Sun.COM case FIS_REG_H2DEV:
592311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
592411048SDavid.Hollister@Sun.COM "FIS REGISTER HOST TO DEVICE: "
592510696SDavid.Hollister@Sun.COM "OP=0x%02x Feature=0x%04x Count=0x%04x Device=0x%02x "
592610696SDavid.Hollister@Sun.COM "LBA=%llu", BYTE2(fis[0]), BYTE3(fis[2]) << 8 |
592710696SDavid.Hollister@Sun.COM BYTE3(fis[0]), WORD0(fis[3]), BYTE3(fis[1]),
592810696SDavid.Hollister@Sun.COM (unsigned long long)
592910696SDavid.Hollister@Sun.COM (((uint64_t)fis[2] & 0x00ffffff) << 24 |
593010696SDavid.Hollister@Sun.COM ((uint64_t)fis[1] & 0x00ffffff)));
593110696SDavid.Hollister@Sun.COM break;
593210696SDavid.Hollister@Sun.COM case FIS_REG_D2H:
593311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
593411048SDavid.Hollister@Sun.COM "FIS REGISTER DEVICE TO HOST: Status=0x%02x "
593511048SDavid.Hollister@Sun.COM "Error=0x%02x Dev=0x%02x Count=0x%04x LBA=%llu",
593610696SDavid.Hollister@Sun.COM BYTE2(fis[0]), BYTE3(fis[0]), BYTE3(fis[1]), WORD0(fis[3]),
593710696SDavid.Hollister@Sun.COM (unsigned long long)(((uint64_t)fis[2] & 0x00ffffff) << 24 |
593810696SDavid.Hollister@Sun.COM ((uint64_t)fis[1] & 0x00ffffff)));
593910696SDavid.Hollister@Sun.COM break;
594010696SDavid.Hollister@Sun.COM default:
594111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
594211693SDavid.Hollister@Sun.COM "FIS: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x",
594311693SDavid.Hollister@Sun.COM fis[0], fis[1], fis[2], fis[3], fis[4]);
594410696SDavid.Hollister@Sun.COM break;
594510696SDavid.Hollister@Sun.COM }
594610696SDavid.Hollister@Sun.COM }
594710696SDavid.Hollister@Sun.COM
594810696SDavid.Hollister@Sun.COM void
pmcs_print_entry(pmcs_hw_t * pwp,int level,char * msg,void * arg)594910696SDavid.Hollister@Sun.COM pmcs_print_entry(pmcs_hw_t *pwp, int level, char *msg, void *arg)
595010696SDavid.Hollister@Sun.COM {
595110696SDavid.Hollister@Sun.COM uint32_t *mb = arg;
595210696SDavid.Hollister@Sun.COM size_t i;
595310696SDavid.Hollister@Sun.COM
595411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, level, NULL, NULL, msg);
595510696SDavid.Hollister@Sun.COM for (i = 0; i < (PMCS_QENTRY_SIZE / sizeof (uint32_t)); i += 4) {
595611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, level, NULL, NULL,
595711048SDavid.Hollister@Sun.COM "Offset %2lu: 0x%08x 0x%08x 0x%08x 0x%08x",
595811048SDavid.Hollister@Sun.COM i * sizeof (uint32_t), LE_32(mb[i]),
595911048SDavid.Hollister@Sun.COM LE_32(mb[i+1]), LE_32(mb[i+2]), LE_32(mb[i+3]));
596010696SDavid.Hollister@Sun.COM }
596110696SDavid.Hollister@Sun.COM }
596210696SDavid.Hollister@Sun.COM
596310696SDavid.Hollister@Sun.COM /*
596410696SDavid.Hollister@Sun.COM * If phyp == NULL we're being called from the worker thread, in which
596510696SDavid.Hollister@Sun.COM * case we need to check all the PHYs. In this case, the softstate lock
596610696SDavid.Hollister@Sun.COM * will be held.
596710696SDavid.Hollister@Sun.COM * If phyp is non-NULL, just issue the spinup release for the specified PHY
596810696SDavid.Hollister@Sun.COM * (which will already be locked).
596910696SDavid.Hollister@Sun.COM */
597010696SDavid.Hollister@Sun.COM void
pmcs_spinup_release(pmcs_hw_t * pwp,pmcs_phy_t * phyp)597110696SDavid.Hollister@Sun.COM pmcs_spinup_release(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
597210696SDavid.Hollister@Sun.COM {
597310696SDavid.Hollister@Sun.COM uint32_t *msg;
597410696SDavid.Hollister@Sun.COM struct pmcwork *pwrk;
597510696SDavid.Hollister@Sun.COM pmcs_phy_t *tphyp;
597610696SDavid.Hollister@Sun.COM
597710696SDavid.Hollister@Sun.COM if (phyp != NULL) {
597810696SDavid.Hollister@Sun.COM ASSERT(mutex_owned(&phyp->phy_lock));
597911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, NULL,
598010696SDavid.Hollister@Sun.COM "%s: Issuing spinup release only for PHY %s", __func__,
598110696SDavid.Hollister@Sun.COM phyp->path);
598210696SDavid.Hollister@Sun.COM mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
598310696SDavid.Hollister@Sun.COM msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
598410696SDavid.Hollister@Sun.COM if (msg == NULL || (pwrk =
598510696SDavid.Hollister@Sun.COM pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) {
598610696SDavid.Hollister@Sun.COM mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
598710696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_SPINUP_RELEASE);
598810696SDavid.Hollister@Sun.COM return;
598910696SDavid.Hollister@Sun.COM }
599010696SDavid.Hollister@Sun.COM
599110696SDavid.Hollister@Sun.COM phyp->spinup_hold = 0;
599210696SDavid.Hollister@Sun.COM bzero(msg, PMCS_QENTRY_SIZE);
599312258Ssrikanth.suravajhala@oracle.com pwrk->htag |= PMCS_TAG_NONIO_CMD;
599410696SDavid.Hollister@Sun.COM msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
599510696SDavid.Hollister@Sun.COM PMCIN_LOCAL_PHY_CONTROL));
599610696SDavid.Hollister@Sun.COM msg[1] = LE_32(pwrk->htag);
599710696SDavid.Hollister@Sun.COM msg[2] = LE_32((0x10 << 8) | phyp->phynum);
599810696SDavid.Hollister@Sun.COM
599910696SDavid.Hollister@Sun.COM pwrk->dtype = phyp->dtype;
600010696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_ONCHIP;
600112462Sjesse.butler@oracle.com pwrk->xp = phyp->target;
600210696SDavid.Hollister@Sun.COM mutex_exit(&pwrk->lock);
600310696SDavid.Hollister@Sun.COM INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
600410696SDavid.Hollister@Sun.COM return;
600510696SDavid.Hollister@Sun.COM }
600610696SDavid.Hollister@Sun.COM
600710696SDavid.Hollister@Sun.COM ASSERT(mutex_owned(&pwp->lock));
600810696SDavid.Hollister@Sun.COM
600910696SDavid.Hollister@Sun.COM tphyp = pwp->root_phys;
601010696SDavid.Hollister@Sun.COM while (tphyp) {
601110696SDavid.Hollister@Sun.COM pmcs_lock_phy(tphyp);
601210696SDavid.Hollister@Sun.COM if (tphyp->spinup_hold == 0) {
601310696SDavid.Hollister@Sun.COM pmcs_unlock_phy(tphyp);
601410696SDavid.Hollister@Sun.COM tphyp = tphyp->sibling;
601510696SDavid.Hollister@Sun.COM continue;
601610696SDavid.Hollister@Sun.COM }
601710696SDavid.Hollister@Sun.COM
601812462Sjesse.butler@oracle.com pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, tphyp, NULL,
601910696SDavid.Hollister@Sun.COM "%s: Issuing spinup release for PHY %s", __func__,
602012462Sjesse.butler@oracle.com tphyp->path);
602110696SDavid.Hollister@Sun.COM
602210696SDavid.Hollister@Sun.COM mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
602310696SDavid.Hollister@Sun.COM msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
602410696SDavid.Hollister@Sun.COM if (msg == NULL || (pwrk =
602510696SDavid.Hollister@Sun.COM pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) {
602610696SDavid.Hollister@Sun.COM pmcs_unlock_phy(tphyp);
602710696SDavid.Hollister@Sun.COM mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
602810696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_SPINUP_RELEASE);
602910696SDavid.Hollister@Sun.COM break;
603010696SDavid.Hollister@Sun.COM }
603110696SDavid.Hollister@Sun.COM
603210696SDavid.Hollister@Sun.COM tphyp->spinup_hold = 0;
603310696SDavid.Hollister@Sun.COM bzero(msg, PMCS_QENTRY_SIZE);
603410696SDavid.Hollister@Sun.COM msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
603510696SDavid.Hollister@Sun.COM PMCIN_LOCAL_PHY_CONTROL));
603610696SDavid.Hollister@Sun.COM msg[1] = LE_32(pwrk->htag);
603710696SDavid.Hollister@Sun.COM msg[2] = LE_32((0x10 << 8) | tphyp->phynum);
603810696SDavid.Hollister@Sun.COM
603912462Sjesse.butler@oracle.com pwrk->dtype = tphyp->dtype;
604010696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_ONCHIP;
604112462Sjesse.butler@oracle.com pwrk->xp = tphyp->target;
604210696SDavid.Hollister@Sun.COM mutex_exit(&pwrk->lock);
604310696SDavid.Hollister@Sun.COM INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
604410696SDavid.Hollister@Sun.COM pmcs_unlock_phy(tphyp);
604510696SDavid.Hollister@Sun.COM
604610696SDavid.Hollister@Sun.COM tphyp = tphyp->sibling;
604710696SDavid.Hollister@Sun.COM }
604810696SDavid.Hollister@Sun.COM }
604910696SDavid.Hollister@Sun.COM
605010696SDavid.Hollister@Sun.COM /*
605110696SDavid.Hollister@Sun.COM * Abort commands on dead PHYs and deregister them as well as removing
605210696SDavid.Hollister@Sun.COM * the associated targets.
605310696SDavid.Hollister@Sun.COM */
605410696SDavid.Hollister@Sun.COM static int
pmcs_kill_devices(pmcs_hw_t * pwp,pmcs_phy_t * phyp)605510696SDavid.Hollister@Sun.COM pmcs_kill_devices(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
605610696SDavid.Hollister@Sun.COM {
605710696SDavid.Hollister@Sun.COM pmcs_phy_t *pnext, *pchild;
605810696SDavid.Hollister@Sun.COM boolean_t remove_device;
605910696SDavid.Hollister@Sun.COM int rval = 0;
606010696SDavid.Hollister@Sun.COM
606110696SDavid.Hollister@Sun.COM while (phyp) {
606210696SDavid.Hollister@Sun.COM pmcs_lock_phy(phyp);
606310696SDavid.Hollister@Sun.COM pchild = phyp->children;
606410696SDavid.Hollister@Sun.COM pnext = phyp->sibling;
606510696SDavid.Hollister@Sun.COM pmcs_unlock_phy(phyp);
606610696SDavid.Hollister@Sun.COM
606710696SDavid.Hollister@Sun.COM if (pchild) {
606810696SDavid.Hollister@Sun.COM rval = pmcs_kill_devices(pwp, pchild);
606910696SDavid.Hollister@Sun.COM if (rval) {
607010696SDavid.Hollister@Sun.COM return (rval);
607110696SDavid.Hollister@Sun.COM }
607210696SDavid.Hollister@Sun.COM }
607310696SDavid.Hollister@Sun.COM
607410696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
607510696SDavid.Hollister@Sun.COM pmcs_lock_phy(phyp);
607610696SDavid.Hollister@Sun.COM if (phyp->dead && phyp->valid_device_id) {
607710696SDavid.Hollister@Sun.COM remove_device = B_TRUE;
607810696SDavid.Hollister@Sun.COM } else {
607910696SDavid.Hollister@Sun.COM remove_device = B_FALSE;
608010696SDavid.Hollister@Sun.COM }
608110696SDavid.Hollister@Sun.COM
608210696SDavid.Hollister@Sun.COM if (remove_device) {
608310696SDavid.Hollister@Sun.COM pmcs_remove_device(pwp, phyp);
608410696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
608510696SDavid.Hollister@Sun.COM
608610696SDavid.Hollister@Sun.COM rval = pmcs_kill_device(pwp, phyp);
608710696SDavid.Hollister@Sun.COM if (rval) {
608810696SDavid.Hollister@Sun.COM pmcs_unlock_phy(phyp);
608910696SDavid.Hollister@Sun.COM return (rval);
609010696SDavid.Hollister@Sun.COM }
609110696SDavid.Hollister@Sun.COM } else {
609210696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
609310696SDavid.Hollister@Sun.COM }
609410696SDavid.Hollister@Sun.COM
609510696SDavid.Hollister@Sun.COM pmcs_unlock_phy(phyp);
609610696SDavid.Hollister@Sun.COM phyp = pnext;
609710696SDavid.Hollister@Sun.COM }
609810696SDavid.Hollister@Sun.COM
609910696SDavid.Hollister@Sun.COM return (rval);
610010696SDavid.Hollister@Sun.COM }
610110696SDavid.Hollister@Sun.COM
610210696SDavid.Hollister@Sun.COM /*
610310696SDavid.Hollister@Sun.COM * Called with PHY locked
610410696SDavid.Hollister@Sun.COM */
610510696SDavid.Hollister@Sun.COM int
pmcs_kill_device(pmcs_hw_t * pwp,pmcs_phy_t * pptr)610610696SDavid.Hollister@Sun.COM pmcs_kill_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
610710696SDavid.Hollister@Sun.COM {
610812462Sjesse.butler@oracle.com int rval;
610910696SDavid.Hollister@Sun.COM
611011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, "kill %s device @ %s",
611110696SDavid.Hollister@Sun.COM pmcs_get_typename(pptr->dtype), pptr->path);
611210696SDavid.Hollister@Sun.COM
611310696SDavid.Hollister@Sun.COM /*
611410696SDavid.Hollister@Sun.COM * There may be an outstanding ABORT_ALL running, which we wouldn't
611510696SDavid.Hollister@Sun.COM * know just by checking abort_pending. We can, however, check
611610696SDavid.Hollister@Sun.COM * abort_all_start. If it's non-zero, there is one, and we'll just
611710696SDavid.Hollister@Sun.COM * sit here and wait for it to complete. If we don't, we'll remove
611810696SDavid.Hollister@Sun.COM * the device while there are still commands pending.
611910696SDavid.Hollister@Sun.COM */
612010696SDavid.Hollister@Sun.COM if (pptr->abort_all_start) {
612110696SDavid.Hollister@Sun.COM while (pptr->abort_all_start) {
612211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
612310696SDavid.Hollister@Sun.COM "%s: Waiting for outstanding ABORT_ALL on PHY 0x%p",
612410696SDavid.Hollister@Sun.COM __func__, (void *)pptr);
612510696SDavid.Hollister@Sun.COM cv_wait(&pptr->abort_all_cv, &pptr->phy_lock);
612610696SDavid.Hollister@Sun.COM }
612710696SDavid.Hollister@Sun.COM } else if (pptr->abort_pending) {
612812462Sjesse.butler@oracle.com rval = pmcs_abort(pwp, pptr, pptr->device_id, 1, 1);
612912462Sjesse.butler@oracle.com if (rval) {
613011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
613110696SDavid.Hollister@Sun.COM "%s: ABORT_ALL returned non-zero status (%d) for "
613212462Sjesse.butler@oracle.com "PHY 0x%p", __func__, rval, (void *)pptr);
613312462Sjesse.butler@oracle.com return (rval);
613410696SDavid.Hollister@Sun.COM }
613510696SDavid.Hollister@Sun.COM pptr->abort_pending = 0;
613610696SDavid.Hollister@Sun.COM }
613710696SDavid.Hollister@Sun.COM
613812462Sjesse.butler@oracle.com if (pptr->valid_device_id) {
613912462Sjesse.butler@oracle.com pmcs_deregister_device(pwp, pptr);
614012462Sjesse.butler@oracle.com }
614112462Sjesse.butler@oracle.com
614210696SDavid.Hollister@Sun.COM PHY_CHANGED(pwp, pptr);
614310696SDavid.Hollister@Sun.COM RESTART_DISCOVERY(pwp);
614410696SDavid.Hollister@Sun.COM pptr->valid_device_id = 0;
614510696SDavid.Hollister@Sun.COM return (0);
614610696SDavid.Hollister@Sun.COM }
614710696SDavid.Hollister@Sun.COM
614810696SDavid.Hollister@Sun.COM /*
614910696SDavid.Hollister@Sun.COM * Acknowledge the SAS h/w events that need acknowledgement.
615010696SDavid.Hollister@Sun.COM * This is only needed for first level PHYs.
615110696SDavid.Hollister@Sun.COM */
615210696SDavid.Hollister@Sun.COM void
pmcs_ack_events(pmcs_hw_t * pwp)615310696SDavid.Hollister@Sun.COM pmcs_ack_events(pmcs_hw_t *pwp)
615410696SDavid.Hollister@Sun.COM {
615510696SDavid.Hollister@Sun.COM uint32_t msg[PMCS_MSG_SIZE], *ptr;
615610696SDavid.Hollister@Sun.COM struct pmcwork *pwrk;
615710696SDavid.Hollister@Sun.COM pmcs_phy_t *pptr;
615810696SDavid.Hollister@Sun.COM
615910696SDavid.Hollister@Sun.COM for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
616010696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
616110696SDavid.Hollister@Sun.COM if (pptr->hw_event_ack == 0) {
616210696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
616310696SDavid.Hollister@Sun.COM continue;
616410696SDavid.Hollister@Sun.COM }
616510696SDavid.Hollister@Sun.COM mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
616610696SDavid.Hollister@Sun.COM ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
616710696SDavid.Hollister@Sun.COM
616810696SDavid.Hollister@Sun.COM if ((ptr == NULL) || (pwrk =
616910696SDavid.Hollister@Sun.COM pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) {
617010696SDavid.Hollister@Sun.COM mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
617110696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
617210696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_SAS_HW_ACK);
617310696SDavid.Hollister@Sun.COM break;
617410696SDavid.Hollister@Sun.COM }
617510696SDavid.Hollister@Sun.COM
617610696SDavid.Hollister@Sun.COM msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
617712258Ssrikanth.suravajhala@oracle.com PMCIN_SAS_HW_EVENT_ACK));
617810696SDavid.Hollister@Sun.COM msg[1] = LE_32(pwrk->htag);
617910696SDavid.Hollister@Sun.COM msg[2] = LE_32(pptr->hw_event_ack);
618010696SDavid.Hollister@Sun.COM
618110696SDavid.Hollister@Sun.COM mutex_exit(&pwrk->lock);
618210696SDavid.Hollister@Sun.COM pwrk->dtype = pptr->dtype;
618310696SDavid.Hollister@Sun.COM pptr->hw_event_ack = 0;
618410696SDavid.Hollister@Sun.COM COPY_MESSAGE(ptr, msg, 3);
618510696SDavid.Hollister@Sun.COM INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
618610696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
618710696SDavid.Hollister@Sun.COM }
618810696SDavid.Hollister@Sun.COM }
618910696SDavid.Hollister@Sun.COM
619010696SDavid.Hollister@Sun.COM /*
619110696SDavid.Hollister@Sun.COM * Load DMA
619210696SDavid.Hollister@Sun.COM */
619310696SDavid.Hollister@Sun.COM int
pmcs_dma_load(pmcs_hw_t * pwp,pmcs_cmd_t * sp,uint32_t * msg)619410696SDavid.Hollister@Sun.COM pmcs_dma_load(pmcs_hw_t *pwp, pmcs_cmd_t *sp, uint32_t *msg)
619510696SDavid.Hollister@Sun.COM {
619610696SDavid.Hollister@Sun.COM ddi_dma_cookie_t *sg;
619710696SDavid.Hollister@Sun.COM pmcs_dmachunk_t *tc;
619810696SDavid.Hollister@Sun.COM pmcs_dmasgl_t *sgl, *prior;
619910696SDavid.Hollister@Sun.COM int seg, tsc;
620010696SDavid.Hollister@Sun.COM uint64_t sgl_addr;
620110696SDavid.Hollister@Sun.COM
620210696SDavid.Hollister@Sun.COM /*
620310696SDavid.Hollister@Sun.COM * If we have no data segments, we're done.
620410696SDavid.Hollister@Sun.COM */
620510696SDavid.Hollister@Sun.COM if (CMD2PKT(sp)->pkt_numcookies == 0) {
620610696SDavid.Hollister@Sun.COM return (0);
620710696SDavid.Hollister@Sun.COM }
620810696SDavid.Hollister@Sun.COM
620910696SDavid.Hollister@Sun.COM /*
621010696SDavid.Hollister@Sun.COM * Get the S/G list pointer.
621110696SDavid.Hollister@Sun.COM */
621210696SDavid.Hollister@Sun.COM sg = CMD2PKT(sp)->pkt_cookies;
621310696SDavid.Hollister@Sun.COM
621410696SDavid.Hollister@Sun.COM /*
621510696SDavid.Hollister@Sun.COM * If we only have one dma segment, we can directly address that
621610696SDavid.Hollister@Sun.COM * data within the Inbound message itself.
621710696SDavid.Hollister@Sun.COM */
621810696SDavid.Hollister@Sun.COM if (CMD2PKT(sp)->pkt_numcookies == 1) {
621910696SDavid.Hollister@Sun.COM msg[12] = LE_32(DWORD0(sg->dmac_laddress));
622010696SDavid.Hollister@Sun.COM msg[13] = LE_32(DWORD1(sg->dmac_laddress));
622110696SDavid.Hollister@Sun.COM msg[14] = LE_32(sg->dmac_size);
622210696SDavid.Hollister@Sun.COM msg[15] = 0;
622310696SDavid.Hollister@Sun.COM return (0);
622410696SDavid.Hollister@Sun.COM }
622510696SDavid.Hollister@Sun.COM
622610696SDavid.Hollister@Sun.COM /*
622710696SDavid.Hollister@Sun.COM * Otherwise, we'll need one or more external S/G list chunks.
622810696SDavid.Hollister@Sun.COM * Get the first one and its dma address into the Inbound message.
622910696SDavid.Hollister@Sun.COM */
623010696SDavid.Hollister@Sun.COM mutex_enter(&pwp->dma_lock);
623110696SDavid.Hollister@Sun.COM tc = pwp->dma_freelist;
623210696SDavid.Hollister@Sun.COM if (tc == NULL) {
623310696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS);
623410696SDavid.Hollister@Sun.COM mutex_exit(&pwp->dma_lock);
623511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
623611048SDavid.Hollister@Sun.COM "%s: out of SG lists", __func__);
623710696SDavid.Hollister@Sun.COM return (-1);
623810696SDavid.Hollister@Sun.COM }
623910696SDavid.Hollister@Sun.COM pwp->dma_freelist = tc->nxt;
624010696SDavid.Hollister@Sun.COM mutex_exit(&pwp->dma_lock);
624110696SDavid.Hollister@Sun.COM
624210696SDavid.Hollister@Sun.COM tc->nxt = NULL;
624310696SDavid.Hollister@Sun.COM sp->cmd_clist = tc;
624410696SDavid.Hollister@Sun.COM sgl = tc->chunks;
624510696SDavid.Hollister@Sun.COM (void) memset(tc->chunks, 0, PMCS_SGL_CHUNKSZ);
624610696SDavid.Hollister@Sun.COM sgl_addr = tc->addr;
624710696SDavid.Hollister@Sun.COM msg[12] = LE_32(DWORD0(sgl_addr));
624810696SDavid.Hollister@Sun.COM msg[13] = LE_32(DWORD1(sgl_addr));
624910696SDavid.Hollister@Sun.COM msg[14] = 0;
625010696SDavid.Hollister@Sun.COM msg[15] = LE_32(PMCS_DMASGL_EXTENSION);
625110696SDavid.Hollister@Sun.COM
625210696SDavid.Hollister@Sun.COM prior = sgl;
625310696SDavid.Hollister@Sun.COM tsc = 0;
625410696SDavid.Hollister@Sun.COM
625510696SDavid.Hollister@Sun.COM for (seg = 0; seg < CMD2PKT(sp)->pkt_numcookies; seg++) {
625610696SDavid.Hollister@Sun.COM /*
625710696SDavid.Hollister@Sun.COM * If the current segment count for this chunk is one less than
625810696SDavid.Hollister@Sun.COM * the number s/g lists per chunk and we have more than one seg
625910696SDavid.Hollister@Sun.COM * to go, we need another chunk. Get it, and make sure that the
626010696SDavid.Hollister@Sun.COM * tail end of the the previous chunk points the new chunk
626110696SDavid.Hollister@Sun.COM * (if remembering an offset can be called 'pointing to').
626210696SDavid.Hollister@Sun.COM *
626310696SDavid.Hollister@Sun.COM * Note that we can store the offset into our command area that
626410696SDavid.Hollister@Sun.COM * represents the new chunk in the length field of the part
626510696SDavid.Hollister@Sun.COM * that points the PMC chip at the next chunk- the PMC chip
626610696SDavid.Hollister@Sun.COM * ignores this field when the EXTENSION bit is set.
626710696SDavid.Hollister@Sun.COM *
626810696SDavid.Hollister@Sun.COM * This is required for dma unloads later.
626910696SDavid.Hollister@Sun.COM */
627010696SDavid.Hollister@Sun.COM if (tsc == (PMCS_SGL_NCHUNKS - 1) &&
627110696SDavid.Hollister@Sun.COM seg < (CMD2PKT(sp)->pkt_numcookies - 1)) {
627210696SDavid.Hollister@Sun.COM mutex_enter(&pwp->dma_lock);
627310696SDavid.Hollister@Sun.COM tc = pwp->dma_freelist;
627410696SDavid.Hollister@Sun.COM if (tc == NULL) {
627510696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS);
627610696SDavid.Hollister@Sun.COM mutex_exit(&pwp->dma_lock);
627710696SDavid.Hollister@Sun.COM pmcs_dma_unload(pwp, sp);
627811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
627910696SDavid.Hollister@Sun.COM "%s: out of SG lists", __func__);
628010696SDavid.Hollister@Sun.COM return (-1);
628110696SDavid.Hollister@Sun.COM }
628210696SDavid.Hollister@Sun.COM pwp->dma_freelist = tc->nxt;
628310696SDavid.Hollister@Sun.COM tc->nxt = sp->cmd_clist;
628410696SDavid.Hollister@Sun.COM mutex_exit(&pwp->dma_lock);
628510696SDavid.Hollister@Sun.COM
628610696SDavid.Hollister@Sun.COM sp->cmd_clist = tc;
628710696SDavid.Hollister@Sun.COM (void) memset(tc->chunks, 0, PMCS_SGL_CHUNKSZ);
628810696SDavid.Hollister@Sun.COM sgl = tc->chunks;
628910696SDavid.Hollister@Sun.COM sgl_addr = tc->addr;
629010696SDavid.Hollister@Sun.COM prior[PMCS_SGL_NCHUNKS-1].sglal =
629110696SDavid.Hollister@Sun.COM LE_32(DWORD0(sgl_addr));
629210696SDavid.Hollister@Sun.COM prior[PMCS_SGL_NCHUNKS-1].sglah =
629310696SDavid.Hollister@Sun.COM LE_32(DWORD1(sgl_addr));
629410696SDavid.Hollister@Sun.COM prior[PMCS_SGL_NCHUNKS-1].sglen = 0;
629510696SDavid.Hollister@Sun.COM prior[PMCS_SGL_NCHUNKS-1].flags =
629610696SDavid.Hollister@Sun.COM LE_32(PMCS_DMASGL_EXTENSION);
629710696SDavid.Hollister@Sun.COM prior = sgl;
629810696SDavid.Hollister@Sun.COM tsc = 0;
629910696SDavid.Hollister@Sun.COM }
630010696SDavid.Hollister@Sun.COM sgl[tsc].sglal = LE_32(DWORD0(sg->dmac_laddress));
630110696SDavid.Hollister@Sun.COM sgl[tsc].sglah = LE_32(DWORD1(sg->dmac_laddress));
630210696SDavid.Hollister@Sun.COM sgl[tsc].sglen = LE_32(sg->dmac_size);
630310696SDavid.Hollister@Sun.COM sgl[tsc++].flags = 0;
630410696SDavid.Hollister@Sun.COM sg++;
630510696SDavid.Hollister@Sun.COM }
630610696SDavid.Hollister@Sun.COM return (0);
630710696SDavid.Hollister@Sun.COM }
630810696SDavid.Hollister@Sun.COM
630910696SDavid.Hollister@Sun.COM /*
631010696SDavid.Hollister@Sun.COM * Unload DMA
631110696SDavid.Hollister@Sun.COM */
631210696SDavid.Hollister@Sun.COM void
pmcs_dma_unload(pmcs_hw_t * pwp,pmcs_cmd_t * sp)631310696SDavid.Hollister@Sun.COM pmcs_dma_unload(pmcs_hw_t *pwp, pmcs_cmd_t *sp)
631410696SDavid.Hollister@Sun.COM {
631510696SDavid.Hollister@Sun.COM pmcs_dmachunk_t *cp;
631610696SDavid.Hollister@Sun.COM
631710696SDavid.Hollister@Sun.COM mutex_enter(&pwp->dma_lock);
631810696SDavid.Hollister@Sun.COM while ((cp = sp->cmd_clist) != NULL) {
631910696SDavid.Hollister@Sun.COM sp->cmd_clist = cp->nxt;
632010696SDavid.Hollister@Sun.COM cp->nxt = pwp->dma_freelist;
632110696SDavid.Hollister@Sun.COM pwp->dma_freelist = cp;
632210696SDavid.Hollister@Sun.COM }
632310696SDavid.Hollister@Sun.COM mutex_exit(&pwp->dma_lock);
632410696SDavid.Hollister@Sun.COM }
632510696SDavid.Hollister@Sun.COM
632610696SDavid.Hollister@Sun.COM /*
632710696SDavid.Hollister@Sun.COM * Take a chunk of consistent memory that has just been allocated and inserted
632810696SDavid.Hollister@Sun.COM * into the cip indices and prepare it for DMA chunk usage and add it to the
632910696SDavid.Hollister@Sun.COM * freelist.
633010696SDavid.Hollister@Sun.COM *
633110696SDavid.Hollister@Sun.COM * Called with dma_lock locked (except during attach when it's unnecessary)
633210696SDavid.Hollister@Sun.COM */
633310696SDavid.Hollister@Sun.COM void
pmcs_idma_chunks(pmcs_hw_t * pwp,pmcs_dmachunk_t * dcp,pmcs_chunk_t * pchunk,unsigned long lim)633410696SDavid.Hollister@Sun.COM pmcs_idma_chunks(pmcs_hw_t *pwp, pmcs_dmachunk_t *dcp,
633510696SDavid.Hollister@Sun.COM pmcs_chunk_t *pchunk, unsigned long lim)
633610696SDavid.Hollister@Sun.COM {
633710696SDavid.Hollister@Sun.COM unsigned long off, n;
633810696SDavid.Hollister@Sun.COM pmcs_dmachunk_t *np = dcp;
633910696SDavid.Hollister@Sun.COM pmcs_chunk_t *tmp_chunk;
634010696SDavid.Hollister@Sun.COM
634110696SDavid.Hollister@Sun.COM if (pwp->dma_chunklist == NULL) {
634210696SDavid.Hollister@Sun.COM pwp->dma_chunklist = pchunk;
634310696SDavid.Hollister@Sun.COM } else {
634410696SDavid.Hollister@Sun.COM tmp_chunk = pwp->dma_chunklist;
634510696SDavid.Hollister@Sun.COM while (tmp_chunk->next) {
634610696SDavid.Hollister@Sun.COM tmp_chunk = tmp_chunk->next;
634710696SDavid.Hollister@Sun.COM }
634810696SDavid.Hollister@Sun.COM tmp_chunk->next = pchunk;
634910696SDavid.Hollister@Sun.COM }
635010696SDavid.Hollister@Sun.COM
635110696SDavid.Hollister@Sun.COM /*
635210696SDavid.Hollister@Sun.COM * Install offsets into chunk lists.
635310696SDavid.Hollister@Sun.COM */
635410696SDavid.Hollister@Sun.COM for (n = 0, off = 0; off < lim; off += PMCS_SGL_CHUNKSZ, n++) {
635510696SDavid.Hollister@Sun.COM np->chunks = (void *)&pchunk->addrp[off];
635610696SDavid.Hollister@Sun.COM np->addr = pchunk->dma_addr + off;
635710696SDavid.Hollister@Sun.COM np->acc_handle = pchunk->acc_handle;
635810696SDavid.Hollister@Sun.COM np->dma_handle = pchunk->dma_handle;
635910696SDavid.Hollister@Sun.COM if ((off + PMCS_SGL_CHUNKSZ) < lim) {
636010696SDavid.Hollister@Sun.COM np = np->nxt;
636110696SDavid.Hollister@Sun.COM }
636210696SDavid.Hollister@Sun.COM }
636310696SDavid.Hollister@Sun.COM np->nxt = pwp->dma_freelist;
636410696SDavid.Hollister@Sun.COM pwp->dma_freelist = dcp;
636511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
636610696SDavid.Hollister@Sun.COM "added %lu DMA chunks ", n);
636710696SDavid.Hollister@Sun.COM }
636810696SDavid.Hollister@Sun.COM
636910696SDavid.Hollister@Sun.COM /*
637010696SDavid.Hollister@Sun.COM * Change the value of the interrupt coalescing timer. This is done currently
637110696SDavid.Hollister@Sun.COM * only for I/O completions. If we're using the "auto clear" feature, it can
637210696SDavid.Hollister@Sun.COM * be turned back on when interrupt coalescing is turned off and must be
637310696SDavid.Hollister@Sun.COM * turned off when the coalescing timer is on.
637410696SDavid.Hollister@Sun.COM * NOTE: PMCS_MSIX_GENERAL and PMCS_OQ_IODONE are the same value. As long
637510696SDavid.Hollister@Sun.COM * as that's true, we don't need to distinguish between them.
637610696SDavid.Hollister@Sun.COM */
637710696SDavid.Hollister@Sun.COM
637810696SDavid.Hollister@Sun.COM void
pmcs_set_intr_coal_timer(pmcs_hw_t * pwp,pmcs_coal_timer_adj_t adj)637910696SDavid.Hollister@Sun.COM pmcs_set_intr_coal_timer(pmcs_hw_t *pwp, pmcs_coal_timer_adj_t adj)
638010696SDavid.Hollister@Sun.COM {
638110696SDavid.Hollister@Sun.COM if (adj == DECREASE_TIMER) {
638210696SDavid.Hollister@Sun.COM /* If the timer is already off, nothing to do. */
638310696SDavid.Hollister@Sun.COM if (pwp->io_intr_coal.timer_on == B_FALSE) {
638410696SDavid.Hollister@Sun.COM return;
638510696SDavid.Hollister@Sun.COM }
638610696SDavid.Hollister@Sun.COM
638710696SDavid.Hollister@Sun.COM pwp->io_intr_coal.intr_coal_timer -= PMCS_COAL_TIMER_GRAN;
638810696SDavid.Hollister@Sun.COM
638910696SDavid.Hollister@Sun.COM if (pwp->io_intr_coal.intr_coal_timer == 0) {
639010696SDavid.Hollister@Sun.COM /* Disable the timer */
639110696SDavid.Hollister@Sun.COM pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_CONTROL, 0);
639210696SDavid.Hollister@Sun.COM
639310696SDavid.Hollister@Sun.COM if (pwp->odb_auto_clear & (1 << PMCS_MSIX_IODONE)) {
639410696SDavid.Hollister@Sun.COM pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR,
639510696SDavid.Hollister@Sun.COM pwp->odb_auto_clear);
639610696SDavid.Hollister@Sun.COM }
639710696SDavid.Hollister@Sun.COM
639810696SDavid.Hollister@Sun.COM pwp->io_intr_coal.timer_on = B_FALSE;
639910696SDavid.Hollister@Sun.COM pwp->io_intr_coal.max_io_completions = B_FALSE;
640010696SDavid.Hollister@Sun.COM pwp->io_intr_coal.num_intrs = 0;
640110696SDavid.Hollister@Sun.COM pwp->io_intr_coal.int_cleared = B_FALSE;
640210696SDavid.Hollister@Sun.COM pwp->io_intr_coal.num_io_completions = 0;
640310696SDavid.Hollister@Sun.COM
640410696SDavid.Hollister@Sun.COM DTRACE_PROBE1(pmcs__intr__coalesce__timer__off,
640510696SDavid.Hollister@Sun.COM pmcs_io_intr_coal_t *, &pwp->io_intr_coal);
640610696SDavid.Hollister@Sun.COM } else {
640710696SDavid.Hollister@Sun.COM pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_TIMER,
640810696SDavid.Hollister@Sun.COM pwp->io_intr_coal.intr_coal_timer);
640910696SDavid.Hollister@Sun.COM }
641010696SDavid.Hollister@Sun.COM } else {
641110696SDavid.Hollister@Sun.COM /*
641210696SDavid.Hollister@Sun.COM * If the timer isn't on yet, do the setup for it now.
641310696SDavid.Hollister@Sun.COM */
641410696SDavid.Hollister@Sun.COM if (pwp->io_intr_coal.timer_on == B_FALSE) {
641510696SDavid.Hollister@Sun.COM /* If auto clear is being used, turn it off. */
641610696SDavid.Hollister@Sun.COM if (pwp->odb_auto_clear & (1 << PMCS_MSIX_IODONE)) {
641710696SDavid.Hollister@Sun.COM pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR,
641810696SDavid.Hollister@Sun.COM (pwp->odb_auto_clear &
641910696SDavid.Hollister@Sun.COM ~(1 << PMCS_MSIX_IODONE)));
642010696SDavid.Hollister@Sun.COM }
642110696SDavid.Hollister@Sun.COM
642210696SDavid.Hollister@Sun.COM pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_CONTROL,
642310696SDavid.Hollister@Sun.COM (1 << PMCS_MSIX_IODONE));
642410696SDavid.Hollister@Sun.COM pwp->io_intr_coal.timer_on = B_TRUE;
642510696SDavid.Hollister@Sun.COM pwp->io_intr_coal.intr_coal_timer =
642610696SDavid.Hollister@Sun.COM PMCS_COAL_TIMER_GRAN;
642710696SDavid.Hollister@Sun.COM
642810696SDavid.Hollister@Sun.COM DTRACE_PROBE1(pmcs__intr__coalesce__timer__on,
642910696SDavid.Hollister@Sun.COM pmcs_io_intr_coal_t *, &pwp->io_intr_coal);
643010696SDavid.Hollister@Sun.COM } else {
643110696SDavid.Hollister@Sun.COM pwp->io_intr_coal.intr_coal_timer +=
643210696SDavid.Hollister@Sun.COM PMCS_COAL_TIMER_GRAN;
643310696SDavid.Hollister@Sun.COM }
643410696SDavid.Hollister@Sun.COM
643510696SDavid.Hollister@Sun.COM if (pwp->io_intr_coal.intr_coal_timer > PMCS_MAX_COAL_TIMER) {
643610696SDavid.Hollister@Sun.COM pwp->io_intr_coal.intr_coal_timer = PMCS_MAX_COAL_TIMER;
643710696SDavid.Hollister@Sun.COM }
643810696SDavid.Hollister@Sun.COM
643910696SDavid.Hollister@Sun.COM pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_TIMER,
644010696SDavid.Hollister@Sun.COM pwp->io_intr_coal.intr_coal_timer);
644110696SDavid.Hollister@Sun.COM }
644210696SDavid.Hollister@Sun.COM
644310696SDavid.Hollister@Sun.COM /*
644410696SDavid.Hollister@Sun.COM * Adjust the interrupt threshold based on the current timer value
644510696SDavid.Hollister@Sun.COM */
644610696SDavid.Hollister@Sun.COM pwp->io_intr_coal.intr_threshold =
644710696SDavid.Hollister@Sun.COM PMCS_INTR_THRESHOLD(PMCS_QUANTUM_TIME_USECS * 1000 /
644810696SDavid.Hollister@Sun.COM (pwp->io_intr_coal.intr_latency +
644910696SDavid.Hollister@Sun.COM (pwp->io_intr_coal.intr_coal_timer * 1000)));
645010696SDavid.Hollister@Sun.COM }
645110696SDavid.Hollister@Sun.COM
645210696SDavid.Hollister@Sun.COM /*
645310696SDavid.Hollister@Sun.COM * Register Access functions
645410696SDavid.Hollister@Sun.COM */
645510696SDavid.Hollister@Sun.COM uint32_t
pmcs_rd_iqci(pmcs_hw_t * pwp,uint32_t qnum)645610696SDavid.Hollister@Sun.COM pmcs_rd_iqci(pmcs_hw_t *pwp, uint32_t qnum)
645710696SDavid.Hollister@Sun.COM {
645810696SDavid.Hollister@Sun.COM uint32_t iqci;
645910696SDavid.Hollister@Sun.COM
646010696SDavid.Hollister@Sun.COM if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORKERNEL) !=
646110696SDavid.Hollister@Sun.COM DDI_SUCCESS) {
646211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
646311048SDavid.Hollister@Sun.COM "%s: ddi_dma_sync failed?", __func__);
646410696SDavid.Hollister@Sun.COM }
646510696SDavid.Hollister@Sun.COM
646610696SDavid.Hollister@Sun.COM iqci = LE_32(
646710696SDavid.Hollister@Sun.COM ((uint32_t *)((void *)pwp->cip))[IQ_OFFSET(qnum) >> 2]);
646810696SDavid.Hollister@Sun.COM
646910696SDavid.Hollister@Sun.COM return (iqci);
647010696SDavid.Hollister@Sun.COM }
647110696SDavid.Hollister@Sun.COM
647210696SDavid.Hollister@Sun.COM uint32_t
pmcs_rd_oqpi(pmcs_hw_t * pwp,uint32_t qnum)647310696SDavid.Hollister@Sun.COM pmcs_rd_oqpi(pmcs_hw_t *pwp, uint32_t qnum)
647410696SDavid.Hollister@Sun.COM {
647510696SDavid.Hollister@Sun.COM uint32_t oqpi;
647610696SDavid.Hollister@Sun.COM
647710696SDavid.Hollister@Sun.COM if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORKERNEL) !=
647810696SDavid.Hollister@Sun.COM DDI_SUCCESS) {
647911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
648011048SDavid.Hollister@Sun.COM "%s: ddi_dma_sync failed?", __func__);
648110696SDavid.Hollister@Sun.COM }
648210696SDavid.Hollister@Sun.COM
648310696SDavid.Hollister@Sun.COM oqpi = LE_32(
648410696SDavid.Hollister@Sun.COM ((uint32_t *)((void *)pwp->cip))[OQ_OFFSET(qnum) >> 2]);
648510696SDavid.Hollister@Sun.COM
648610696SDavid.Hollister@Sun.COM return (oqpi);
648710696SDavid.Hollister@Sun.COM }
648810696SDavid.Hollister@Sun.COM
648910696SDavid.Hollister@Sun.COM uint32_t
pmcs_rd_gsm_reg(pmcs_hw_t * pwp,uint8_t hi,uint32_t off)649011980SDavid.Hollister@Sun.COM pmcs_rd_gsm_reg(pmcs_hw_t *pwp, uint8_t hi, uint32_t off)
649111980SDavid.Hollister@Sun.COM {
649211980SDavid.Hollister@Sun.COM uint32_t rv, newaxil, oldaxil, oldaxih;
649310696SDavid.Hollister@Sun.COM
649410696SDavid.Hollister@Sun.COM newaxil = off & ~GSM_BASE_MASK;
649510696SDavid.Hollister@Sun.COM off &= GSM_BASE_MASK;
649610696SDavid.Hollister@Sun.COM mutex_enter(&pwp->axil_lock);
649710696SDavid.Hollister@Sun.COM oldaxil = ddi_get32(pwp->top_acc_handle,
649810696SDavid.Hollister@Sun.COM &pwp->top_regs[PMCS_AXI_TRANS >> 2]);
649910696SDavid.Hollister@Sun.COM ddi_put32(pwp->top_acc_handle,
650010696SDavid.Hollister@Sun.COM &pwp->top_regs[PMCS_AXI_TRANS >> 2], newaxil);
650110696SDavid.Hollister@Sun.COM drv_usecwait(10);
650210696SDavid.Hollister@Sun.COM if (ddi_get32(pwp->top_acc_handle,
650310696SDavid.Hollister@Sun.COM &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != newaxil) {
650411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
650511048SDavid.Hollister@Sun.COM "AXIL register update failed");
650610696SDavid.Hollister@Sun.COM }
650711980SDavid.Hollister@Sun.COM if (hi) {
650811980SDavid.Hollister@Sun.COM oldaxih = ddi_get32(pwp->top_acc_handle,
650911980SDavid.Hollister@Sun.COM &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2]);
651011980SDavid.Hollister@Sun.COM ddi_put32(pwp->top_acc_handle,
651111980SDavid.Hollister@Sun.COM &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2], hi);
651211980SDavid.Hollister@Sun.COM drv_usecwait(10);
651311980SDavid.Hollister@Sun.COM if (ddi_get32(pwp->top_acc_handle,
651411980SDavid.Hollister@Sun.COM &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2]) != hi) {
651511980SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
651611980SDavid.Hollister@Sun.COM "AXIH register update failed");
651711980SDavid.Hollister@Sun.COM }
651811980SDavid.Hollister@Sun.COM }
651910696SDavid.Hollister@Sun.COM rv = ddi_get32(pwp->gsm_acc_handle, &pwp->gsm_regs[off >> 2]);
652011980SDavid.Hollister@Sun.COM if (hi) {
652111980SDavid.Hollister@Sun.COM ddi_put32(pwp->top_acc_handle,
652211980SDavid.Hollister@Sun.COM &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2], oldaxih);
652311980SDavid.Hollister@Sun.COM drv_usecwait(10);
652411980SDavid.Hollister@Sun.COM if (ddi_get32(pwp->top_acc_handle,
652511980SDavid.Hollister@Sun.COM &pwp->top_regs[PMCS_AXI_TRANS_UPPER >> 2]) != oldaxih) {
652611980SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
652711980SDavid.Hollister@Sun.COM "AXIH register restore failed");
652811980SDavid.Hollister@Sun.COM }
652911980SDavid.Hollister@Sun.COM }
653010696SDavid.Hollister@Sun.COM ddi_put32(pwp->top_acc_handle,
653110696SDavid.Hollister@Sun.COM &pwp->top_regs[PMCS_AXI_TRANS >> 2], oldaxil);
653210696SDavid.Hollister@Sun.COM drv_usecwait(10);
653310696SDavid.Hollister@Sun.COM if (ddi_get32(pwp->top_acc_handle,
653410696SDavid.Hollister@Sun.COM &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != oldaxil) {
653511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
653611048SDavid.Hollister@Sun.COM "AXIL register restore failed");
653710696SDavid.Hollister@Sun.COM }
653810696SDavid.Hollister@Sun.COM mutex_exit(&pwp->axil_lock);
653910696SDavid.Hollister@Sun.COM return (rv);
654010696SDavid.Hollister@Sun.COM }
654110696SDavid.Hollister@Sun.COM
654210696SDavid.Hollister@Sun.COM void
pmcs_wr_gsm_reg(pmcs_hw_t * pwp,uint32_t off,uint32_t val)654310696SDavid.Hollister@Sun.COM pmcs_wr_gsm_reg(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
654410696SDavid.Hollister@Sun.COM {
654510696SDavid.Hollister@Sun.COM uint32_t newaxil, oldaxil;
654610696SDavid.Hollister@Sun.COM
654710696SDavid.Hollister@Sun.COM newaxil = off & ~GSM_BASE_MASK;
654810696SDavid.Hollister@Sun.COM off &= GSM_BASE_MASK;
654910696SDavid.Hollister@Sun.COM mutex_enter(&pwp->axil_lock);
655010696SDavid.Hollister@Sun.COM oldaxil = ddi_get32(pwp->top_acc_handle,
655110696SDavid.Hollister@Sun.COM &pwp->top_regs[PMCS_AXI_TRANS >> 2]);
655210696SDavid.Hollister@Sun.COM ddi_put32(pwp->top_acc_handle,
655310696SDavid.Hollister@Sun.COM &pwp->top_regs[PMCS_AXI_TRANS >> 2], newaxil);
655410696SDavid.Hollister@Sun.COM drv_usecwait(10);
655510696SDavid.Hollister@Sun.COM if (ddi_get32(pwp->top_acc_handle,
655610696SDavid.Hollister@Sun.COM &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != newaxil) {
655711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
655811048SDavid.Hollister@Sun.COM "AXIL register update failed");
655910696SDavid.Hollister@Sun.COM }
656010696SDavid.Hollister@Sun.COM ddi_put32(pwp->gsm_acc_handle, &pwp->gsm_regs[off >> 2], val);
656110696SDavid.Hollister@Sun.COM ddi_put32(pwp->top_acc_handle,
656210696SDavid.Hollister@Sun.COM &pwp->top_regs[PMCS_AXI_TRANS >> 2], oldaxil);
656310696SDavid.Hollister@Sun.COM drv_usecwait(10);
656410696SDavid.Hollister@Sun.COM if (ddi_get32(pwp->top_acc_handle,
656510696SDavid.Hollister@Sun.COM &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != oldaxil) {
656611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
656711048SDavid.Hollister@Sun.COM "AXIL register restore failed");
656810696SDavid.Hollister@Sun.COM }
656910696SDavid.Hollister@Sun.COM mutex_exit(&pwp->axil_lock);
657010696SDavid.Hollister@Sun.COM }
657110696SDavid.Hollister@Sun.COM
657210696SDavid.Hollister@Sun.COM uint32_t
pmcs_rd_topunit(pmcs_hw_t * pwp,uint32_t off)657310696SDavid.Hollister@Sun.COM pmcs_rd_topunit(pmcs_hw_t *pwp, uint32_t off)
657410696SDavid.Hollister@Sun.COM {
657510696SDavid.Hollister@Sun.COM switch (off) {
657610696SDavid.Hollister@Sun.COM case PMCS_SPC_RESET:
657710696SDavid.Hollister@Sun.COM case PMCS_SPC_BOOT_STRAP:
657810696SDavid.Hollister@Sun.COM case PMCS_SPC_DEVICE_ID:
657910696SDavid.Hollister@Sun.COM case PMCS_DEVICE_REVISION:
658011980SDavid.Hollister@Sun.COM off = pmcs_rd_gsm_reg(pwp, 0, off);
658110696SDavid.Hollister@Sun.COM break;
658210696SDavid.Hollister@Sun.COM default:
658310696SDavid.Hollister@Sun.COM off = ddi_get32(pwp->top_acc_handle,
658410696SDavid.Hollister@Sun.COM &pwp->top_regs[off >> 2]);
658510696SDavid.Hollister@Sun.COM break;
658610696SDavid.Hollister@Sun.COM }
658710696SDavid.Hollister@Sun.COM return (off);
658810696SDavid.Hollister@Sun.COM }
658910696SDavid.Hollister@Sun.COM
659010696SDavid.Hollister@Sun.COM void
pmcs_wr_topunit(pmcs_hw_t * pwp,uint32_t off,uint32_t val)659110696SDavid.Hollister@Sun.COM pmcs_wr_topunit(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
659210696SDavid.Hollister@Sun.COM {
659310696SDavid.Hollister@Sun.COM switch (off) {
659410696SDavid.Hollister@Sun.COM case PMCS_SPC_RESET:
659510696SDavid.Hollister@Sun.COM case PMCS_DEVICE_REVISION:
659610696SDavid.Hollister@Sun.COM pmcs_wr_gsm_reg(pwp, off, val);
659710696SDavid.Hollister@Sun.COM break;
659810696SDavid.Hollister@Sun.COM default:
659910696SDavid.Hollister@Sun.COM ddi_put32(pwp->top_acc_handle, &pwp->top_regs[off >> 2], val);
660010696SDavid.Hollister@Sun.COM break;
660110696SDavid.Hollister@Sun.COM }
660210696SDavid.Hollister@Sun.COM }
660310696SDavid.Hollister@Sun.COM
660410696SDavid.Hollister@Sun.COM uint32_t
pmcs_rd_msgunit(pmcs_hw_t * pwp,uint32_t off)660510696SDavid.Hollister@Sun.COM pmcs_rd_msgunit(pmcs_hw_t *pwp, uint32_t off)
660610696SDavid.Hollister@Sun.COM {
660710696SDavid.Hollister@Sun.COM return (ddi_get32(pwp->msg_acc_handle, &pwp->msg_regs[off >> 2]));
660810696SDavid.Hollister@Sun.COM }
660910696SDavid.Hollister@Sun.COM
661010696SDavid.Hollister@Sun.COM uint32_t
pmcs_rd_mpi_tbl(pmcs_hw_t * pwp,uint32_t off)661110696SDavid.Hollister@Sun.COM pmcs_rd_mpi_tbl(pmcs_hw_t *pwp, uint32_t off)
661210696SDavid.Hollister@Sun.COM {
661310696SDavid.Hollister@Sun.COM return (ddi_get32(pwp->mpi_acc_handle,
661410696SDavid.Hollister@Sun.COM &pwp->mpi_regs[(pwp->mpi_offset + off) >> 2]));
661510696SDavid.Hollister@Sun.COM }
661610696SDavid.Hollister@Sun.COM
661710696SDavid.Hollister@Sun.COM uint32_t
pmcs_rd_gst_tbl(pmcs_hw_t * pwp,uint32_t off)661810696SDavid.Hollister@Sun.COM pmcs_rd_gst_tbl(pmcs_hw_t *pwp, uint32_t off)
661910696SDavid.Hollister@Sun.COM {
662010696SDavid.Hollister@Sun.COM return (ddi_get32(pwp->mpi_acc_handle,
662110696SDavid.Hollister@Sun.COM &pwp->mpi_regs[(pwp->mpi_gst_offset + off) >> 2]));
662210696SDavid.Hollister@Sun.COM }
662310696SDavid.Hollister@Sun.COM
662410696SDavid.Hollister@Sun.COM uint32_t
pmcs_rd_iqc_tbl(pmcs_hw_t * pwp,uint32_t off)662510696SDavid.Hollister@Sun.COM pmcs_rd_iqc_tbl(pmcs_hw_t *pwp, uint32_t off)
662610696SDavid.Hollister@Sun.COM {
662710696SDavid.Hollister@Sun.COM return (ddi_get32(pwp->mpi_acc_handle,
662810696SDavid.Hollister@Sun.COM &pwp->mpi_regs[(pwp->mpi_iqc_offset + off) >> 2]));
662910696SDavid.Hollister@Sun.COM }
663010696SDavid.Hollister@Sun.COM
663110696SDavid.Hollister@Sun.COM uint32_t
pmcs_rd_oqc_tbl(pmcs_hw_t * pwp,uint32_t off)663210696SDavid.Hollister@Sun.COM pmcs_rd_oqc_tbl(pmcs_hw_t *pwp, uint32_t off)
663310696SDavid.Hollister@Sun.COM {
663410696SDavid.Hollister@Sun.COM return (ddi_get32(pwp->mpi_acc_handle,
663510696SDavid.Hollister@Sun.COM &pwp->mpi_regs[(pwp->mpi_oqc_offset + off) >> 2]));
663610696SDavid.Hollister@Sun.COM }
663710696SDavid.Hollister@Sun.COM
663810696SDavid.Hollister@Sun.COM uint32_t
pmcs_rd_iqpi(pmcs_hw_t * pwp,uint32_t qnum)663910696SDavid.Hollister@Sun.COM pmcs_rd_iqpi(pmcs_hw_t *pwp, uint32_t qnum)
664010696SDavid.Hollister@Sun.COM {
664110696SDavid.Hollister@Sun.COM return (ddi_get32(pwp->mpi_acc_handle,
664210696SDavid.Hollister@Sun.COM &pwp->mpi_regs[pwp->iqpi_offset[qnum] >> 2]));
664310696SDavid.Hollister@Sun.COM }
664410696SDavid.Hollister@Sun.COM
664510696SDavid.Hollister@Sun.COM uint32_t
pmcs_rd_oqci(pmcs_hw_t * pwp,uint32_t qnum)664610696SDavid.Hollister@Sun.COM pmcs_rd_oqci(pmcs_hw_t *pwp, uint32_t qnum)
664710696SDavid.Hollister@Sun.COM {
664810696SDavid.Hollister@Sun.COM return (ddi_get32(pwp->mpi_acc_handle,
664910696SDavid.Hollister@Sun.COM &pwp->mpi_regs[pwp->oqci_offset[qnum] >> 2]));
665010696SDavid.Hollister@Sun.COM }
665110696SDavid.Hollister@Sun.COM
665210696SDavid.Hollister@Sun.COM void
pmcs_wr_msgunit(pmcs_hw_t * pwp,uint32_t off,uint32_t val)665310696SDavid.Hollister@Sun.COM pmcs_wr_msgunit(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
665410696SDavid.Hollister@Sun.COM {
665510696SDavid.Hollister@Sun.COM ddi_put32(pwp->msg_acc_handle, &pwp->msg_regs[off >> 2], val);
665610696SDavid.Hollister@Sun.COM }
665710696SDavid.Hollister@Sun.COM
665810696SDavid.Hollister@Sun.COM void
pmcs_wr_mpi_tbl(pmcs_hw_t * pwp,uint32_t off,uint32_t val)665910696SDavid.Hollister@Sun.COM pmcs_wr_mpi_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
666010696SDavid.Hollister@Sun.COM {
666110696SDavid.Hollister@Sun.COM ddi_put32(pwp->mpi_acc_handle,
666210696SDavid.Hollister@Sun.COM &pwp->mpi_regs[(pwp->mpi_offset + off) >> 2], (val));
666310696SDavid.Hollister@Sun.COM }
666410696SDavid.Hollister@Sun.COM
666510696SDavid.Hollister@Sun.COM void
pmcs_wr_gst_tbl(pmcs_hw_t * pwp,uint32_t off,uint32_t val)666610696SDavid.Hollister@Sun.COM pmcs_wr_gst_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
666710696SDavid.Hollister@Sun.COM {
666810696SDavid.Hollister@Sun.COM ddi_put32(pwp->mpi_acc_handle,
666910696SDavid.Hollister@Sun.COM &pwp->mpi_regs[(pwp->mpi_gst_offset + off) >> 2], val);
667010696SDavid.Hollister@Sun.COM }
667110696SDavid.Hollister@Sun.COM
667210696SDavid.Hollister@Sun.COM void
pmcs_wr_iqc_tbl(pmcs_hw_t * pwp,uint32_t off,uint32_t val)667310696SDavid.Hollister@Sun.COM pmcs_wr_iqc_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
667410696SDavid.Hollister@Sun.COM {
667510696SDavid.Hollister@Sun.COM ddi_put32(pwp->mpi_acc_handle,
667610696SDavid.Hollister@Sun.COM &pwp->mpi_regs[(pwp->mpi_iqc_offset + off) >> 2], val);
667710696SDavid.Hollister@Sun.COM }
667810696SDavid.Hollister@Sun.COM
667910696SDavid.Hollister@Sun.COM void
pmcs_wr_oqc_tbl(pmcs_hw_t * pwp,uint32_t off,uint32_t val)668010696SDavid.Hollister@Sun.COM pmcs_wr_oqc_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
668110696SDavid.Hollister@Sun.COM {
668210696SDavid.Hollister@Sun.COM ddi_put32(pwp->mpi_acc_handle,
668310696SDavid.Hollister@Sun.COM &pwp->mpi_regs[(pwp->mpi_oqc_offset + off) >> 2], val);
668410696SDavid.Hollister@Sun.COM }
668510696SDavid.Hollister@Sun.COM
668610696SDavid.Hollister@Sun.COM void
pmcs_wr_iqci(pmcs_hw_t * pwp,uint32_t qnum,uint32_t val)668710696SDavid.Hollister@Sun.COM pmcs_wr_iqci(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
668810696SDavid.Hollister@Sun.COM {
668910696SDavid.Hollister@Sun.COM ((uint32_t *)((void *)pwp->cip))[IQ_OFFSET(qnum) >> 2] = val;
669010696SDavid.Hollister@Sun.COM if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORDEV) !=
669110696SDavid.Hollister@Sun.COM DDI_SUCCESS) {
669211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
669311048SDavid.Hollister@Sun.COM "%s: ddi_dma_sync failed?", __func__);
669410696SDavid.Hollister@Sun.COM }
669510696SDavid.Hollister@Sun.COM }
669610696SDavid.Hollister@Sun.COM
669710696SDavid.Hollister@Sun.COM void
pmcs_wr_iqpi(pmcs_hw_t * pwp,uint32_t qnum,uint32_t val)669810696SDavid.Hollister@Sun.COM pmcs_wr_iqpi(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
669910696SDavid.Hollister@Sun.COM {
670010696SDavid.Hollister@Sun.COM ddi_put32(pwp->mpi_acc_handle,
670110696SDavid.Hollister@Sun.COM &pwp->mpi_regs[pwp->iqpi_offset[qnum] >> 2], val);
670210696SDavid.Hollister@Sun.COM }
670310696SDavid.Hollister@Sun.COM
670410696SDavid.Hollister@Sun.COM void
pmcs_wr_oqci(pmcs_hw_t * pwp,uint32_t qnum,uint32_t val)670510696SDavid.Hollister@Sun.COM pmcs_wr_oqci(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
670610696SDavid.Hollister@Sun.COM {
670710696SDavid.Hollister@Sun.COM ddi_put32(pwp->mpi_acc_handle,
670810696SDavid.Hollister@Sun.COM &pwp->mpi_regs[pwp->oqci_offset[qnum] >> 2], val);
670910696SDavid.Hollister@Sun.COM }
671010696SDavid.Hollister@Sun.COM
671110696SDavid.Hollister@Sun.COM void
pmcs_wr_oqpi(pmcs_hw_t * pwp,uint32_t qnum,uint32_t val)671210696SDavid.Hollister@Sun.COM pmcs_wr_oqpi(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
671310696SDavid.Hollister@Sun.COM {
671410696SDavid.Hollister@Sun.COM ((uint32_t *)((void *)pwp->cip))[OQ_OFFSET(qnum) >> 2] = val;
671510696SDavid.Hollister@Sun.COM if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORDEV) !=
671610696SDavid.Hollister@Sun.COM DDI_SUCCESS) {
671711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
671811048SDavid.Hollister@Sun.COM "%s: ddi_dma_sync failed?", __func__);
671910696SDavid.Hollister@Sun.COM }
672010696SDavid.Hollister@Sun.COM }
672110696SDavid.Hollister@Sun.COM
672210696SDavid.Hollister@Sun.COM /*
672310696SDavid.Hollister@Sun.COM * Check the status value of an outbound IOMB and report anything bad
672410696SDavid.Hollister@Sun.COM */
672510696SDavid.Hollister@Sun.COM
672610696SDavid.Hollister@Sun.COM void
pmcs_check_iomb_status(pmcs_hw_t * pwp,uint32_t * iomb)672710696SDavid.Hollister@Sun.COM pmcs_check_iomb_status(pmcs_hw_t *pwp, uint32_t *iomb)
672810696SDavid.Hollister@Sun.COM {
672910696SDavid.Hollister@Sun.COM uint16_t opcode;
673010696SDavid.Hollister@Sun.COM int offset;
673110696SDavid.Hollister@Sun.COM
673210696SDavid.Hollister@Sun.COM if (iomb == NULL) {
673310696SDavid.Hollister@Sun.COM return;
673410696SDavid.Hollister@Sun.COM }
673510696SDavid.Hollister@Sun.COM
673610696SDavid.Hollister@Sun.COM opcode = LE_32(iomb[0]) & 0xfff;
673710696SDavid.Hollister@Sun.COM
673810696SDavid.Hollister@Sun.COM switch (opcode) {
673910696SDavid.Hollister@Sun.COM /*
674010696SDavid.Hollister@Sun.COM * The following have no status field, so ignore them
674110696SDavid.Hollister@Sun.COM */
674210696SDavid.Hollister@Sun.COM case PMCOUT_ECHO:
674310696SDavid.Hollister@Sun.COM case PMCOUT_SAS_HW_EVENT:
674410696SDavid.Hollister@Sun.COM case PMCOUT_GET_DEVICE_HANDLE:
674510696SDavid.Hollister@Sun.COM case PMCOUT_SATA_EVENT:
674610696SDavid.Hollister@Sun.COM case PMCOUT_SSP_EVENT:
674710696SDavid.Hollister@Sun.COM case PMCOUT_DEVICE_HANDLE_ARRIVED:
674810696SDavid.Hollister@Sun.COM case PMCOUT_GPIO:
674910696SDavid.Hollister@Sun.COM case PMCOUT_GPIO_EVENT:
675010696SDavid.Hollister@Sun.COM case PMCOUT_GET_TIME_STAMP:
675110696SDavid.Hollister@Sun.COM case PMCOUT_SKIP_ENTRIES:
675210696SDavid.Hollister@Sun.COM case PMCOUT_GET_NVMD_DATA: /* Actually lower 16 bits of word 3 */
675310696SDavid.Hollister@Sun.COM case PMCOUT_SET_NVMD_DATA: /* but ignore - we don't use these */
675410696SDavid.Hollister@Sun.COM case PMCOUT_DEVICE_HANDLE_REMOVED:
675510696SDavid.Hollister@Sun.COM case PMCOUT_SSP_REQUEST_RECEIVED:
675610696SDavid.Hollister@Sun.COM return;
675710696SDavid.Hollister@Sun.COM
675810696SDavid.Hollister@Sun.COM case PMCOUT_GENERAL_EVENT:
675910696SDavid.Hollister@Sun.COM offset = 1;
676010696SDavid.Hollister@Sun.COM break;
676110696SDavid.Hollister@Sun.COM
676210696SDavid.Hollister@Sun.COM case PMCOUT_SSP_COMPLETION:
676310696SDavid.Hollister@Sun.COM case PMCOUT_SMP_COMPLETION:
676410696SDavid.Hollister@Sun.COM case PMCOUT_DEVICE_REGISTRATION:
676510696SDavid.Hollister@Sun.COM case PMCOUT_DEREGISTER_DEVICE_HANDLE:
676610696SDavid.Hollister@Sun.COM case PMCOUT_SATA_COMPLETION:
676710696SDavid.Hollister@Sun.COM case PMCOUT_DEVICE_INFO:
676810696SDavid.Hollister@Sun.COM case PMCOUT_FW_FLASH_UPDATE:
676910696SDavid.Hollister@Sun.COM case PMCOUT_SSP_ABORT:
677010696SDavid.Hollister@Sun.COM case PMCOUT_SATA_ABORT:
677110696SDavid.Hollister@Sun.COM case PMCOUT_SAS_DIAG_MODE_START_END:
677210696SDavid.Hollister@Sun.COM case PMCOUT_SAS_HW_EVENT_ACK_ACK:
677310696SDavid.Hollister@Sun.COM case PMCOUT_SMP_ABORT:
677410696SDavid.Hollister@Sun.COM case PMCOUT_SET_DEVICE_STATE:
677510696SDavid.Hollister@Sun.COM case PMCOUT_GET_DEVICE_STATE:
677610696SDavid.Hollister@Sun.COM case PMCOUT_SET_DEVICE_INFO:
677710696SDavid.Hollister@Sun.COM offset = 2;
677810696SDavid.Hollister@Sun.COM break;
677910696SDavid.Hollister@Sun.COM
678010696SDavid.Hollister@Sun.COM case PMCOUT_LOCAL_PHY_CONTROL:
678110696SDavid.Hollister@Sun.COM case PMCOUT_SAS_DIAG_EXECUTE:
678210696SDavid.Hollister@Sun.COM case PMCOUT_PORT_CONTROL:
678310696SDavid.Hollister@Sun.COM offset = 3;
678410696SDavid.Hollister@Sun.COM break;
678510696SDavid.Hollister@Sun.COM
678610696SDavid.Hollister@Sun.COM case PMCOUT_GET_INFO:
678710696SDavid.Hollister@Sun.COM case PMCOUT_GET_VPD:
678810696SDavid.Hollister@Sun.COM case PMCOUT_SAS_ASSISTED_DISCOVERY_EVENT:
678910696SDavid.Hollister@Sun.COM case PMCOUT_SATA_ASSISTED_DISCOVERY_EVENT:
679010696SDavid.Hollister@Sun.COM case PMCOUT_SET_VPD:
679110696SDavid.Hollister@Sun.COM case PMCOUT_TWI:
679210696SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
679310696SDavid.Hollister@Sun.COM "Got response for deprecated opcode", iomb);
679410696SDavid.Hollister@Sun.COM return;
679510696SDavid.Hollister@Sun.COM
679610696SDavid.Hollister@Sun.COM default:
679710696SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
679810696SDavid.Hollister@Sun.COM "Got response for unknown opcode", iomb);
679910696SDavid.Hollister@Sun.COM return;
680010696SDavid.Hollister@Sun.COM }
680110696SDavid.Hollister@Sun.COM
680210696SDavid.Hollister@Sun.COM if (LE_32(iomb[offset]) != PMCOUT_STATUS_OK) {
680310696SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
680410696SDavid.Hollister@Sun.COM "bad status on TAG_TYPE_NONE command", iomb);
680510696SDavid.Hollister@Sun.COM }
680610696SDavid.Hollister@Sun.COM }
680710696SDavid.Hollister@Sun.COM
680810696SDavid.Hollister@Sun.COM /*
680910696SDavid.Hollister@Sun.COM * Called with statlock held
681010696SDavid.Hollister@Sun.COM */
681110696SDavid.Hollister@Sun.COM void
pmcs_clear_xp(pmcs_hw_t * pwp,pmcs_xscsi_t * xp)681210696SDavid.Hollister@Sun.COM pmcs_clear_xp(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
681310696SDavid.Hollister@Sun.COM {
681410696SDavid.Hollister@Sun.COM _NOTE(ARGUNUSED(pwp));
681510696SDavid.Hollister@Sun.COM
681610696SDavid.Hollister@Sun.COM ASSERT(mutex_owned(&xp->statlock));
681710696SDavid.Hollister@Sun.COM
681811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, "%s: Device 0x%p is gone.",
681911048SDavid.Hollister@Sun.COM __func__, (void *)xp);
682010696SDavid.Hollister@Sun.COM
682110696SDavid.Hollister@Sun.COM xp->special_running = 0;
682210696SDavid.Hollister@Sun.COM xp->recovering = 0;
682310696SDavid.Hollister@Sun.COM xp->recover_wait = 0;
682410696SDavid.Hollister@Sun.COM xp->draining = 0;
682510696SDavid.Hollister@Sun.COM xp->new = 0;
682610696SDavid.Hollister@Sun.COM xp->assigned = 0;
682710696SDavid.Hollister@Sun.COM xp->dev_state = 0;
682810696SDavid.Hollister@Sun.COM xp->tagmap = 0;
682910696SDavid.Hollister@Sun.COM xp->dev_gone = 1;
683010696SDavid.Hollister@Sun.COM xp->event_recovery = 0;
683110696SDavid.Hollister@Sun.COM xp->dtype = NOTHING;
683210696SDavid.Hollister@Sun.COM xp->wq_recovery_tail = NULL;
683310696SDavid.Hollister@Sun.COM /* Don't clear xp->phy */
683410696SDavid.Hollister@Sun.COM /* Don't clear xp->actv_cnt */
683511347SRamana.Srikanth@Sun.COM /* Don't clear xp->actv_pkts */
683610755SJesse.Butler@Sun.COM
683710755SJesse.Butler@Sun.COM /*
683810755SJesse.Butler@Sun.COM * Flush all target queues
683910755SJesse.Butler@Sun.COM */
684010755SJesse.Butler@Sun.COM pmcs_flush_target_queues(pwp, xp, PMCS_TGT_ALL_QUEUES);
684110696SDavid.Hollister@Sun.COM }
684210696SDavid.Hollister@Sun.COM
684310696SDavid.Hollister@Sun.COM static int
pmcs_smp_function_result(pmcs_hw_t * pwp,smp_response_frame_t * srf)684410696SDavid.Hollister@Sun.COM pmcs_smp_function_result(pmcs_hw_t *pwp, smp_response_frame_t *srf)
684510696SDavid.Hollister@Sun.COM {
684610696SDavid.Hollister@Sun.COM int result = srf->srf_result;
684710696SDavid.Hollister@Sun.COM
684810696SDavid.Hollister@Sun.COM switch (result) {
684910696SDavid.Hollister@Sun.COM case SMP_RES_UNKNOWN_FUNCTION:
685011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
685111048SDavid.Hollister@Sun.COM "%s: SMP DISCOVER Response "
685210696SDavid.Hollister@Sun.COM "Function Result: Unknown SMP Function(0x%x)",
685310696SDavid.Hollister@Sun.COM __func__, result);
685410696SDavid.Hollister@Sun.COM break;
685510696SDavid.Hollister@Sun.COM case SMP_RES_FUNCTION_FAILED:
685611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
685711048SDavid.Hollister@Sun.COM "%s: SMP DISCOVER Response "
685810696SDavid.Hollister@Sun.COM "Function Result: SMP Function Failed(0x%x)",
685910696SDavid.Hollister@Sun.COM __func__, result);
686010696SDavid.Hollister@Sun.COM break;
686110696SDavid.Hollister@Sun.COM case SMP_RES_INVALID_REQUEST_FRAME_LENGTH:
686211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
686311048SDavid.Hollister@Sun.COM "%s: SMP DISCOVER Response "
686410696SDavid.Hollister@Sun.COM "Function Result: Invalid Request Frame Length(0x%x)",
686510696SDavid.Hollister@Sun.COM __func__, result);
686610696SDavid.Hollister@Sun.COM break;
686710696SDavid.Hollister@Sun.COM case SMP_RES_INCOMPLETE_DESCRIPTOR_LIST:
686811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
686911048SDavid.Hollister@Sun.COM "%s: SMP DISCOVER Response "
687010696SDavid.Hollister@Sun.COM "Function Result: Incomplete Descriptor List(0x%x)",
687110696SDavid.Hollister@Sun.COM __func__, result);
687210696SDavid.Hollister@Sun.COM break;
687310696SDavid.Hollister@Sun.COM case SMP_RES_PHY_DOES_NOT_EXIST:
687411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
687511048SDavid.Hollister@Sun.COM "%s: SMP DISCOVER Response "
687610696SDavid.Hollister@Sun.COM "Function Result: PHY does not exist(0x%x)",
687710696SDavid.Hollister@Sun.COM __func__, result);
687810696SDavid.Hollister@Sun.COM break;
687910696SDavid.Hollister@Sun.COM case SMP_RES_PHY_VACANT:
688011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
688111048SDavid.Hollister@Sun.COM "%s: SMP DISCOVER Response "
688210696SDavid.Hollister@Sun.COM "Function Result: PHY Vacant(0x%x)",
688310696SDavid.Hollister@Sun.COM __func__, result);
688410696SDavid.Hollister@Sun.COM break;
688510696SDavid.Hollister@Sun.COM default:
688611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
688711048SDavid.Hollister@Sun.COM "%s: SMP DISCOVER Response "
688810696SDavid.Hollister@Sun.COM "Function Result: (0x%x)",
688910696SDavid.Hollister@Sun.COM __func__, result);
689010696SDavid.Hollister@Sun.COM break;
689110696SDavid.Hollister@Sun.COM }
689210696SDavid.Hollister@Sun.COM
689310696SDavid.Hollister@Sun.COM return (result);
689410696SDavid.Hollister@Sun.COM }
689510696SDavid.Hollister@Sun.COM
689610696SDavid.Hollister@Sun.COM /*
689710696SDavid.Hollister@Sun.COM * Do all the repetitive stuff necessary to setup for DMA
689810696SDavid.Hollister@Sun.COM *
689910696SDavid.Hollister@Sun.COM * pwp: Used for dip
690010696SDavid.Hollister@Sun.COM * dma_attr: ddi_dma_attr_t to use for the mapping
690110696SDavid.Hollister@Sun.COM * acch: ddi_acc_handle_t to use for the mapping
690210696SDavid.Hollister@Sun.COM * dmah: ddi_dma_handle_t to use
690310696SDavid.Hollister@Sun.COM * length: Amount of memory for mapping
690411185SSean.McEnroe@Sun.COM * kvap: Pointer filled in with kernel virtual address on successful return
690510696SDavid.Hollister@Sun.COM * dma_addr: Pointer filled in with DMA address on successful return
690610696SDavid.Hollister@Sun.COM */
690710696SDavid.Hollister@Sun.COM boolean_t
pmcs_dma_setup(pmcs_hw_t * pwp,ddi_dma_attr_t * dma_attr,ddi_acc_handle_t * acch,ddi_dma_handle_t * dmah,size_t length,caddr_t * kvap,uint64_t * dma_addr)690810696SDavid.Hollister@Sun.COM pmcs_dma_setup(pmcs_hw_t *pwp, ddi_dma_attr_t *dma_attr, ddi_acc_handle_t *acch,
690911185SSean.McEnroe@Sun.COM ddi_dma_handle_t *dmah, size_t length, caddr_t *kvap, uint64_t *dma_addr)
691010696SDavid.Hollister@Sun.COM {
691110696SDavid.Hollister@Sun.COM dev_info_t *dip = pwp->dip;
691210696SDavid.Hollister@Sun.COM ddi_dma_cookie_t cookie;
691310696SDavid.Hollister@Sun.COM size_t real_length;
691410696SDavid.Hollister@Sun.COM uint_t ddma_flag = DDI_DMA_CONSISTENT;
691510696SDavid.Hollister@Sun.COM uint_t ddabh_flag = DDI_DMA_CONSISTENT | DDI_DMA_RDWR;
691610696SDavid.Hollister@Sun.COM uint_t cookie_cnt;
691710696SDavid.Hollister@Sun.COM ddi_device_acc_attr_t mattr = {
691810696SDavid.Hollister@Sun.COM DDI_DEVICE_ATTR_V0,
691910696SDavid.Hollister@Sun.COM DDI_NEVERSWAP_ACC,
692010696SDavid.Hollister@Sun.COM DDI_STRICTORDER_ACC,
692110696SDavid.Hollister@Sun.COM DDI_DEFAULT_ACC
692210696SDavid.Hollister@Sun.COM };
692310696SDavid.Hollister@Sun.COM
692410696SDavid.Hollister@Sun.COM *acch = NULL;
692510696SDavid.Hollister@Sun.COM *dmah = NULL;
692610696SDavid.Hollister@Sun.COM
692710696SDavid.Hollister@Sun.COM if (ddi_dma_alloc_handle(dip, dma_attr, DDI_DMA_SLEEP, NULL, dmah) !=
692810696SDavid.Hollister@Sun.COM DDI_SUCCESS) {
692911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
693011048SDavid.Hollister@Sun.COM "Failed to allocate DMA handle");
693110696SDavid.Hollister@Sun.COM return (B_FALSE);
693210696SDavid.Hollister@Sun.COM }
693310696SDavid.Hollister@Sun.COM
693410696SDavid.Hollister@Sun.COM if (ddi_dma_mem_alloc(*dmah, length, &mattr, ddma_flag, DDI_DMA_SLEEP,
693511185SSean.McEnroe@Sun.COM NULL, kvap, &real_length, acch) != DDI_SUCCESS) {
693611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
693711048SDavid.Hollister@Sun.COM "Failed to allocate DMA mem");
693810696SDavid.Hollister@Sun.COM ddi_dma_free_handle(dmah);
693910696SDavid.Hollister@Sun.COM *dmah = NULL;
694010696SDavid.Hollister@Sun.COM return (B_FALSE);
694110696SDavid.Hollister@Sun.COM }
694210696SDavid.Hollister@Sun.COM
694311185SSean.McEnroe@Sun.COM if (ddi_dma_addr_bind_handle(*dmah, NULL, *kvap, real_length,
694410696SDavid.Hollister@Sun.COM ddabh_flag, DDI_DMA_SLEEP, NULL, &cookie, &cookie_cnt)
694510696SDavid.Hollister@Sun.COM != DDI_DMA_MAPPED) {
694611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "Failed to bind DMA");
694710696SDavid.Hollister@Sun.COM ddi_dma_free_handle(dmah);
694810696SDavid.Hollister@Sun.COM ddi_dma_mem_free(acch);
694910696SDavid.Hollister@Sun.COM *dmah = NULL;
695010696SDavid.Hollister@Sun.COM *acch = NULL;
695110696SDavid.Hollister@Sun.COM return (B_FALSE);
695210696SDavid.Hollister@Sun.COM }
695310696SDavid.Hollister@Sun.COM
695410696SDavid.Hollister@Sun.COM if (cookie_cnt != 1) {
695511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "Multiple cookies");
695610696SDavid.Hollister@Sun.COM if (ddi_dma_unbind_handle(*dmah) != DDI_SUCCESS) {
695711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "Condition "
695811048SDavid.Hollister@Sun.COM "failed at %s():%d", __func__, __LINE__);
695910696SDavid.Hollister@Sun.COM }
696010696SDavid.Hollister@Sun.COM ddi_dma_free_handle(dmah);
696110696SDavid.Hollister@Sun.COM ddi_dma_mem_free(acch);
696210696SDavid.Hollister@Sun.COM *dmah = NULL;
696310696SDavid.Hollister@Sun.COM *acch = NULL;
696410696SDavid.Hollister@Sun.COM return (B_FALSE);
696510696SDavid.Hollister@Sun.COM }
696610696SDavid.Hollister@Sun.COM
696710696SDavid.Hollister@Sun.COM *dma_addr = cookie.dmac_laddress;
696810696SDavid.Hollister@Sun.COM
696910696SDavid.Hollister@Sun.COM return (B_TRUE);
697010696SDavid.Hollister@Sun.COM }
697110696SDavid.Hollister@Sun.COM
697210696SDavid.Hollister@Sun.COM /*
697310696SDavid.Hollister@Sun.COM * Flush requested queues for a particular target. Called with statlock held
697410696SDavid.Hollister@Sun.COM */
697510696SDavid.Hollister@Sun.COM void
pmcs_flush_target_queues(pmcs_hw_t * pwp,pmcs_xscsi_t * tgt,uint8_t queues)697610696SDavid.Hollister@Sun.COM pmcs_flush_target_queues(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt, uint8_t queues)
697710696SDavid.Hollister@Sun.COM {
697811601SDavid.Hollister@Sun.COM pmcs_cmd_t *sp, *sp_next;
697910696SDavid.Hollister@Sun.COM pmcwork_t *pwrk;
698010696SDavid.Hollister@Sun.COM
698110696SDavid.Hollister@Sun.COM ASSERT(pwp != NULL);
698210696SDavid.Hollister@Sun.COM ASSERT(tgt != NULL);
698310696SDavid.Hollister@Sun.COM
698411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, tgt,
698510696SDavid.Hollister@Sun.COM "%s: Flushing queues (%d) for target 0x%p", __func__,
698610696SDavid.Hollister@Sun.COM queues, (void *)tgt);
698710696SDavid.Hollister@Sun.COM
698810696SDavid.Hollister@Sun.COM /*
698910696SDavid.Hollister@Sun.COM * Commands on the wait queue (or the special queue below) don't have
699010696SDavid.Hollister@Sun.COM * work structures associated with them.
699110696SDavid.Hollister@Sun.COM */
699210696SDavid.Hollister@Sun.COM if (queues & PMCS_TGT_WAIT_QUEUE) {
699310696SDavid.Hollister@Sun.COM mutex_enter(&tgt->wqlock);
699410696SDavid.Hollister@Sun.COM while ((sp = STAILQ_FIRST(&tgt->wq)) != NULL) {
699510696SDavid.Hollister@Sun.COM STAILQ_REMOVE(&tgt->wq, sp, pmcs_cmd, cmd_next);
699611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, tgt,
699710696SDavid.Hollister@Sun.COM "%s: Removing cmd 0x%p from wq for target 0x%p",
699810696SDavid.Hollister@Sun.COM __func__, (void *)sp, (void *)tgt);
699910696SDavid.Hollister@Sun.COM CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
700010696SDavid.Hollister@Sun.COM CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
700110696SDavid.Hollister@Sun.COM mutex_exit(&tgt->wqlock);
700210696SDavid.Hollister@Sun.COM pmcs_dma_unload(pwp, sp);
700310696SDavid.Hollister@Sun.COM mutex_enter(&pwp->cq_lock);
700410696SDavid.Hollister@Sun.COM STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
700512343Sdavid.hollister@oracle.com PMCS_CQ_RUN_LOCKED(pwp);
700610696SDavid.Hollister@Sun.COM mutex_exit(&pwp->cq_lock);
700710696SDavid.Hollister@Sun.COM mutex_enter(&tgt->wqlock);
700810696SDavid.Hollister@Sun.COM }
700910696SDavid.Hollister@Sun.COM mutex_exit(&tgt->wqlock);
701010696SDavid.Hollister@Sun.COM }
701110696SDavid.Hollister@Sun.COM
701210696SDavid.Hollister@Sun.COM /*
701310696SDavid.Hollister@Sun.COM * Commands on the active queue will have work structures associated
701410696SDavid.Hollister@Sun.COM * with them.
701510696SDavid.Hollister@Sun.COM */
701610696SDavid.Hollister@Sun.COM if (queues & PMCS_TGT_ACTIVE_QUEUE) {
701712078SJesse.Butler@Sun.COM mutex_exit(&tgt->statlock);
701810696SDavid.Hollister@Sun.COM mutex_enter(&tgt->aqlock);
701911601SDavid.Hollister@Sun.COM sp = STAILQ_FIRST(&tgt->aq);
702011601SDavid.Hollister@Sun.COM while (sp) {
702111601SDavid.Hollister@Sun.COM sp_next = STAILQ_NEXT(sp, cmd_next);
702212258Ssrikanth.suravajhala@oracle.com pwrk = pmcs_tag2wp(pwp, sp->cmd_tag, B_FALSE);
702311601SDavid.Hollister@Sun.COM
702411601SDavid.Hollister@Sun.COM /*
702511601SDavid.Hollister@Sun.COM * If we don't find a work structure, it's because
702611601SDavid.Hollister@Sun.COM * the command is already complete. If so, move on
702711601SDavid.Hollister@Sun.COM * to the next one.
702811601SDavid.Hollister@Sun.COM */
702911601SDavid.Hollister@Sun.COM if (pwrk == NULL) {
703011601SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG1, tgt->phy, tgt,
703111601SDavid.Hollister@Sun.COM "%s: Not removing cmd 0x%p (htag 0x%x) "
703211601SDavid.Hollister@Sun.COM "from aq", __func__, (void *)sp,
703311601SDavid.Hollister@Sun.COM sp->cmd_tag);
703411601SDavid.Hollister@Sun.COM sp = sp_next;
703511601SDavid.Hollister@Sun.COM continue;
703611601SDavid.Hollister@Sun.COM }
703711601SDavid.Hollister@Sun.COM
703810696SDavid.Hollister@Sun.COM STAILQ_REMOVE(&tgt->aq, sp, pmcs_cmd, cmd_next);
703911601SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG1, tgt->phy, tgt,
704011601SDavid.Hollister@Sun.COM "%s: Removing cmd 0x%p (htag 0x%x) from aq for "
704111601SDavid.Hollister@Sun.COM "target 0x%p", __func__, (void *)sp, sp->cmd_tag,
704211601SDavid.Hollister@Sun.COM (void *)tgt);
704310696SDavid.Hollister@Sun.COM mutex_exit(&tgt->aqlock);
704412462Sjesse.butler@oracle.com
704510696SDavid.Hollister@Sun.COM /*
704611601SDavid.Hollister@Sun.COM * Mark the work structure as dead and complete it
704710696SDavid.Hollister@Sun.COM */
704811601SDavid.Hollister@Sun.COM pwrk->dead = 1;
704911601SDavid.Hollister@Sun.COM CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
705011601SDavid.Hollister@Sun.COM CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
705111601SDavid.Hollister@Sun.COM pmcs_complete_work_impl(pwp, pwrk, NULL, 0);
705210696SDavid.Hollister@Sun.COM pmcs_dma_unload(pwp, sp);
705310696SDavid.Hollister@Sun.COM mutex_enter(&pwp->cq_lock);
705410696SDavid.Hollister@Sun.COM STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
705510696SDavid.Hollister@Sun.COM mutex_exit(&pwp->cq_lock);
705610696SDavid.Hollister@Sun.COM mutex_enter(&tgt->aqlock);
705711601SDavid.Hollister@Sun.COM sp = sp_next;
705810696SDavid.Hollister@Sun.COM }
705910696SDavid.Hollister@Sun.COM mutex_exit(&tgt->aqlock);
706012078SJesse.Butler@Sun.COM mutex_enter(&tgt->statlock);
706110696SDavid.Hollister@Sun.COM }
706210696SDavid.Hollister@Sun.COM
706310696SDavid.Hollister@Sun.COM if (queues & PMCS_TGT_SPECIAL_QUEUE) {
706410696SDavid.Hollister@Sun.COM while ((sp = STAILQ_FIRST(&tgt->sq)) != NULL) {
706510696SDavid.Hollister@Sun.COM STAILQ_REMOVE(&tgt->sq, sp, pmcs_cmd, cmd_next);
706611601SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG1, tgt->phy, tgt,
706710696SDavid.Hollister@Sun.COM "%s: Removing cmd 0x%p from sq for target 0x%p",
706810696SDavid.Hollister@Sun.COM __func__, (void *)sp, (void *)tgt);
706910696SDavid.Hollister@Sun.COM CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
707010696SDavid.Hollister@Sun.COM CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
707110696SDavid.Hollister@Sun.COM pmcs_dma_unload(pwp, sp);
707210696SDavid.Hollister@Sun.COM mutex_enter(&pwp->cq_lock);
707310696SDavid.Hollister@Sun.COM STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
707410696SDavid.Hollister@Sun.COM mutex_exit(&pwp->cq_lock);
707510696SDavid.Hollister@Sun.COM }
707610696SDavid.Hollister@Sun.COM }
707712258Ssrikanth.suravajhala@oracle.com
707812258Ssrikanth.suravajhala@oracle.com if (queues == PMCS_TGT_ALL_QUEUES) {
707912258Ssrikanth.suravajhala@oracle.com mutex_exit(&tgt->statlock);
708012462Sjesse.butler@oracle.com pmcs_flush_nonio_cmds(pwp, tgt);
708112258Ssrikanth.suravajhala@oracle.com mutex_enter(&tgt->statlock);
708212258Ssrikanth.suravajhala@oracle.com }
708312258Ssrikanth.suravajhala@oracle.com }
708412258Ssrikanth.suravajhala@oracle.com
708512258Ssrikanth.suravajhala@oracle.com /*
708612462Sjesse.butler@oracle.com * Flush non-IO commands for this target. This cleans up the off-queue
708712462Sjesse.butler@oracle.com * work with no pmcs_cmd_t associated.
708812258Ssrikanth.suravajhala@oracle.com */
708912462Sjesse.butler@oracle.com static void
pmcs_flush_nonio_cmds(pmcs_hw_t * pwp,pmcs_xscsi_t * tgt)709012462Sjesse.butler@oracle.com pmcs_flush_nonio_cmds(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt)
709112258Ssrikanth.suravajhala@oracle.com {
709212258Ssrikanth.suravajhala@oracle.com int i;
709312258Ssrikanth.suravajhala@oracle.com pmcwork_t *p;
709412258Ssrikanth.suravajhala@oracle.com
709512258Ssrikanth.suravajhala@oracle.com for (i = 0; i < pwp->max_cmd; i++) {
709612258Ssrikanth.suravajhala@oracle.com p = &pwp->work[i];
709712258Ssrikanth.suravajhala@oracle.com mutex_enter(&p->lock);
709812462Sjesse.butler@oracle.com if (p->xp != tgt) {
709912462Sjesse.butler@oracle.com mutex_exit(&p->lock);
710012462Sjesse.butler@oracle.com continue;
710112462Sjesse.butler@oracle.com }
710212258Ssrikanth.suravajhala@oracle.com if (p->htag & PMCS_TAG_NONIO_CMD) {
710312258Ssrikanth.suravajhala@oracle.com if (!PMCS_COMMAND_ACTIVE(p) || PMCS_COMMAND_DONE(p)) {
710412258Ssrikanth.suravajhala@oracle.com mutex_exit(&p->lock);
710512258Ssrikanth.suravajhala@oracle.com continue;
710612258Ssrikanth.suravajhala@oracle.com }
710712258Ssrikanth.suravajhala@oracle.com pmcs_prt(pwp, PMCS_PRT_DEBUG, p->phy, p->xp,
710812258Ssrikanth.suravajhala@oracle.com "%s: Completing non-io cmd with HTAG 0x%x",
710912258Ssrikanth.suravajhala@oracle.com __func__, p->htag);
711012258Ssrikanth.suravajhala@oracle.com pmcs_complete_work_impl(pwp, p, NULL, 0);
711112258Ssrikanth.suravajhala@oracle.com } else {
711212258Ssrikanth.suravajhala@oracle.com mutex_exit(&p->lock);
711312258Ssrikanth.suravajhala@oracle.com }
711412258Ssrikanth.suravajhala@oracle.com }
711510696SDavid.Hollister@Sun.COM }
711610696SDavid.Hollister@Sun.COM
711710696SDavid.Hollister@Sun.COM void
pmcs_complete_work_impl(pmcs_hw_t * pwp,pmcwork_t * pwrk,uint32_t * iomb,size_t amt)711810696SDavid.Hollister@Sun.COM pmcs_complete_work_impl(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *iomb,
711910696SDavid.Hollister@Sun.COM size_t amt)
712010696SDavid.Hollister@Sun.COM {
712112539Sjesse.butler@oracle.com pmcs_phy_t *pptr = NULL;
712212506Sjesse.butler@oracle.com
712310696SDavid.Hollister@Sun.COM switch (PMCS_TAG_TYPE(pwrk->htag)) {
712410696SDavid.Hollister@Sun.COM case PMCS_TAG_TYPE_CBACK:
712510696SDavid.Hollister@Sun.COM {
712610696SDavid.Hollister@Sun.COM pmcs_cb_t callback = (pmcs_cb_t)pwrk->ptr;
712710696SDavid.Hollister@Sun.COM (*callback)(pwp, pwrk, iomb);
712810696SDavid.Hollister@Sun.COM break;
712910696SDavid.Hollister@Sun.COM }
713010696SDavid.Hollister@Sun.COM case PMCS_TAG_TYPE_WAIT:
713110696SDavid.Hollister@Sun.COM if (pwrk->arg && iomb && amt) {
713210696SDavid.Hollister@Sun.COM (void) memcpy(pwrk->arg, iomb, amt);
713310696SDavid.Hollister@Sun.COM }
713410696SDavid.Hollister@Sun.COM cv_signal(&pwrk->sleep_cv);
713510696SDavid.Hollister@Sun.COM mutex_exit(&pwrk->lock);
713610696SDavid.Hollister@Sun.COM break;
713710696SDavid.Hollister@Sun.COM case PMCS_TAG_TYPE_NONE:
713810696SDavid.Hollister@Sun.COM #ifdef DEBUG
713910696SDavid.Hollister@Sun.COM pmcs_check_iomb_status(pwp, iomb);
714010696SDavid.Hollister@Sun.COM #endif
714112539Sjesse.butler@oracle.com pptr = pwrk->phy;
714212539Sjesse.butler@oracle.com pmcs_pwork(pwp, pwrk);
714312539Sjesse.butler@oracle.com
714412539Sjesse.butler@oracle.com /* If this was an abort all, clean up if needed */
714512539Sjesse.butler@oracle.com if ((pwrk->abt_htag == PMCS_ABT_HTAG_ALL) && (pptr != NULL)) {
714612539Sjesse.butler@oracle.com mutex_enter(&pptr->phy_lock);
714712539Sjesse.butler@oracle.com if (pptr->abort_all_start) {
714812539Sjesse.butler@oracle.com pptr->abort_all_start = 0;
714912539Sjesse.butler@oracle.com cv_signal(&pptr->abort_all_cv);
715012506Sjesse.butler@oracle.com }
715112539Sjesse.butler@oracle.com mutex_exit(&pptr->phy_lock);
715212539Sjesse.butler@oracle.com }
715310696SDavid.Hollister@Sun.COM break;
715410696SDavid.Hollister@Sun.COM default:
715510696SDavid.Hollister@Sun.COM /*
715610696SDavid.Hollister@Sun.COM * We will leak a structure here if we don't know
715710696SDavid.Hollister@Sun.COM * what happened
715810696SDavid.Hollister@Sun.COM */
715911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
716011048SDavid.Hollister@Sun.COM "%s: Unknown PMCS_TAG_TYPE (%x)",
716110696SDavid.Hollister@Sun.COM __func__, PMCS_TAG_TYPE(pwrk->htag));
716210696SDavid.Hollister@Sun.COM break;
716310696SDavid.Hollister@Sun.COM }
716410696SDavid.Hollister@Sun.COM }
716510696SDavid.Hollister@Sun.COM
716610696SDavid.Hollister@Sun.COM /*
716710696SDavid.Hollister@Sun.COM * Determine if iport still has targets. During detach(9E), if SCSA is
716810696SDavid.Hollister@Sun.COM * successfull in its guarantee of tran_tgt_free(9E) before detach(9E),
716910696SDavid.Hollister@Sun.COM * this should always return B_FALSE.
717010696SDavid.Hollister@Sun.COM */
717110696SDavid.Hollister@Sun.COM boolean_t
pmcs_iport_has_targets(pmcs_hw_t * pwp,pmcs_iport_t * iport)717210696SDavid.Hollister@Sun.COM pmcs_iport_has_targets(pmcs_hw_t *pwp, pmcs_iport_t *iport)
717310696SDavid.Hollister@Sun.COM {
717410696SDavid.Hollister@Sun.COM pmcs_xscsi_t *xp;
717510696SDavid.Hollister@Sun.COM int i;
717610696SDavid.Hollister@Sun.COM
717710696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
717810696SDavid.Hollister@Sun.COM
717910696SDavid.Hollister@Sun.COM if (!pwp->targets || !pwp->max_dev) {
718010696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
718110696SDavid.Hollister@Sun.COM return (B_FALSE);
718210696SDavid.Hollister@Sun.COM }
718310696SDavid.Hollister@Sun.COM
718410696SDavid.Hollister@Sun.COM for (i = 0; i < pwp->max_dev; i++) {
718510696SDavid.Hollister@Sun.COM xp = pwp->targets[i];
718610696SDavid.Hollister@Sun.COM if ((xp == NULL) || (xp->phy == NULL) ||
718710696SDavid.Hollister@Sun.COM (xp->phy->iport != iport)) {
718810696SDavid.Hollister@Sun.COM continue;
718910696SDavid.Hollister@Sun.COM }
719010696SDavid.Hollister@Sun.COM
719110696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
719210696SDavid.Hollister@Sun.COM return (B_TRUE);
719310696SDavid.Hollister@Sun.COM }
719410696SDavid.Hollister@Sun.COM
719510696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
719610696SDavid.Hollister@Sun.COM return (B_FALSE);
719710696SDavid.Hollister@Sun.COM }
719810696SDavid.Hollister@Sun.COM
719910696SDavid.Hollister@Sun.COM /*
720010696SDavid.Hollister@Sun.COM * Called with softstate lock held
720110696SDavid.Hollister@Sun.COM */
720210696SDavid.Hollister@Sun.COM void
pmcs_destroy_target(pmcs_xscsi_t * target)720310696SDavid.Hollister@Sun.COM pmcs_destroy_target(pmcs_xscsi_t *target)
720410696SDavid.Hollister@Sun.COM {
720510696SDavid.Hollister@Sun.COM pmcs_hw_t *pwp = target->pwp;
720610696SDavid.Hollister@Sun.COM pmcs_iport_t *iport;
720710696SDavid.Hollister@Sun.COM
720810696SDavid.Hollister@Sun.COM ASSERT(pwp);
720910696SDavid.Hollister@Sun.COM ASSERT(mutex_owned(&pwp->lock));
721010696SDavid.Hollister@Sun.COM
721110696SDavid.Hollister@Sun.COM if (!target->ua) {
721211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, target,
721311048SDavid.Hollister@Sun.COM "%s: target %p iport address is null",
721410696SDavid.Hollister@Sun.COM __func__, (void *)target);
721510696SDavid.Hollister@Sun.COM }
721610696SDavid.Hollister@Sun.COM
721710696SDavid.Hollister@Sun.COM iport = pmcs_get_iport_by_ua(pwp, target->ua);
721810696SDavid.Hollister@Sun.COM if (iport == NULL) {
721911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, target,
722010696SDavid.Hollister@Sun.COM "%s: no iport associated with tgt(0x%p)",
722110696SDavid.Hollister@Sun.COM __func__, (void *)target);
722210696SDavid.Hollister@Sun.COM return;
722310696SDavid.Hollister@Sun.COM }
722410696SDavid.Hollister@Sun.COM
722511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, target,
722610696SDavid.Hollister@Sun.COM "%s: free target %p", __func__, (void *)target);
722710696SDavid.Hollister@Sun.COM if (target->ua) {
722810696SDavid.Hollister@Sun.COM strfree(target->ua);
722910696SDavid.Hollister@Sun.COM }
723010696SDavid.Hollister@Sun.COM
723110696SDavid.Hollister@Sun.COM mutex_destroy(&target->wqlock);
723210696SDavid.Hollister@Sun.COM mutex_destroy(&target->aqlock);
723310696SDavid.Hollister@Sun.COM mutex_destroy(&target->statlock);
723410696SDavid.Hollister@Sun.COM cv_destroy(&target->reset_cv);
723510696SDavid.Hollister@Sun.COM cv_destroy(&target->abort_cv);
723610696SDavid.Hollister@Sun.COM ddi_soft_state_bystr_fini(&target->lun_sstate);
723710696SDavid.Hollister@Sun.COM ddi_soft_state_bystr_free(iport->tgt_sstate, target->unit_address);
723810696SDavid.Hollister@Sun.COM pmcs_rele_iport(iport);
723910696SDavid.Hollister@Sun.COM }
724010696SDavid.Hollister@Sun.COM
724110696SDavid.Hollister@Sun.COM /*
724210696SDavid.Hollister@Sun.COM * pmcs_lock_phy_impl
724310696SDavid.Hollister@Sun.COM *
724410696SDavid.Hollister@Sun.COM * This function is what does the actual work for pmcs_lock_phy. It will
724510696SDavid.Hollister@Sun.COM * lock all PHYs from phyp down in a top-down fashion.
724610696SDavid.Hollister@Sun.COM *
724710696SDavid.Hollister@Sun.COM * Locking notes:
724810696SDavid.Hollister@Sun.COM * 1. level starts from 0 for the PHY ("parent") that's passed in. It is
724910696SDavid.Hollister@Sun.COM * not a reflection of the actual level of the PHY in the SAS topology.
725010696SDavid.Hollister@Sun.COM * 2. If parent is an expander, then parent is locked along with all its
725110696SDavid.Hollister@Sun.COM * descendents.
725210696SDavid.Hollister@Sun.COM * 3. Expander subsidiary PHYs at level 0 are not locked. It is the
725310696SDavid.Hollister@Sun.COM * responsibility of the caller to individually lock expander subsidiary PHYs
725410696SDavid.Hollister@Sun.COM * at level 0 if necessary.
725510696SDavid.Hollister@Sun.COM * 4. Siblings at level 0 are not traversed due to the possibility that we're
725610696SDavid.Hollister@Sun.COM * locking a PHY on the dead list. The siblings could be pointing to invalid
725710696SDavid.Hollister@Sun.COM * PHYs. We don't lock siblings at level 0 anyway.
725810696SDavid.Hollister@Sun.COM */
725910696SDavid.Hollister@Sun.COM static void
pmcs_lock_phy_impl(pmcs_phy_t * phyp,int level)726010696SDavid.Hollister@Sun.COM pmcs_lock_phy_impl(pmcs_phy_t *phyp, int level)
726110696SDavid.Hollister@Sun.COM {
726210696SDavid.Hollister@Sun.COM pmcs_phy_t *tphyp;
726310696SDavid.Hollister@Sun.COM
726410696SDavid.Hollister@Sun.COM ASSERT((phyp->dtype == SAS) || (phyp->dtype == SATA) ||
726510696SDavid.Hollister@Sun.COM (phyp->dtype == EXPANDER) || (phyp->dtype == NOTHING));
726610696SDavid.Hollister@Sun.COM
726710696SDavid.Hollister@Sun.COM /*
726810696SDavid.Hollister@Sun.COM * Start walking the PHYs.
726910696SDavid.Hollister@Sun.COM */
727010696SDavid.Hollister@Sun.COM tphyp = phyp;
727110696SDavid.Hollister@Sun.COM while (tphyp) {
727210696SDavid.Hollister@Sun.COM /*
727310696SDavid.Hollister@Sun.COM * If we're at the top level, only lock ourselves. For anything
727410696SDavid.Hollister@Sun.COM * at level > 0, traverse children while locking everything.
727510696SDavid.Hollister@Sun.COM */
727610696SDavid.Hollister@Sun.COM if ((level > 0) || (tphyp == phyp)) {
727711048SDavid.Hollister@Sun.COM pmcs_prt(tphyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, tphyp,
727811048SDavid.Hollister@Sun.COM NULL, "%s: PHY 0x%p parent 0x%p path %s lvl %d",
727910696SDavid.Hollister@Sun.COM __func__, (void *)tphyp, (void *)tphyp->parent,
728010696SDavid.Hollister@Sun.COM tphyp->path, level);
728110696SDavid.Hollister@Sun.COM mutex_enter(&tphyp->phy_lock);
728210696SDavid.Hollister@Sun.COM
728310696SDavid.Hollister@Sun.COM if (tphyp->children) {
728410696SDavid.Hollister@Sun.COM pmcs_lock_phy_impl(tphyp->children, level + 1);
728510696SDavid.Hollister@Sun.COM }
728610696SDavid.Hollister@Sun.COM }
728710696SDavid.Hollister@Sun.COM
728810696SDavid.Hollister@Sun.COM if (level == 0) {
728910696SDavid.Hollister@Sun.COM return;
729010696SDavid.Hollister@Sun.COM }
729110696SDavid.Hollister@Sun.COM
729210696SDavid.Hollister@Sun.COM tphyp = tphyp->sibling;
729310696SDavid.Hollister@Sun.COM }
729410696SDavid.Hollister@Sun.COM }
729510696SDavid.Hollister@Sun.COM
729610696SDavid.Hollister@Sun.COM /*
729710696SDavid.Hollister@Sun.COM * pmcs_lock_phy
729810696SDavid.Hollister@Sun.COM *
729910696SDavid.Hollister@Sun.COM * This function is responsible for locking a PHY and all its descendents
730010696SDavid.Hollister@Sun.COM */
730110696SDavid.Hollister@Sun.COM void
pmcs_lock_phy(pmcs_phy_t * phyp)730210696SDavid.Hollister@Sun.COM pmcs_lock_phy(pmcs_phy_t *phyp)
730310696SDavid.Hollister@Sun.COM {
730410696SDavid.Hollister@Sun.COM #ifdef DEBUG
730510696SDavid.Hollister@Sun.COM char *callername = NULL;
730610696SDavid.Hollister@Sun.COM ulong_t off;
730710696SDavid.Hollister@Sun.COM
730810696SDavid.Hollister@Sun.COM ASSERT(phyp != NULL);
730910696SDavid.Hollister@Sun.COM
731010696SDavid.Hollister@Sun.COM callername = modgetsymname((uintptr_t)caller(), &off);
731110696SDavid.Hollister@Sun.COM
731210696SDavid.Hollister@Sun.COM if (callername == NULL) {
731311048SDavid.Hollister@Sun.COM pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
731410696SDavid.Hollister@Sun.COM "%s: PHY 0x%p path %s caller: unknown", __func__,
731510696SDavid.Hollister@Sun.COM (void *)phyp, phyp->path);
731610696SDavid.Hollister@Sun.COM } else {
731711048SDavid.Hollister@Sun.COM pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
731810696SDavid.Hollister@Sun.COM "%s: PHY 0x%p path %s caller: %s+%lx", __func__,
731910696SDavid.Hollister@Sun.COM (void *)phyp, phyp->path, callername, off);
732010696SDavid.Hollister@Sun.COM }
732110696SDavid.Hollister@Sun.COM #else
732211048SDavid.Hollister@Sun.COM pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
732310696SDavid.Hollister@Sun.COM "%s: PHY 0x%p path %s", __func__, (void *)phyp, phyp->path);
732410696SDavid.Hollister@Sun.COM #endif
732510696SDavid.Hollister@Sun.COM pmcs_lock_phy_impl(phyp, 0);
732610696SDavid.Hollister@Sun.COM }
732710696SDavid.Hollister@Sun.COM
732810696SDavid.Hollister@Sun.COM /*
732910696SDavid.Hollister@Sun.COM * pmcs_unlock_phy_impl
733010696SDavid.Hollister@Sun.COM *
733110696SDavid.Hollister@Sun.COM * Unlock all PHYs from phyp down in a bottom-up fashion.
733210696SDavid.Hollister@Sun.COM */
733310696SDavid.Hollister@Sun.COM static void
pmcs_unlock_phy_impl(pmcs_phy_t * phyp,int level)733410696SDavid.Hollister@Sun.COM pmcs_unlock_phy_impl(pmcs_phy_t *phyp, int level)
733510696SDavid.Hollister@Sun.COM {
733610696SDavid.Hollister@Sun.COM pmcs_phy_t *phy_next;
733710696SDavid.Hollister@Sun.COM
733810696SDavid.Hollister@Sun.COM ASSERT((phyp->dtype == SAS) || (phyp->dtype == SATA) ||
733910696SDavid.Hollister@Sun.COM (phyp->dtype == EXPANDER) || (phyp->dtype == NOTHING));
734010696SDavid.Hollister@Sun.COM
734110696SDavid.Hollister@Sun.COM /*
734210696SDavid.Hollister@Sun.COM * Recurse down to the bottom PHYs
734310696SDavid.Hollister@Sun.COM */
734410696SDavid.Hollister@Sun.COM if (level == 0) {
734510696SDavid.Hollister@Sun.COM if (phyp->children) {
734610696SDavid.Hollister@Sun.COM pmcs_unlock_phy_impl(phyp->children, level + 1);
734710696SDavid.Hollister@Sun.COM }
734810696SDavid.Hollister@Sun.COM } else {
734910696SDavid.Hollister@Sun.COM phy_next = phyp;
735010696SDavid.Hollister@Sun.COM while (phy_next) {
735110696SDavid.Hollister@Sun.COM if (phy_next->children) {
735210696SDavid.Hollister@Sun.COM pmcs_unlock_phy_impl(phy_next->children,
735310696SDavid.Hollister@Sun.COM level + 1);
735410696SDavid.Hollister@Sun.COM }
735510696SDavid.Hollister@Sun.COM phy_next = phy_next->sibling;
735610696SDavid.Hollister@Sun.COM }
735710696SDavid.Hollister@Sun.COM }
735810696SDavid.Hollister@Sun.COM
735910696SDavid.Hollister@Sun.COM /*
736010696SDavid.Hollister@Sun.COM * Iterate through PHYs unlocking all at level > 0 as well the top PHY
736110696SDavid.Hollister@Sun.COM */
736210696SDavid.Hollister@Sun.COM phy_next = phyp;
736310696SDavid.Hollister@Sun.COM while (phy_next) {
736410696SDavid.Hollister@Sun.COM if ((level > 0) || (phy_next == phyp)) {
736510696SDavid.Hollister@Sun.COM pmcs_prt(phy_next->pwp, PMCS_PRT_DEBUG_PHY_LOCKING,
736611048SDavid.Hollister@Sun.COM phy_next, NULL,
736710696SDavid.Hollister@Sun.COM "%s: PHY 0x%p parent 0x%p path %s lvl %d",
736810696SDavid.Hollister@Sun.COM __func__, (void *)phy_next,
736910696SDavid.Hollister@Sun.COM (void *)phy_next->parent, phy_next->path, level);
737010696SDavid.Hollister@Sun.COM mutex_exit(&phy_next->phy_lock);
737110696SDavid.Hollister@Sun.COM }
737210696SDavid.Hollister@Sun.COM
737310696SDavid.Hollister@Sun.COM if (level == 0) {
737410696SDavid.Hollister@Sun.COM return;
737510696SDavid.Hollister@Sun.COM }
737610696SDavid.Hollister@Sun.COM
737710696SDavid.Hollister@Sun.COM phy_next = phy_next->sibling;
737810696SDavid.Hollister@Sun.COM }
737910696SDavid.Hollister@Sun.COM }
738010696SDavid.Hollister@Sun.COM
738110696SDavid.Hollister@Sun.COM /*
738210696SDavid.Hollister@Sun.COM * pmcs_unlock_phy
738310696SDavid.Hollister@Sun.COM *
738410696SDavid.Hollister@Sun.COM * Unlock a PHY and all its descendents
738510696SDavid.Hollister@Sun.COM */
738610696SDavid.Hollister@Sun.COM void
pmcs_unlock_phy(pmcs_phy_t * phyp)738710696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pmcs_phy_t *phyp)
738810696SDavid.Hollister@Sun.COM {
738910696SDavid.Hollister@Sun.COM #ifdef DEBUG
739010696SDavid.Hollister@Sun.COM char *callername = NULL;
739110696SDavid.Hollister@Sun.COM ulong_t off;
739210696SDavid.Hollister@Sun.COM
739310696SDavid.Hollister@Sun.COM ASSERT(phyp != NULL);
739410696SDavid.Hollister@Sun.COM
739510696SDavid.Hollister@Sun.COM callername = modgetsymname((uintptr_t)caller(), &off);
739610696SDavid.Hollister@Sun.COM
739710696SDavid.Hollister@Sun.COM if (callername == NULL) {
739811048SDavid.Hollister@Sun.COM pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
739910696SDavid.Hollister@Sun.COM "%s: PHY 0x%p path %s caller: unknown", __func__,
740010696SDavid.Hollister@Sun.COM (void *)phyp, phyp->path);
740110696SDavid.Hollister@Sun.COM } else {
740211048SDavid.Hollister@Sun.COM pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
740310696SDavid.Hollister@Sun.COM "%s: PHY 0x%p path %s caller: %s+%lx", __func__,
740410696SDavid.Hollister@Sun.COM (void *)phyp, phyp->path, callername, off);
740510696SDavid.Hollister@Sun.COM }
740610696SDavid.Hollister@Sun.COM #else
740711048SDavid.Hollister@Sun.COM pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING, phyp, NULL,
740810696SDavid.Hollister@Sun.COM "%s: PHY 0x%p path %s", __func__, (void *)phyp, phyp->path);
740910696SDavid.Hollister@Sun.COM #endif
741010696SDavid.Hollister@Sun.COM pmcs_unlock_phy_impl(phyp, 0);
741110696SDavid.Hollister@Sun.COM }
741210696SDavid.Hollister@Sun.COM
741310696SDavid.Hollister@Sun.COM /*
741410696SDavid.Hollister@Sun.COM * pmcs_get_root_phy
741510696SDavid.Hollister@Sun.COM *
741610696SDavid.Hollister@Sun.COM * For a given phy pointer return its root phy.
741711601SDavid.Hollister@Sun.COM * This function must only be called during discovery in order to ensure that
741811601SDavid.Hollister@Sun.COM * the chain of PHYs from phyp up to the root PHY doesn't change.
741910696SDavid.Hollister@Sun.COM */
742010696SDavid.Hollister@Sun.COM pmcs_phy_t *
pmcs_get_root_phy(pmcs_phy_t * phyp)742110696SDavid.Hollister@Sun.COM pmcs_get_root_phy(pmcs_phy_t *phyp)
742210696SDavid.Hollister@Sun.COM {
742310696SDavid.Hollister@Sun.COM ASSERT(phyp);
742410696SDavid.Hollister@Sun.COM
742510696SDavid.Hollister@Sun.COM while (phyp) {
742610696SDavid.Hollister@Sun.COM if (IS_ROOT_PHY(phyp)) {
742710696SDavid.Hollister@Sun.COM break;
742810696SDavid.Hollister@Sun.COM }
742910696SDavid.Hollister@Sun.COM phyp = phyp->parent;
743010696SDavid.Hollister@Sun.COM }
743110696SDavid.Hollister@Sun.COM
743210696SDavid.Hollister@Sun.COM return (phyp);
743310696SDavid.Hollister@Sun.COM }
743410696SDavid.Hollister@Sun.COM
743510696SDavid.Hollister@Sun.COM /*
743610696SDavid.Hollister@Sun.COM * pmcs_free_dma_chunklist
743710696SDavid.Hollister@Sun.COM *
743810696SDavid.Hollister@Sun.COM * Free DMA S/G chunk list
743910696SDavid.Hollister@Sun.COM */
744010696SDavid.Hollister@Sun.COM void
pmcs_free_dma_chunklist(pmcs_hw_t * pwp)744110696SDavid.Hollister@Sun.COM pmcs_free_dma_chunklist(pmcs_hw_t *pwp)
744210696SDavid.Hollister@Sun.COM {
744310696SDavid.Hollister@Sun.COM pmcs_chunk_t *pchunk;
744410696SDavid.Hollister@Sun.COM
744510696SDavid.Hollister@Sun.COM while (pwp->dma_chunklist) {
744610696SDavid.Hollister@Sun.COM pchunk = pwp->dma_chunklist;
744710696SDavid.Hollister@Sun.COM pwp->dma_chunklist = pwp->dma_chunklist->next;
744810696SDavid.Hollister@Sun.COM if (pchunk->dma_handle) {
744910696SDavid.Hollister@Sun.COM if (ddi_dma_unbind_handle(pchunk->dma_handle) !=
745010696SDavid.Hollister@Sun.COM DDI_SUCCESS) {
745111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
745211048SDavid.Hollister@Sun.COM "Condition failed at %s():%d",
745311048SDavid.Hollister@Sun.COM __func__, __LINE__);
745410696SDavid.Hollister@Sun.COM }
745510696SDavid.Hollister@Sun.COM ddi_dma_free_handle(&pchunk->dma_handle);
745610696SDavid.Hollister@Sun.COM ddi_dma_mem_free(&pchunk->acc_handle);
745710696SDavid.Hollister@Sun.COM }
745810696SDavid.Hollister@Sun.COM kmem_free(pchunk, sizeof (pmcs_chunk_t));
745910696SDavid.Hollister@Sun.COM }
746010696SDavid.Hollister@Sun.COM }
746110696SDavid.Hollister@Sun.COM
746210696SDavid.Hollister@Sun.COM /*ARGSUSED2*/
746310696SDavid.Hollister@Sun.COM int
pmcs_phy_constructor(void * buf,void * arg,int kmflags)746410696SDavid.Hollister@Sun.COM pmcs_phy_constructor(void *buf, void *arg, int kmflags)
746510696SDavid.Hollister@Sun.COM {
746610696SDavid.Hollister@Sun.COM pmcs_hw_t *pwp = (pmcs_hw_t *)arg;
746710696SDavid.Hollister@Sun.COM pmcs_phy_t *phyp = (pmcs_phy_t *)buf;
746810696SDavid.Hollister@Sun.COM
746910696SDavid.Hollister@Sun.COM mutex_init(&phyp->phy_lock, NULL, MUTEX_DRIVER,
747010696SDavid.Hollister@Sun.COM DDI_INTR_PRI(pwp->intr_pri));
747110696SDavid.Hollister@Sun.COM cv_init(&phyp->abort_all_cv, NULL, CV_DRIVER, NULL);
747210696SDavid.Hollister@Sun.COM return (0);
747310696SDavid.Hollister@Sun.COM }
747410696SDavid.Hollister@Sun.COM
747510696SDavid.Hollister@Sun.COM /*ARGSUSED1*/
747610696SDavid.Hollister@Sun.COM void
pmcs_phy_destructor(void * buf,void * arg)747710696SDavid.Hollister@Sun.COM pmcs_phy_destructor(void *buf, void *arg)
747810696SDavid.Hollister@Sun.COM {
747910696SDavid.Hollister@Sun.COM pmcs_phy_t *phyp = (pmcs_phy_t *)buf;
748010696SDavid.Hollister@Sun.COM
748110696SDavid.Hollister@Sun.COM cv_destroy(&phyp->abort_all_cv);
748210696SDavid.Hollister@Sun.COM mutex_destroy(&phyp->phy_lock);
748310696SDavid.Hollister@Sun.COM }
748410696SDavid.Hollister@Sun.COM
748510696SDavid.Hollister@Sun.COM /*
748610696SDavid.Hollister@Sun.COM * Free all PHYs from the kmem_cache starting at phyp as well as everything
748710696SDavid.Hollister@Sun.COM * on the dead_phys list.
748810696SDavid.Hollister@Sun.COM *
748910696SDavid.Hollister@Sun.COM * NOTE: This function does not free root PHYs as they are not allocated
749010696SDavid.Hollister@Sun.COM * from the kmem_cache.
749110696SDavid.Hollister@Sun.COM *
749210696SDavid.Hollister@Sun.COM * No PHY locks are acquired as this should only be called during DDI_DETACH
749310696SDavid.Hollister@Sun.COM * or soft reset (while pmcs interrupts are disabled).
749410696SDavid.Hollister@Sun.COM */
749510696SDavid.Hollister@Sun.COM void
pmcs_free_all_phys(pmcs_hw_t * pwp,pmcs_phy_t * phyp)749610696SDavid.Hollister@Sun.COM pmcs_free_all_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
749710696SDavid.Hollister@Sun.COM {
749812399SChris.Horne@Sun.COM pmcs_phy_t *tphyp, *nphyp, *cphyp;
749910696SDavid.Hollister@Sun.COM
750010696SDavid.Hollister@Sun.COM if (phyp == NULL) {
750110696SDavid.Hollister@Sun.COM return;
750210696SDavid.Hollister@Sun.COM }
750310696SDavid.Hollister@Sun.COM
750412399SChris.Horne@Sun.COM for (tphyp = phyp; tphyp; tphyp = nphyp) {
750510696SDavid.Hollister@Sun.COM nphyp = tphyp->sibling;
750612399SChris.Horne@Sun.COM cphyp = tphyp->children;
750712399SChris.Horne@Sun.COM
750812399SChris.Horne@Sun.COM if (cphyp) {
750910696SDavid.Hollister@Sun.COM tphyp->children = NULL;
751012399SChris.Horne@Sun.COM pmcs_free_all_phys(pwp, cphyp);
751112399SChris.Horne@Sun.COM }
751212399SChris.Horne@Sun.COM
751310696SDavid.Hollister@Sun.COM if (!IS_ROOT_PHY(tphyp)) {
751412668Ssrikanth.suravajhala@oracle.com tphyp->target_addr = NULL;
751510696SDavid.Hollister@Sun.COM kmem_cache_free(pwp->phy_cache, tphyp);
751610696SDavid.Hollister@Sun.COM }
751710696SDavid.Hollister@Sun.COM }
751810696SDavid.Hollister@Sun.COM
751912258Ssrikanth.suravajhala@oracle.com mutex_enter(&pwp->dead_phylist_lock);
752012399SChris.Horne@Sun.COM for (tphyp = pwp->dead_phys; tphyp; tphyp = nphyp) {
752112258Ssrikanth.suravajhala@oracle.com nphyp = tphyp->dead_next;
752212668Ssrikanth.suravajhala@oracle.com tphyp->target_addr = NULL;
752310696SDavid.Hollister@Sun.COM kmem_cache_free(pwp->phy_cache, tphyp);
752410696SDavid.Hollister@Sun.COM }
752510696SDavid.Hollister@Sun.COM pwp->dead_phys = NULL;
752612258Ssrikanth.suravajhala@oracle.com mutex_exit(&pwp->dead_phylist_lock);
752710696SDavid.Hollister@Sun.COM }
752810696SDavid.Hollister@Sun.COM
752910696SDavid.Hollister@Sun.COM /*
753010696SDavid.Hollister@Sun.COM * Free a list of PHYs linked together by the sibling pointer back to the
753110696SDavid.Hollister@Sun.COM * kmem cache from whence they came. This function does not recurse, so the
753210696SDavid.Hollister@Sun.COM * caller must ensure there are no children.
753310696SDavid.Hollister@Sun.COM */
753410696SDavid.Hollister@Sun.COM void
pmcs_free_phys(pmcs_hw_t * pwp,pmcs_phy_t * phyp)753510696SDavid.Hollister@Sun.COM pmcs_free_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
753610696SDavid.Hollister@Sun.COM {
753710696SDavid.Hollister@Sun.COM pmcs_phy_t *next_phy;
753810696SDavid.Hollister@Sun.COM
753910696SDavid.Hollister@Sun.COM while (phyp) {
754010696SDavid.Hollister@Sun.COM next_phy = phyp->sibling;
754110696SDavid.Hollister@Sun.COM ASSERT(!mutex_owned(&phyp->phy_lock));
754212668Ssrikanth.suravajhala@oracle.com phyp->target_addr = NULL;
754310696SDavid.Hollister@Sun.COM kmem_cache_free(pwp->phy_cache, phyp);
754410696SDavid.Hollister@Sun.COM phyp = next_phy;
754510696SDavid.Hollister@Sun.COM }
754610696SDavid.Hollister@Sun.COM }
754710696SDavid.Hollister@Sun.COM
754810696SDavid.Hollister@Sun.COM /*
754910696SDavid.Hollister@Sun.COM * Make a copy of an existing PHY structure. This is used primarily in
755010696SDavid.Hollister@Sun.COM * discovery to compare the contents of an existing PHY with what gets
755110696SDavid.Hollister@Sun.COM * reported back by an expander.
755210696SDavid.Hollister@Sun.COM *
755310696SDavid.Hollister@Sun.COM * This function must not be called from any context where sleeping is
755410696SDavid.Hollister@Sun.COM * not possible.
755510696SDavid.Hollister@Sun.COM *
755610696SDavid.Hollister@Sun.COM * The new PHY is returned unlocked.
755710696SDavid.Hollister@Sun.COM */
755810696SDavid.Hollister@Sun.COM static pmcs_phy_t *
pmcs_clone_phy(pmcs_phy_t * orig_phy)755910696SDavid.Hollister@Sun.COM pmcs_clone_phy(pmcs_phy_t *orig_phy)
756010696SDavid.Hollister@Sun.COM {
756110696SDavid.Hollister@Sun.COM pmcs_phy_t *local;
756210696SDavid.Hollister@Sun.COM
756310696SDavid.Hollister@Sun.COM local = kmem_cache_alloc(orig_phy->pwp->phy_cache, KM_SLEEP);
756410696SDavid.Hollister@Sun.COM
756510696SDavid.Hollister@Sun.COM /*
756610696SDavid.Hollister@Sun.COM * Go ahead and just copy everything...
756710696SDavid.Hollister@Sun.COM */
756810696SDavid.Hollister@Sun.COM *local = *orig_phy;
756912668Ssrikanth.suravajhala@oracle.com local->target_addr = &orig_phy->target;
757010696SDavid.Hollister@Sun.COM
757110696SDavid.Hollister@Sun.COM /*
757210696SDavid.Hollister@Sun.COM * But the following must be set appropriately for this copy
757310696SDavid.Hollister@Sun.COM */
757410696SDavid.Hollister@Sun.COM local->sibling = NULL;
757510696SDavid.Hollister@Sun.COM local->children = NULL;
757612807Ssrikanth.suravajhala@oracle.com local->target = NULL;
757710696SDavid.Hollister@Sun.COM mutex_init(&local->phy_lock, NULL, MUTEX_DRIVER,
757810696SDavid.Hollister@Sun.COM DDI_INTR_PRI(orig_phy->pwp->intr_pri));
757910696SDavid.Hollister@Sun.COM
758010696SDavid.Hollister@Sun.COM return (local);
758110696SDavid.Hollister@Sun.COM }
758210696SDavid.Hollister@Sun.COM
758310696SDavid.Hollister@Sun.COM int
pmcs_check_acc_handle(ddi_acc_handle_t handle)758410696SDavid.Hollister@Sun.COM pmcs_check_acc_handle(ddi_acc_handle_t handle)
758510696SDavid.Hollister@Sun.COM {
758610696SDavid.Hollister@Sun.COM ddi_fm_error_t de;
758710696SDavid.Hollister@Sun.COM
758810696SDavid.Hollister@Sun.COM if (handle == NULL) {
758910696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
759010696SDavid.Hollister@Sun.COM }
759110696SDavid.Hollister@Sun.COM ddi_fm_acc_err_get(handle, &de, DDI_FME_VER0);
759210696SDavid.Hollister@Sun.COM return (de.fme_status);
759310696SDavid.Hollister@Sun.COM }
759410696SDavid.Hollister@Sun.COM
759510696SDavid.Hollister@Sun.COM int
pmcs_check_dma_handle(ddi_dma_handle_t handle)759610696SDavid.Hollister@Sun.COM pmcs_check_dma_handle(ddi_dma_handle_t handle)
759710696SDavid.Hollister@Sun.COM {
759810696SDavid.Hollister@Sun.COM ddi_fm_error_t de;
759910696SDavid.Hollister@Sun.COM
760010696SDavid.Hollister@Sun.COM if (handle == NULL) {
760110696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
760210696SDavid.Hollister@Sun.COM }
760310696SDavid.Hollister@Sun.COM ddi_fm_dma_err_get(handle, &de, DDI_FME_VER0);
760410696SDavid.Hollister@Sun.COM return (de.fme_status);
760510696SDavid.Hollister@Sun.COM }
760610696SDavid.Hollister@Sun.COM
760710696SDavid.Hollister@Sun.COM
760810696SDavid.Hollister@Sun.COM void
pmcs_fm_ereport(pmcs_hw_t * pwp,char * detail)760910696SDavid.Hollister@Sun.COM pmcs_fm_ereport(pmcs_hw_t *pwp, char *detail)
761010696SDavid.Hollister@Sun.COM {
761110696SDavid.Hollister@Sun.COM uint64_t ena;
761210696SDavid.Hollister@Sun.COM char buf[FM_MAX_CLASS];
761310696SDavid.Hollister@Sun.COM
761410696SDavid.Hollister@Sun.COM (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", DDI_FM_DEVICE, detail);
761510696SDavid.Hollister@Sun.COM ena = fm_ena_generate(0, FM_ENA_FMT1);
761610696SDavid.Hollister@Sun.COM if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities)) {
761710696SDavid.Hollister@Sun.COM ddi_fm_ereport_post(pwp->dip, buf, ena, DDI_NOSLEEP,
761810696SDavid.Hollister@Sun.COM FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, NULL);
761910696SDavid.Hollister@Sun.COM }
762010696SDavid.Hollister@Sun.COM }
762110696SDavid.Hollister@Sun.COM
762210696SDavid.Hollister@Sun.COM int
pmcs_check_acc_dma_handle(pmcs_hw_t * pwp)762310696SDavid.Hollister@Sun.COM pmcs_check_acc_dma_handle(pmcs_hw_t *pwp)
762410696SDavid.Hollister@Sun.COM {
762510696SDavid.Hollister@Sun.COM pmcs_chunk_t *pchunk;
762610696SDavid.Hollister@Sun.COM int i;
762710696SDavid.Hollister@Sun.COM
762810696SDavid.Hollister@Sun.COM /* check all acc & dma handles allocated in attach */
762910696SDavid.Hollister@Sun.COM if ((pmcs_check_acc_handle(pwp->pci_acc_handle) != DDI_SUCCESS) ||
763010696SDavid.Hollister@Sun.COM (pmcs_check_acc_handle(pwp->msg_acc_handle) != DDI_SUCCESS) ||
763110696SDavid.Hollister@Sun.COM (pmcs_check_acc_handle(pwp->top_acc_handle) != DDI_SUCCESS) ||
763210696SDavid.Hollister@Sun.COM (pmcs_check_acc_handle(pwp->mpi_acc_handle) != DDI_SUCCESS) ||
763310696SDavid.Hollister@Sun.COM (pmcs_check_acc_handle(pwp->gsm_acc_handle) != DDI_SUCCESS)) {
763410696SDavid.Hollister@Sun.COM goto check_failed;
763510696SDavid.Hollister@Sun.COM }
763610696SDavid.Hollister@Sun.COM
763710696SDavid.Hollister@Sun.COM for (i = 0; i < PMCS_NIQ; i++) {
763810696SDavid.Hollister@Sun.COM if ((pmcs_check_dma_handle(
763910696SDavid.Hollister@Sun.COM pwp->iqp_handles[i]) != DDI_SUCCESS) ||
764010696SDavid.Hollister@Sun.COM (pmcs_check_acc_handle(
764110696SDavid.Hollister@Sun.COM pwp->iqp_acchdls[i]) != DDI_SUCCESS)) {
764210696SDavid.Hollister@Sun.COM goto check_failed;
764310696SDavid.Hollister@Sun.COM }
764410696SDavid.Hollister@Sun.COM }
764510696SDavid.Hollister@Sun.COM
764610696SDavid.Hollister@Sun.COM for (i = 0; i < PMCS_NOQ; i++) {
764710696SDavid.Hollister@Sun.COM if ((pmcs_check_dma_handle(
764810696SDavid.Hollister@Sun.COM pwp->oqp_handles[i]) != DDI_SUCCESS) ||
764910696SDavid.Hollister@Sun.COM (pmcs_check_acc_handle(
765010696SDavid.Hollister@Sun.COM pwp->oqp_acchdls[i]) != DDI_SUCCESS)) {
765110696SDavid.Hollister@Sun.COM goto check_failed;
765210696SDavid.Hollister@Sun.COM }
765310696SDavid.Hollister@Sun.COM }
765410696SDavid.Hollister@Sun.COM
765510696SDavid.Hollister@Sun.COM if ((pmcs_check_dma_handle(pwp->cip_handles) != DDI_SUCCESS) ||
765610696SDavid.Hollister@Sun.COM (pmcs_check_acc_handle(pwp->cip_acchdls) != DDI_SUCCESS)) {
765710696SDavid.Hollister@Sun.COM goto check_failed;
765810696SDavid.Hollister@Sun.COM }
765910696SDavid.Hollister@Sun.COM
766010696SDavid.Hollister@Sun.COM if (pwp->fwlog &&
766110696SDavid.Hollister@Sun.COM ((pmcs_check_dma_handle(pwp->fwlog_hndl) != DDI_SUCCESS) ||
766210696SDavid.Hollister@Sun.COM (pmcs_check_acc_handle(pwp->fwlog_acchdl) != DDI_SUCCESS))) {
766310696SDavid.Hollister@Sun.COM goto check_failed;
766410696SDavid.Hollister@Sun.COM }
766510696SDavid.Hollister@Sun.COM
766610696SDavid.Hollister@Sun.COM if (pwp->regdump_hndl && pwp->regdump_acchdl &&
766710696SDavid.Hollister@Sun.COM ((pmcs_check_dma_handle(pwp->regdump_hndl) != DDI_SUCCESS) ||
766810696SDavid.Hollister@Sun.COM (pmcs_check_acc_handle(pwp->regdump_acchdl)
766910696SDavid.Hollister@Sun.COM != DDI_SUCCESS))) {
767010696SDavid.Hollister@Sun.COM goto check_failed;
767110696SDavid.Hollister@Sun.COM }
767210696SDavid.Hollister@Sun.COM
767310696SDavid.Hollister@Sun.COM
767410696SDavid.Hollister@Sun.COM pchunk = pwp->dma_chunklist;
767510696SDavid.Hollister@Sun.COM while (pchunk) {
767610696SDavid.Hollister@Sun.COM if ((pmcs_check_acc_handle(pchunk->acc_handle)
767710696SDavid.Hollister@Sun.COM != DDI_SUCCESS) ||
767810696SDavid.Hollister@Sun.COM (pmcs_check_dma_handle(pchunk->dma_handle)
767910696SDavid.Hollister@Sun.COM != DDI_SUCCESS)) {
768010696SDavid.Hollister@Sun.COM goto check_failed;
768110696SDavid.Hollister@Sun.COM }
768210696SDavid.Hollister@Sun.COM pchunk = pchunk->next;
768310696SDavid.Hollister@Sun.COM }
768410696SDavid.Hollister@Sun.COM
768510696SDavid.Hollister@Sun.COM return (0);
768610696SDavid.Hollister@Sun.COM
768710696SDavid.Hollister@Sun.COM check_failed:
768810696SDavid.Hollister@Sun.COM
768910696SDavid.Hollister@Sun.COM return (1);
769010696SDavid.Hollister@Sun.COM }
769110696SDavid.Hollister@Sun.COM
769210696SDavid.Hollister@Sun.COM /*
769310696SDavid.Hollister@Sun.COM * pmcs_handle_dead_phys
769410696SDavid.Hollister@Sun.COM *
769510696SDavid.Hollister@Sun.COM * If the PHY has no outstanding work associated with it, remove it from
769610696SDavid.Hollister@Sun.COM * the dead PHY list and free it.
769710696SDavid.Hollister@Sun.COM *
769810696SDavid.Hollister@Sun.COM * If pwp->ds_err_recovering or pwp->configuring is set, don't run.
769910696SDavid.Hollister@Sun.COM * This keeps routines that need to submit work to the chip from having to
770010696SDavid.Hollister@Sun.COM * hold PHY locks to ensure that PHYs don't disappear while they do their work.
770110696SDavid.Hollister@Sun.COM */
770210696SDavid.Hollister@Sun.COM void
pmcs_handle_dead_phys(pmcs_hw_t * pwp)770310696SDavid.Hollister@Sun.COM pmcs_handle_dead_phys(pmcs_hw_t *pwp)
770410696SDavid.Hollister@Sun.COM {
770510696SDavid.Hollister@Sun.COM pmcs_phy_t *phyp, *nphyp, *pphyp;
770610696SDavid.Hollister@Sun.COM
770710696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
770810696SDavid.Hollister@Sun.COM mutex_enter(&pwp->config_lock);
770910696SDavid.Hollister@Sun.COM
771010696SDavid.Hollister@Sun.COM if (pwp->configuring | pwp->ds_err_recovering) {
771110696SDavid.Hollister@Sun.COM mutex_exit(&pwp->config_lock);
771210696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
771310696SDavid.Hollister@Sun.COM return;
771410696SDavid.Hollister@Sun.COM }
771510696SDavid.Hollister@Sun.COM
771610696SDavid.Hollister@Sun.COM /*
771710696SDavid.Hollister@Sun.COM * Check every PHY in the dead PHY list
771810696SDavid.Hollister@Sun.COM */
771910696SDavid.Hollister@Sun.COM mutex_enter(&pwp->dead_phylist_lock);
772010696SDavid.Hollister@Sun.COM phyp = pwp->dead_phys;
772110696SDavid.Hollister@Sun.COM pphyp = NULL; /* Set previous PHY to NULL */
772210696SDavid.Hollister@Sun.COM
772310696SDavid.Hollister@Sun.COM while (phyp != NULL) {
772410696SDavid.Hollister@Sun.COM pmcs_lock_phy(phyp);
772510696SDavid.Hollister@Sun.COM ASSERT(phyp->dead);
772610696SDavid.Hollister@Sun.COM
772710696SDavid.Hollister@Sun.COM nphyp = phyp->dead_next;
772810696SDavid.Hollister@Sun.COM
772910696SDavid.Hollister@Sun.COM /*
773010696SDavid.Hollister@Sun.COM * Check for outstanding work
773110696SDavid.Hollister@Sun.COM */
773210696SDavid.Hollister@Sun.COM if (phyp->ref_count > 0) {
773310696SDavid.Hollister@Sun.COM pmcs_unlock_phy(phyp);
773410696SDavid.Hollister@Sun.COM pphyp = phyp; /* This PHY becomes "previous" */
773510696SDavid.Hollister@Sun.COM } else if (phyp->target) {
773610696SDavid.Hollister@Sun.COM pmcs_unlock_phy(phyp);
773711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG1, phyp, phyp->target,
773810696SDavid.Hollister@Sun.COM "%s: Not freeing PHY 0x%p: target 0x%p is not free",
773910696SDavid.Hollister@Sun.COM __func__, (void *)phyp, (void *)phyp->target);
774010696SDavid.Hollister@Sun.COM pphyp = phyp;
774110696SDavid.Hollister@Sun.COM } else {
774210696SDavid.Hollister@Sun.COM /*
774310696SDavid.Hollister@Sun.COM * No outstanding work or target references. Remove it
774410696SDavid.Hollister@Sun.COM * from the list and free it
774510696SDavid.Hollister@Sun.COM */
774611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, phyp->target,
774710696SDavid.Hollister@Sun.COM "%s: Freeing inactive dead PHY 0x%p @ %s "
774810696SDavid.Hollister@Sun.COM "target = 0x%p", __func__, (void *)phyp,
774910696SDavid.Hollister@Sun.COM phyp->path, (void *)phyp->target);
775010696SDavid.Hollister@Sun.COM /*
775110696SDavid.Hollister@Sun.COM * If pphyp is NULL, then phyp was the head of the list,
775210696SDavid.Hollister@Sun.COM * so just reset the head to nphyp. Otherwise, the
775310696SDavid.Hollister@Sun.COM * previous PHY will now point to nphyp (the next PHY)
775410696SDavid.Hollister@Sun.COM */
775510696SDavid.Hollister@Sun.COM if (pphyp == NULL) {
775610696SDavid.Hollister@Sun.COM pwp->dead_phys = nphyp;
775710696SDavid.Hollister@Sun.COM } else {
775810696SDavid.Hollister@Sun.COM pphyp->dead_next = nphyp;
775910696SDavid.Hollister@Sun.COM }
776010696SDavid.Hollister@Sun.COM /*
776110696SDavid.Hollister@Sun.COM * If the target still points to this PHY, remove
776210696SDavid.Hollister@Sun.COM * that linkage now.
776310696SDavid.Hollister@Sun.COM */
776410696SDavid.Hollister@Sun.COM if (phyp->target) {
776510696SDavid.Hollister@Sun.COM mutex_enter(&phyp->target->statlock);
776610696SDavid.Hollister@Sun.COM if (phyp->target->phy == phyp) {
776710696SDavid.Hollister@Sun.COM phyp->target->phy = NULL;
776810696SDavid.Hollister@Sun.COM }
776910696SDavid.Hollister@Sun.COM mutex_exit(&phyp->target->statlock);
777010696SDavid.Hollister@Sun.COM }
777110755SJesse.Butler@Sun.COM pmcs_unlock_phy(phyp);
777212668Ssrikanth.suravajhala@oracle.com phyp->target_addr = NULL;
777310696SDavid.Hollister@Sun.COM kmem_cache_free(pwp->phy_cache, phyp);
777410696SDavid.Hollister@Sun.COM }
777510696SDavid.Hollister@Sun.COM
777610696SDavid.Hollister@Sun.COM phyp = nphyp;
777710696SDavid.Hollister@Sun.COM }
777810696SDavid.Hollister@Sun.COM
777910696SDavid.Hollister@Sun.COM mutex_exit(&pwp->dead_phylist_lock);
778010696SDavid.Hollister@Sun.COM mutex_exit(&pwp->config_lock);
778110696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
778210696SDavid.Hollister@Sun.COM }
778310696SDavid.Hollister@Sun.COM
778410696SDavid.Hollister@Sun.COM void
pmcs_inc_phy_ref_count(pmcs_phy_t * phyp)778510696SDavid.Hollister@Sun.COM pmcs_inc_phy_ref_count(pmcs_phy_t *phyp)
778610696SDavid.Hollister@Sun.COM {
778710696SDavid.Hollister@Sun.COM atomic_inc_32(&phyp->ref_count);
778810696SDavid.Hollister@Sun.COM }
778910696SDavid.Hollister@Sun.COM
779010696SDavid.Hollister@Sun.COM void
pmcs_dec_phy_ref_count(pmcs_phy_t * phyp)779110696SDavid.Hollister@Sun.COM pmcs_dec_phy_ref_count(pmcs_phy_t *phyp)
779210696SDavid.Hollister@Sun.COM {
779310696SDavid.Hollister@Sun.COM ASSERT(phyp->ref_count != 0);
779410696SDavid.Hollister@Sun.COM atomic_dec_32(&phyp->ref_count);
779510696SDavid.Hollister@Sun.COM }
779610696SDavid.Hollister@Sun.COM
779710696SDavid.Hollister@Sun.COM /*
779810696SDavid.Hollister@Sun.COM * pmcs_reap_dead_phy
779910696SDavid.Hollister@Sun.COM *
780010696SDavid.Hollister@Sun.COM * This function is called from pmcs_new_tport when we have a PHY
780110696SDavid.Hollister@Sun.COM * without a target pointer. It's possible in that case that this PHY
780210696SDavid.Hollister@Sun.COM * may have a "brother" on the dead_phys list. That is, it may be the same as
780310696SDavid.Hollister@Sun.COM * this one but with a different root PHY number (e.g. pp05 vs. pp04). If
780410696SDavid.Hollister@Sun.COM * that's the case, update the dead PHY and this new PHY. If that's not the
780510696SDavid.Hollister@Sun.COM * case, we should get a tran_tgt_init on this after it's reported to SCSA.
780610696SDavid.Hollister@Sun.COM *
780710696SDavid.Hollister@Sun.COM * Called with PHY locked.
780810696SDavid.Hollister@Sun.COM */
780910696SDavid.Hollister@Sun.COM static void
pmcs_reap_dead_phy(pmcs_phy_t * phyp)781010696SDavid.Hollister@Sun.COM pmcs_reap_dead_phy(pmcs_phy_t *phyp)
781110696SDavid.Hollister@Sun.COM {
781210696SDavid.Hollister@Sun.COM pmcs_hw_t *pwp = phyp->pwp;
781310696SDavid.Hollister@Sun.COM pmcs_phy_t *ctmp;
781411501SDavid.Hollister@Sun.COM pmcs_iport_t *iport_cmp;
781510696SDavid.Hollister@Sun.COM
781610696SDavid.Hollister@Sun.COM ASSERT(mutex_owned(&phyp->phy_lock));
781710696SDavid.Hollister@Sun.COM
781810696SDavid.Hollister@Sun.COM /*
781910696SDavid.Hollister@Sun.COM * Check the dead PHYs list
782010696SDavid.Hollister@Sun.COM */
782110696SDavid.Hollister@Sun.COM mutex_enter(&pwp->dead_phylist_lock);
782210696SDavid.Hollister@Sun.COM ctmp = pwp->dead_phys;
782310696SDavid.Hollister@Sun.COM while (ctmp) {
782411501SDavid.Hollister@Sun.COM /*
782511501SDavid.Hollister@Sun.COM * If the iport is NULL, compare against last_iport.
782611501SDavid.Hollister@Sun.COM */
782711501SDavid.Hollister@Sun.COM if (ctmp->iport) {
782811501SDavid.Hollister@Sun.COM iport_cmp = ctmp->iport;
782911501SDavid.Hollister@Sun.COM } else {
783011501SDavid.Hollister@Sun.COM iport_cmp = ctmp->last_iport;
783111501SDavid.Hollister@Sun.COM }
783211501SDavid.Hollister@Sun.COM
783311501SDavid.Hollister@Sun.COM if ((iport_cmp != phyp->iport) ||
783410696SDavid.Hollister@Sun.COM (memcmp((void *)&ctmp->sas_address[0],
783510696SDavid.Hollister@Sun.COM (void *)&phyp->sas_address[0], 8))) {
783610696SDavid.Hollister@Sun.COM ctmp = ctmp->dead_next;
783710696SDavid.Hollister@Sun.COM continue;
783810696SDavid.Hollister@Sun.COM }
783910696SDavid.Hollister@Sun.COM
784010696SDavid.Hollister@Sun.COM /*
784110696SDavid.Hollister@Sun.COM * Same SAS address on same iport. Now check to see if
784210696SDavid.Hollister@Sun.COM * the PHY path is the same with the possible exception
784310696SDavid.Hollister@Sun.COM * of the root PHY number.
784410696SDavid.Hollister@Sun.COM * The "5" is the string length of "pp00."
784510696SDavid.Hollister@Sun.COM */
784610696SDavid.Hollister@Sun.COM if ((strnlen(phyp->path, 5) >= 5) &&
784710696SDavid.Hollister@Sun.COM (strnlen(ctmp->path, 5) >= 5)) {
784810696SDavid.Hollister@Sun.COM if (memcmp((void *)&phyp->path[5],
784910696SDavid.Hollister@Sun.COM (void *)&ctmp->path[5],
785010696SDavid.Hollister@Sun.COM strnlen(phyp->path, 32) - 5) == 0) {
785110696SDavid.Hollister@Sun.COM break;
785210696SDavid.Hollister@Sun.COM }
785310696SDavid.Hollister@Sun.COM }
785410696SDavid.Hollister@Sun.COM
785510696SDavid.Hollister@Sun.COM ctmp = ctmp->dead_next;
785610696SDavid.Hollister@Sun.COM }
785710696SDavid.Hollister@Sun.COM mutex_exit(&pwp->dead_phylist_lock);
785810696SDavid.Hollister@Sun.COM
785910696SDavid.Hollister@Sun.COM /*
786010696SDavid.Hollister@Sun.COM * Found a match. Remove the target linkage and drop the
786110696SDavid.Hollister@Sun.COM * ref count on the old PHY. Then, increment the ref count
786210696SDavid.Hollister@Sun.COM * on the new PHY to compensate.
786310696SDavid.Hollister@Sun.COM */
786410696SDavid.Hollister@Sun.COM if (ctmp) {
786511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, ctmp, NULL,
786611501SDavid.Hollister@Sun.COM "%s: Found match in dead PHY list (0x%p) for new PHY %s",
786711501SDavid.Hollister@Sun.COM __func__, (void *)ctmp, phyp->path);
786811501SDavid.Hollister@Sun.COM /*
786911501SDavid.Hollister@Sun.COM * If there is a pointer to the target in the dead PHY, move
787011501SDavid.Hollister@Sun.COM * all reference counts to the new PHY.
787111501SDavid.Hollister@Sun.COM */
787210696SDavid.Hollister@Sun.COM if (ctmp->target) {
787311501SDavid.Hollister@Sun.COM mutex_enter(&ctmp->target->statlock);
787410696SDavid.Hollister@Sun.COM phyp->target = ctmp->target;
787511501SDavid.Hollister@Sun.COM
787611501SDavid.Hollister@Sun.COM while (ctmp->ref_count != 0) {
787711501SDavid.Hollister@Sun.COM pmcs_inc_phy_ref_count(phyp);
787811501SDavid.Hollister@Sun.COM pmcs_dec_phy_ref_count(ctmp);
787911501SDavid.Hollister@Sun.COM }
788010696SDavid.Hollister@Sun.COM /*
788110696SDavid.Hollister@Sun.COM * Update the target's linkage as well
788210696SDavid.Hollister@Sun.COM */
788310696SDavid.Hollister@Sun.COM phyp->target->phy = phyp;
788410696SDavid.Hollister@Sun.COM phyp->target->dtype = phyp->dtype;
788511501SDavid.Hollister@Sun.COM ctmp->target = NULL;
788610696SDavid.Hollister@Sun.COM mutex_exit(&phyp->target->statlock);
788710696SDavid.Hollister@Sun.COM }
788810696SDavid.Hollister@Sun.COM }
788910696SDavid.Hollister@Sun.COM }
789010696SDavid.Hollister@Sun.COM
789110696SDavid.Hollister@Sun.COM /*
789210696SDavid.Hollister@Sun.COM * Called with iport lock held
789310696SDavid.Hollister@Sun.COM */
789410696SDavid.Hollister@Sun.COM void
pmcs_add_phy_to_iport(pmcs_iport_t * iport,pmcs_phy_t * phyp)789510696SDavid.Hollister@Sun.COM pmcs_add_phy_to_iport(pmcs_iport_t *iport, pmcs_phy_t *phyp)
789610696SDavid.Hollister@Sun.COM {
789710696SDavid.Hollister@Sun.COM ASSERT(mutex_owned(&iport->lock));
789810696SDavid.Hollister@Sun.COM ASSERT(phyp);
789910696SDavid.Hollister@Sun.COM ASSERT(!list_link_active(&phyp->list_node));
790012258Ssrikanth.suravajhala@oracle.com
790110696SDavid.Hollister@Sun.COM iport->nphy++;
790211090SDavid.Hollister@Sun.COM list_insert_tail(&iport->phys, phyp);
790310696SDavid.Hollister@Sun.COM pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
790410696SDavid.Hollister@Sun.COM &iport->nphy);
790512077Ssrikanth.suravajhala@oracle.com mutex_enter(&phyp->phy_lock);
790612077Ssrikanth.suravajhala@oracle.com pmcs_create_one_phy_stats(iport, phyp);
790712077Ssrikanth.suravajhala@oracle.com mutex_exit(&phyp->phy_lock);
790812462Sjesse.butler@oracle.com pmcs_hold_iport(iport);
790910696SDavid.Hollister@Sun.COM }
791010696SDavid.Hollister@Sun.COM
791110696SDavid.Hollister@Sun.COM /*
791210696SDavid.Hollister@Sun.COM * Called with the iport lock held
791310696SDavid.Hollister@Sun.COM */
791410696SDavid.Hollister@Sun.COM void
pmcs_remove_phy_from_iport(pmcs_iport_t * iport,pmcs_phy_t * phyp)791510696SDavid.Hollister@Sun.COM pmcs_remove_phy_from_iport(pmcs_iport_t *iport, pmcs_phy_t *phyp)
791610696SDavid.Hollister@Sun.COM {
791710696SDavid.Hollister@Sun.COM pmcs_phy_t *pptr, *next_pptr;
791810696SDavid.Hollister@Sun.COM
791910696SDavid.Hollister@Sun.COM ASSERT(mutex_owned(&iport->lock));
792010696SDavid.Hollister@Sun.COM
792110696SDavid.Hollister@Sun.COM /*
792210696SDavid.Hollister@Sun.COM * If phyp is NULL, remove all PHYs from the iport
792310696SDavid.Hollister@Sun.COM */
792410696SDavid.Hollister@Sun.COM if (phyp == NULL) {
792510696SDavid.Hollister@Sun.COM for (pptr = list_head(&iport->phys); pptr != NULL;
792610696SDavid.Hollister@Sun.COM pptr = next_pptr) {
792710696SDavid.Hollister@Sun.COM next_pptr = list_next(&iport->phys, pptr);
792810696SDavid.Hollister@Sun.COM mutex_enter(&pptr->phy_lock);
792912077Ssrikanth.suravajhala@oracle.com if (pptr->phy_stats != NULL) {
793012077Ssrikanth.suravajhala@oracle.com kstat_delete(pptr->phy_stats);
793112077Ssrikanth.suravajhala@oracle.com pptr->phy_stats = NULL;
793212077Ssrikanth.suravajhala@oracle.com }
793310696SDavid.Hollister@Sun.COM pptr->iport = NULL;
793411692SJesse.Butler@Sun.COM pmcs_update_phy_pm_props(pptr, pptr->att_port_pm_tmp,
793511692SJesse.Butler@Sun.COM pptr->tgt_port_pm_tmp, B_FALSE);
793610696SDavid.Hollister@Sun.COM mutex_exit(&pptr->phy_lock);
793710696SDavid.Hollister@Sun.COM pmcs_rele_iport(iport);
793810696SDavid.Hollister@Sun.COM list_remove(&iport->phys, pptr);
793911601SDavid.Hollister@Sun.COM pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32,
794011601SDavid.Hollister@Sun.COM PMCS_NUM_PHYS, &iport->nphy);
794110696SDavid.Hollister@Sun.COM }
794210696SDavid.Hollister@Sun.COM iport->nphy = 0;
794310696SDavid.Hollister@Sun.COM return;
794410696SDavid.Hollister@Sun.COM }
794510696SDavid.Hollister@Sun.COM
794610696SDavid.Hollister@Sun.COM ASSERT(phyp);
794710696SDavid.Hollister@Sun.COM ASSERT(iport->nphy > 0);
794810696SDavid.Hollister@Sun.COM ASSERT(list_link_active(&phyp->list_node));
794910696SDavid.Hollister@Sun.COM iport->nphy--;
795011090SDavid.Hollister@Sun.COM list_remove(&iport->phys, phyp);
795111307SDavid.Hollister@Sun.COM pmcs_update_phy_pm_props(phyp, phyp->att_port_pm_tmp,
795211307SDavid.Hollister@Sun.COM phyp->tgt_port_pm_tmp, B_FALSE);
795310696SDavid.Hollister@Sun.COM pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
795410696SDavid.Hollister@Sun.COM &iport->nphy);
795510696SDavid.Hollister@Sun.COM pmcs_rele_iport(iport);
795610696SDavid.Hollister@Sun.COM }
795710696SDavid.Hollister@Sun.COM
795810696SDavid.Hollister@Sun.COM /*
795910696SDavid.Hollister@Sun.COM * This function checks to see if the target pointed to by phyp is still
796010696SDavid.Hollister@Sun.COM * correct. This is done by comparing the target's unit address with the
796110696SDavid.Hollister@Sun.COM * SAS address in phyp.
796210696SDavid.Hollister@Sun.COM *
796310696SDavid.Hollister@Sun.COM * Called with PHY locked and target statlock held
796410696SDavid.Hollister@Sun.COM */
796510696SDavid.Hollister@Sun.COM static boolean_t
pmcs_phy_target_match(pmcs_phy_t * phyp)796610696SDavid.Hollister@Sun.COM pmcs_phy_target_match(pmcs_phy_t *phyp)
796710696SDavid.Hollister@Sun.COM {
796810696SDavid.Hollister@Sun.COM uint64_t wwn;
796910696SDavid.Hollister@Sun.COM char unit_address[PMCS_MAX_UA_SIZE];
797010696SDavid.Hollister@Sun.COM boolean_t rval = B_FALSE;
797110696SDavid.Hollister@Sun.COM
797210696SDavid.Hollister@Sun.COM ASSERT(phyp);
797310696SDavid.Hollister@Sun.COM ASSERT(phyp->target);
797410696SDavid.Hollister@Sun.COM ASSERT(mutex_owned(&phyp->phy_lock));
797510696SDavid.Hollister@Sun.COM ASSERT(mutex_owned(&phyp->target->statlock));
797610696SDavid.Hollister@Sun.COM
797710696SDavid.Hollister@Sun.COM wwn = pmcs_barray2wwn(phyp->sas_address);
797810696SDavid.Hollister@Sun.COM (void) scsi_wwn_to_wwnstr(wwn, 1, unit_address);
797910696SDavid.Hollister@Sun.COM
798010696SDavid.Hollister@Sun.COM if (memcmp((void *)unit_address, (void *)phyp->target->unit_address,
798110696SDavid.Hollister@Sun.COM strnlen(phyp->target->unit_address, PMCS_MAX_UA_SIZE)) == 0) {
798210696SDavid.Hollister@Sun.COM rval = B_TRUE;
798310696SDavid.Hollister@Sun.COM }
798410696SDavid.Hollister@Sun.COM
798510696SDavid.Hollister@Sun.COM return (rval);
798610696SDavid.Hollister@Sun.COM }
798711267SJesse.Butler@Sun.COM /*
798811267SJesse.Butler@Sun.COM * Commands used to serialize SMP requests.
798911267SJesse.Butler@Sun.COM *
799011267SJesse.Butler@Sun.COM * The SPC only allows 2 SMP commands per SMP target: 1 cmd pending and 1 cmd
799111267SJesse.Butler@Sun.COM * queued for the same SMP target. If a third SMP cmd is sent to the SPC for an
799211267SJesse.Butler@Sun.COM * SMP target that already has a SMP cmd pending and one queued, then the
799311267SJesse.Butler@Sun.COM * SPC responds with the ERROR_INTERNAL_SMP_RESOURCE response.
799411267SJesse.Butler@Sun.COM *
799511267SJesse.Butler@Sun.COM * Additionally, the SPC has an 8 entry deep cmd queue and the number of SMP
799611267SJesse.Butler@Sun.COM * cmds that can be queued is controlled by the PORT_CONTROL IOMB. The
799711267SJesse.Butler@Sun.COM * SPC default is 1 SMP command/port (iport). These 2 queued SMP cmds would
799811267SJesse.Butler@Sun.COM * have to be for different SMP targets. The INTERNAL_SMP_RESOURCE error will
799911267SJesse.Butler@Sun.COM * also be returned if a 2nd SMP cmd is sent to the controller when there is
800011267SJesse.Butler@Sun.COM * already 1 SMP cmd queued for that port or if a 3rd SMP cmd is sent to the
800111267SJesse.Butler@Sun.COM * queue if there are already 2 queued SMP cmds.
800211267SJesse.Butler@Sun.COM */
800311267SJesse.Butler@Sun.COM void
pmcs_smp_acquire(pmcs_iport_t * iport)800411267SJesse.Butler@Sun.COM pmcs_smp_acquire(pmcs_iport_t *iport)
800511267SJesse.Butler@Sun.COM {
800611267SJesse.Butler@Sun.COM if (iport == NULL) {
800711267SJesse.Butler@Sun.COM return;
800811267SJesse.Butler@Sun.COM }
800911267SJesse.Butler@Sun.COM
801011267SJesse.Butler@Sun.COM mutex_enter(&iport->smp_lock);
801111267SJesse.Butler@Sun.COM while (iport->smp_active) {
801211267SJesse.Butler@Sun.COM pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL,
801311267SJesse.Butler@Sun.COM "%s: SMP is active on thread 0x%p, waiting", __func__,
801411267SJesse.Butler@Sun.COM (void *)iport->smp_active_thread);
801511267SJesse.Butler@Sun.COM cv_wait(&iport->smp_cv, &iport->smp_lock);
801611267SJesse.Butler@Sun.COM }
801711267SJesse.Butler@Sun.COM iport->smp_active = B_TRUE;
801811267SJesse.Butler@Sun.COM iport->smp_active_thread = curthread;
801911601SDavid.Hollister@Sun.COM pmcs_prt(iport->pwp, PMCS_PRT_DEBUG3, NULL, NULL,
802011267SJesse.Butler@Sun.COM "%s: SMP acquired by thread 0x%p", __func__,
802111267SJesse.Butler@Sun.COM (void *)iport->smp_active_thread);
802211267SJesse.Butler@Sun.COM mutex_exit(&iport->smp_lock);
802311267SJesse.Butler@Sun.COM }
802411267SJesse.Butler@Sun.COM
802511267SJesse.Butler@Sun.COM void
pmcs_smp_release(pmcs_iport_t * iport)802611267SJesse.Butler@Sun.COM pmcs_smp_release(pmcs_iport_t *iport)
802711267SJesse.Butler@Sun.COM {
802811267SJesse.Butler@Sun.COM if (iport == NULL) {
802911267SJesse.Butler@Sun.COM return;
803011267SJesse.Butler@Sun.COM }
803111267SJesse.Butler@Sun.COM
803211267SJesse.Butler@Sun.COM mutex_enter(&iport->smp_lock);
803311601SDavid.Hollister@Sun.COM pmcs_prt(iport->pwp, PMCS_PRT_DEBUG3, NULL, NULL,
803411267SJesse.Butler@Sun.COM "%s: SMP released by thread 0x%p", __func__, (void *)curthread);
803511267SJesse.Butler@Sun.COM iport->smp_active = B_FALSE;
803611267SJesse.Butler@Sun.COM iport->smp_active_thread = NULL;
803711267SJesse.Butler@Sun.COM cv_signal(&iport->smp_cv);
803811267SJesse.Butler@Sun.COM mutex_exit(&iport->smp_lock);
803911267SJesse.Butler@Sun.COM }
804011307SDavid.Hollister@Sun.COM
804111307SDavid.Hollister@Sun.COM /*
804211307SDavid.Hollister@Sun.COM * Update a PHY's attached-port-pm and target-port-pm properties
804311307SDavid.Hollister@Sun.COM *
804411307SDavid.Hollister@Sun.COM * phyp: PHY whose properties are to be updated
804511307SDavid.Hollister@Sun.COM *
804611307SDavid.Hollister@Sun.COM * att_bv: Bit value of the attached-port-pm property to be updated in the
804711307SDavid.Hollister@Sun.COM * 64-bit holding area for the PHY.
804811307SDavid.Hollister@Sun.COM *
804911307SDavid.Hollister@Sun.COM * tgt_bv: Bit value of the target-port-pm property to update in the 64-bit
805011307SDavid.Hollister@Sun.COM * holding area for the PHY.
805111307SDavid.Hollister@Sun.COM *
805211307SDavid.Hollister@Sun.COM * prop_add_val: If TRUE, we're adding bits into the property value.
805311307SDavid.Hollister@Sun.COM * Otherwise, we're taking them out. Either way, the properties for this
805411307SDavid.Hollister@Sun.COM * PHY will be updated.
805511307SDavid.Hollister@Sun.COM */
805611307SDavid.Hollister@Sun.COM void
pmcs_update_phy_pm_props(pmcs_phy_t * phyp,uint64_t att_bv,uint64_t tgt_bv,boolean_t prop_add_val)805711307SDavid.Hollister@Sun.COM pmcs_update_phy_pm_props(pmcs_phy_t *phyp, uint64_t att_bv, uint64_t tgt_bv,
805811307SDavid.Hollister@Sun.COM boolean_t prop_add_val)
805911307SDavid.Hollister@Sun.COM {
806012668Ssrikanth.suravajhala@oracle.com pmcs_xscsi_t *tgt;
806112668Ssrikanth.suravajhala@oracle.com
806211307SDavid.Hollister@Sun.COM if (prop_add_val) {
806311307SDavid.Hollister@Sun.COM /*
806411307SDavid.Hollister@Sun.COM * If the values are currently 0, then we're setting the
806511307SDavid.Hollister@Sun.COM * phymask for just this PHY as well.
806611307SDavid.Hollister@Sun.COM */
806711307SDavid.Hollister@Sun.COM if (phyp->att_port_pm_tmp == 0) {
806811442SDavid.Hollister@Sun.COM phyp->att_port_pm = att_bv;
806911442SDavid.Hollister@Sun.COM phyp->tgt_port_pm = tgt_bv;
807011442SDavid.Hollister@Sun.COM }
807111442SDavid.Hollister@Sun.COM phyp->att_port_pm_tmp |= att_bv;
807211442SDavid.Hollister@Sun.COM phyp->tgt_port_pm_tmp |= tgt_bv;
807311307SDavid.Hollister@Sun.COM (void) snprintf(phyp->att_port_pm_str, PMCS_PM_MAX_NAMELEN,
807411307SDavid.Hollister@Sun.COM "%"PRIx64, phyp->att_port_pm_tmp);
807511307SDavid.Hollister@Sun.COM (void) snprintf(phyp->tgt_port_pm_str, PMCS_PM_MAX_NAMELEN,
807611307SDavid.Hollister@Sun.COM "%"PRIx64, phyp->tgt_port_pm_tmp);
807711307SDavid.Hollister@Sun.COM } else {
807811442SDavid.Hollister@Sun.COM phyp->att_port_pm_tmp &= ~att_bv;
807911442SDavid.Hollister@Sun.COM phyp->tgt_port_pm_tmp &= ~tgt_bv;
808011307SDavid.Hollister@Sun.COM if (phyp->att_port_pm_tmp) {
808111307SDavid.Hollister@Sun.COM (void) snprintf(phyp->att_port_pm_str,
808211307SDavid.Hollister@Sun.COM PMCS_PM_MAX_NAMELEN, "%"PRIx64,
808311307SDavid.Hollister@Sun.COM phyp->att_port_pm_tmp);
808411307SDavid.Hollister@Sun.COM } else {
808511307SDavid.Hollister@Sun.COM phyp->att_port_pm_str[0] = '\0';
808611307SDavid.Hollister@Sun.COM phyp->att_port_pm = 0;
808711307SDavid.Hollister@Sun.COM }
808811307SDavid.Hollister@Sun.COM if (phyp->tgt_port_pm_tmp) {
808911307SDavid.Hollister@Sun.COM (void) snprintf(phyp->tgt_port_pm_str,
809011307SDavid.Hollister@Sun.COM PMCS_PM_MAX_NAMELEN, "%"PRIx64,
809111307SDavid.Hollister@Sun.COM phyp->tgt_port_pm_tmp);
809211307SDavid.Hollister@Sun.COM } else {
809311307SDavid.Hollister@Sun.COM phyp->tgt_port_pm_str[0] = '\0';
809411307SDavid.Hollister@Sun.COM phyp->tgt_port_pm = 0;
809511307SDavid.Hollister@Sun.COM }
809611307SDavid.Hollister@Sun.COM }
809711307SDavid.Hollister@Sun.COM
809812668Ssrikanth.suravajhala@oracle.com if ((phyp->target_addr) && (*phyp->target_addr != NULL)) {
809912668Ssrikanth.suravajhala@oracle.com tgt = *phyp->target_addr;
810012668Ssrikanth.suravajhala@oracle.com } else if (phyp->target != NULL) {
810112668Ssrikanth.suravajhala@oracle.com tgt = phyp->target;
810212668Ssrikanth.suravajhala@oracle.com } else {
810311307SDavid.Hollister@Sun.COM return;
810411307SDavid.Hollister@Sun.COM }
810511307SDavid.Hollister@Sun.COM
810612668Ssrikanth.suravajhala@oracle.com mutex_enter(&tgt->statlock);
810712668Ssrikanth.suravajhala@oracle.com if (!list_is_empty(&tgt->lun_list)) {
810811501SDavid.Hollister@Sun.COM pmcs_lun_t *lunp;
810911501SDavid.Hollister@Sun.COM
811012668Ssrikanth.suravajhala@oracle.com lunp = list_head(&tgt->lun_list);
811111501SDavid.Hollister@Sun.COM while (lunp) {
811211501SDavid.Hollister@Sun.COM (void) scsi_device_prop_update_string(lunp->sd,
811311501SDavid.Hollister@Sun.COM SCSI_DEVICE_PROP_PATH,
811411501SDavid.Hollister@Sun.COM SCSI_ADDR_PROP_ATTACHED_PORT_PM,
811511501SDavid.Hollister@Sun.COM phyp->att_port_pm_str);
811611501SDavid.Hollister@Sun.COM (void) scsi_device_prop_update_string(lunp->sd,
811711501SDavid.Hollister@Sun.COM SCSI_DEVICE_PROP_PATH,
811811501SDavid.Hollister@Sun.COM SCSI_ADDR_PROP_TARGET_PORT_PM,
811911501SDavid.Hollister@Sun.COM phyp->tgt_port_pm_str);
812012668Ssrikanth.suravajhala@oracle.com lunp = list_next(&tgt->lun_list, lunp);
812112668Ssrikanth.suravajhala@oracle.com }
812212668Ssrikanth.suravajhala@oracle.com } else if (tgt->smpd) {
812312668Ssrikanth.suravajhala@oracle.com (void) smp_device_prop_update_string(tgt->smpd,
812411307SDavid.Hollister@Sun.COM SCSI_ADDR_PROP_ATTACHED_PORT_PM,
812511307SDavid.Hollister@Sun.COM phyp->att_port_pm_str);
812612668Ssrikanth.suravajhala@oracle.com (void) smp_device_prop_update_string(tgt->smpd,
812711307SDavid.Hollister@Sun.COM SCSI_ADDR_PROP_TARGET_PORT_PM,
812811307SDavid.Hollister@Sun.COM phyp->tgt_port_pm_str);
812911307SDavid.Hollister@Sun.COM }
813012668Ssrikanth.suravajhala@oracle.com mutex_exit(&tgt->statlock);
813111307SDavid.Hollister@Sun.COM }
813211347SRamana.Srikanth@Sun.COM
813311347SRamana.Srikanth@Sun.COM /* ARGSUSED */
813411347SRamana.Srikanth@Sun.COM void
pmcs_deregister_device_work(pmcs_hw_t * pwp,pmcs_phy_t * phyp)813511347SRamana.Srikanth@Sun.COM pmcs_deregister_device_work(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
813611347SRamana.Srikanth@Sun.COM {
813711347SRamana.Srikanth@Sun.COM pmcs_phy_t *pptr;
813811347SRamana.Srikanth@Sun.COM
813911347SRamana.Srikanth@Sun.COM for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
814011347SRamana.Srikanth@Sun.COM pmcs_lock_phy(pptr);
814111347SRamana.Srikanth@Sun.COM if (pptr->deregister_wait) {
814211347SRamana.Srikanth@Sun.COM pmcs_deregister_device(pwp, pptr);
814311347SRamana.Srikanth@Sun.COM }
814411347SRamana.Srikanth@Sun.COM pmcs_unlock_phy(pptr);
814511347SRamana.Srikanth@Sun.COM }
814611347SRamana.Srikanth@Sun.COM }
814711601SDavid.Hollister@Sun.COM
814811601SDavid.Hollister@Sun.COM /*
814911601SDavid.Hollister@Sun.COM * pmcs_iport_active
815011601SDavid.Hollister@Sun.COM *
815111601SDavid.Hollister@Sun.COM * Mark this iport as active. Called with the iport lock held.
815211601SDavid.Hollister@Sun.COM */
815311601SDavid.Hollister@Sun.COM static void
pmcs_iport_active(pmcs_iport_t * iport)815411601SDavid.Hollister@Sun.COM pmcs_iport_active(pmcs_iport_t *iport)
815511601SDavid.Hollister@Sun.COM {
815611601SDavid.Hollister@Sun.COM ASSERT(mutex_owned(&iport->lock));
815711601SDavid.Hollister@Sun.COM
815811601SDavid.Hollister@Sun.COM iport->ua_state = UA_ACTIVE;
815911601SDavid.Hollister@Sun.COM iport->smp_active = B_FALSE;
816011601SDavid.Hollister@Sun.COM iport->smp_active_thread = NULL;
816111601SDavid.Hollister@Sun.COM }
816211601SDavid.Hollister@Sun.COM
816311601SDavid.Hollister@Sun.COM /* ARGSUSED */
816411601SDavid.Hollister@Sun.COM static void
pmcs_tgtmap_activate_cb(void * tgtmap_priv,char * tgt_addr,scsi_tgtmap_tgt_type_t tgt_type,void ** tgt_privp)816511601SDavid.Hollister@Sun.COM pmcs_tgtmap_activate_cb(void *tgtmap_priv, char *tgt_addr,
816611601SDavid.Hollister@Sun.COM scsi_tgtmap_tgt_type_t tgt_type, void **tgt_privp)
816711601SDavid.Hollister@Sun.COM {
816811601SDavid.Hollister@Sun.COM pmcs_iport_t *iport = (pmcs_iport_t *)tgtmap_priv;
816911692SJesse.Butler@Sun.COM pmcs_hw_t *pwp = iport->pwp;
817011692SJesse.Butler@Sun.COM pmcs_xscsi_t *target;
817111692SJesse.Butler@Sun.COM
817211692SJesse.Butler@Sun.COM /*
817311692SJesse.Butler@Sun.COM * Look up the target. If there is one, and it doesn't have a PHY
817411692SJesse.Butler@Sun.COM * pointer, re-establish that linkage here.
817511692SJesse.Butler@Sun.COM */
817611692SJesse.Butler@Sun.COM mutex_enter(&pwp->lock);
817711692SJesse.Butler@Sun.COM target = pmcs_get_target(iport, tgt_addr, B_FALSE);
817811692SJesse.Butler@Sun.COM mutex_exit(&pwp->lock);
817911692SJesse.Butler@Sun.COM
818011692SJesse.Butler@Sun.COM /*
818111692SJesse.Butler@Sun.COM * If we got a target, it will now have a PHY pointer and the PHY
818211692SJesse.Butler@Sun.COM * will point to the target. The PHY will be locked, so we'll need
818311692SJesse.Butler@Sun.COM * to unlock it.
818411692SJesse.Butler@Sun.COM */
818512462Sjesse.butler@oracle.com if (target != NULL) {
818611692SJesse.Butler@Sun.COM pmcs_unlock_phy(target->phy);
818711692SJesse.Butler@Sun.COM }
818811601SDavid.Hollister@Sun.COM
818911601SDavid.Hollister@Sun.COM /*
819011601SDavid.Hollister@Sun.COM * Update config_restart_time so we don't try to restart discovery
819111601SDavid.Hollister@Sun.COM * while enumeration is still in progress.
819211601SDavid.Hollister@Sun.COM */
819311692SJesse.Butler@Sun.COM mutex_enter(&pwp->config_lock);
819411692SJesse.Butler@Sun.COM pwp->config_restart_time = ddi_get_lbolt() +
819511601SDavid.Hollister@Sun.COM drv_usectohz(PMCS_REDISCOVERY_DELAY);
819611692SJesse.Butler@Sun.COM mutex_exit(&pwp->config_lock);
819711601SDavid.Hollister@Sun.COM }
819811601SDavid.Hollister@Sun.COM
819911601SDavid.Hollister@Sun.COM /* ARGSUSED */
820011601SDavid.Hollister@Sun.COM static boolean_t
pmcs_tgtmap_deactivate_cb(void * tgtmap_priv,char * tgt_addr,scsi_tgtmap_tgt_type_t tgt_type,void * tgt_priv,scsi_tgtmap_deact_rsn_t tgt_deact_rsn)820111601SDavid.Hollister@Sun.COM pmcs_tgtmap_deactivate_cb(void *tgtmap_priv, char *tgt_addr,
820211601SDavid.Hollister@Sun.COM scsi_tgtmap_tgt_type_t tgt_type, void *tgt_priv,
820311601SDavid.Hollister@Sun.COM scsi_tgtmap_deact_rsn_t tgt_deact_rsn)
820411601SDavid.Hollister@Sun.COM {
820511601SDavid.Hollister@Sun.COM pmcs_iport_t *iport = (pmcs_iport_t *)tgtmap_priv;
820611601SDavid.Hollister@Sun.COM pmcs_phy_t *phyp;
820711601SDavid.Hollister@Sun.COM boolean_t rediscover = B_FALSE;
820811601SDavid.Hollister@Sun.COM
820911601SDavid.Hollister@Sun.COM ASSERT(iport);
821011601SDavid.Hollister@Sun.COM
821111601SDavid.Hollister@Sun.COM phyp = pmcs_find_phy_by_sas_address(iport->pwp, iport, NULL, tgt_addr);
821211601SDavid.Hollister@Sun.COM if (phyp == NULL) {
821311601SDavid.Hollister@Sun.COM pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL,
821411601SDavid.Hollister@Sun.COM "%s: Couldn't find PHY at %s", __func__, tgt_addr);
821511601SDavid.Hollister@Sun.COM return (rediscover);
821611601SDavid.Hollister@Sun.COM }
821711601SDavid.Hollister@Sun.COM /* phyp is locked */
821811601SDavid.Hollister@Sun.COM
821911601SDavid.Hollister@Sun.COM if (!phyp->reenumerate && phyp->configured) {
822011601SDavid.Hollister@Sun.COM pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, phyp, phyp->target,
822111601SDavid.Hollister@Sun.COM "%s: PHY @ %s is configured... re-enumerate", __func__,
822211601SDavid.Hollister@Sun.COM tgt_addr);
822311601SDavid.Hollister@Sun.COM phyp->reenumerate = 1;
822411601SDavid.Hollister@Sun.COM }
822511601SDavid.Hollister@Sun.COM
822611601SDavid.Hollister@Sun.COM /*
822711601SDavid.Hollister@Sun.COM * Check to see if reenumerate is set, and if so, if we've reached our
822811601SDavid.Hollister@Sun.COM * maximum number of retries.
822911601SDavid.Hollister@Sun.COM */
823011601SDavid.Hollister@Sun.COM if (phyp->reenumerate) {
823111601SDavid.Hollister@Sun.COM if (phyp->enum_attempts == PMCS_MAX_REENUMERATE) {
823211601SDavid.Hollister@Sun.COM pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, phyp,
823311601SDavid.Hollister@Sun.COM phyp->target,
823411601SDavid.Hollister@Sun.COM "%s: No more enumeration attempts for %s", __func__,
823511601SDavid.Hollister@Sun.COM tgt_addr);
823611601SDavid.Hollister@Sun.COM } else {
823711601SDavid.Hollister@Sun.COM pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, phyp,
823811601SDavid.Hollister@Sun.COM phyp->target, "%s: Re-attempt enumeration for %s",
823911601SDavid.Hollister@Sun.COM __func__, tgt_addr);
824011601SDavid.Hollister@Sun.COM ++phyp->enum_attempts;
824111601SDavid.Hollister@Sun.COM rediscover = B_TRUE;
824211601SDavid.Hollister@Sun.COM }
824311601SDavid.Hollister@Sun.COM
824411601SDavid.Hollister@Sun.COM phyp->reenumerate = 0;
824511601SDavid.Hollister@Sun.COM }
824611601SDavid.Hollister@Sun.COM
824711601SDavid.Hollister@Sun.COM pmcs_unlock_phy(phyp);
824811601SDavid.Hollister@Sun.COM
824911601SDavid.Hollister@Sun.COM mutex_enter(&iport->pwp->config_lock);
825011601SDavid.Hollister@Sun.COM iport->pwp->config_restart_time = ddi_get_lbolt() +
825111601SDavid.Hollister@Sun.COM drv_usectohz(PMCS_REDISCOVERY_DELAY);
825211601SDavid.Hollister@Sun.COM if (rediscover) {
825311601SDavid.Hollister@Sun.COM iport->pwp->config_restart = B_TRUE;
825411601SDavid.Hollister@Sun.COM } else if (iport->pwp->config_restart == B_TRUE) {
825511601SDavid.Hollister@Sun.COM /*
825611601SDavid.Hollister@Sun.COM * If we aren't asking for rediscovery because of this PHY,
825711601SDavid.Hollister@Sun.COM * check to see if we're already asking for it on behalf of
825811601SDavid.Hollister@Sun.COM * some other PHY. If so, we'll want to return TRUE, so reset
825911601SDavid.Hollister@Sun.COM * "rediscover" here.
826011601SDavid.Hollister@Sun.COM */
826111601SDavid.Hollister@Sun.COM rediscover = B_TRUE;
826211601SDavid.Hollister@Sun.COM }
826311601SDavid.Hollister@Sun.COM
826411601SDavid.Hollister@Sun.COM mutex_exit(&iport->pwp->config_lock);
826511601SDavid.Hollister@Sun.COM
826611601SDavid.Hollister@Sun.COM return (rediscover);
826711601SDavid.Hollister@Sun.COM }
826811601SDavid.Hollister@Sun.COM
826911601SDavid.Hollister@Sun.COM void
pmcs_status_disposition(pmcs_phy_t * phyp,uint32_t status)827011601SDavid.Hollister@Sun.COM pmcs_status_disposition(pmcs_phy_t *phyp, uint32_t status)
827111601SDavid.Hollister@Sun.COM {
827211601SDavid.Hollister@Sun.COM ASSERT(phyp);
827311601SDavid.Hollister@Sun.COM ASSERT(!mutex_owned(&phyp->phy_lock));
827411601SDavid.Hollister@Sun.COM
827511601SDavid.Hollister@Sun.COM if (phyp == NULL) {
827611601SDavid.Hollister@Sun.COM return;
827711601SDavid.Hollister@Sun.COM }
827811601SDavid.Hollister@Sun.COM
827911601SDavid.Hollister@Sun.COM pmcs_lock_phy(phyp);
828011601SDavid.Hollister@Sun.COM
828111601SDavid.Hollister@Sun.COM /*
828211601SDavid.Hollister@Sun.COM * XXX: Do we need to call this function from an SSP_EVENT?
828311601SDavid.Hollister@Sun.COM */
828411601SDavid.Hollister@Sun.COM
828511601SDavid.Hollister@Sun.COM switch (status) {
828611601SDavid.Hollister@Sun.COM case PMCOUT_STATUS_NO_DEVICE:
828711601SDavid.Hollister@Sun.COM case PMCOUT_STATUS_ERROR_HW_TIMEOUT:
828811601SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERR_BREAK:
828911601SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
829011601SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED:
829111601SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION:
829211601SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK:
829311601SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION:
829411601SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
829511601SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
829611601SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION:
829711601SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_ERROR:
829811601SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED:
829911601SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_RX_FRAME:
830011601SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
830111601SDavid.Hollister@Sun.COM case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE:
830211601SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_PORT_IN_RESET:
830311601SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL:
830411601SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_DS_IN_RECOVERY:
830511601SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
830611601SDavid.Hollister@Sun.COM pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG, phyp, phyp->target,
830711601SDavid.Hollister@Sun.COM "%s: status = 0x%x for " SAS_ADDR_FMT ", reenumerate",
830811601SDavid.Hollister@Sun.COM __func__, status, SAS_ADDR_PRT(phyp->sas_address));
830911601SDavid.Hollister@Sun.COM phyp->reenumerate = 1;
831011601SDavid.Hollister@Sun.COM break;
831111601SDavid.Hollister@Sun.COM
831211601SDavid.Hollister@Sun.COM default:
831311601SDavid.Hollister@Sun.COM pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG, phyp, phyp->target,
831411601SDavid.Hollister@Sun.COM "%s: status = 0x%x for " SAS_ADDR_FMT ", no reenumeration",
831511601SDavid.Hollister@Sun.COM __func__, status, SAS_ADDR_PRT(phyp->sas_address));
831611601SDavid.Hollister@Sun.COM break;
831711601SDavid.Hollister@Sun.COM }
831811601SDavid.Hollister@Sun.COM
831911601SDavid.Hollister@Sun.COM pmcs_unlock_phy(phyp);
832011601SDavid.Hollister@Sun.COM }
832111601SDavid.Hollister@Sun.COM
832211601SDavid.Hollister@Sun.COM /*
832311601SDavid.Hollister@Sun.COM * Add the list of PHYs pointed to by phyp to the dead_phys_list
832411601SDavid.Hollister@Sun.COM *
832511601SDavid.Hollister@Sun.COM * Called with all PHYs in the list locked
832611601SDavid.Hollister@Sun.COM */
832711601SDavid.Hollister@Sun.COM static void
pmcs_add_dead_phys(pmcs_hw_t * pwp,pmcs_phy_t * phyp)832811601SDavid.Hollister@Sun.COM pmcs_add_dead_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
832911601SDavid.Hollister@Sun.COM {
833011601SDavid.Hollister@Sun.COM mutex_enter(&pwp->dead_phylist_lock);
833111601SDavid.Hollister@Sun.COM while (phyp) {
833211601SDavid.Hollister@Sun.COM pmcs_phy_t *nxt = phyp->sibling;
833311601SDavid.Hollister@Sun.COM ASSERT(phyp->dead);
833411601SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, NULL,
833511601SDavid.Hollister@Sun.COM "%s: dead PHY 0x%p (%s) (ref_count %d)", __func__,
833611601SDavid.Hollister@Sun.COM (void *)phyp, phyp->path, phyp->ref_count);
833711601SDavid.Hollister@Sun.COM /*
833811601SDavid.Hollister@Sun.COM * Put this PHY on the dead PHY list for the watchdog to
833911601SDavid.Hollister@Sun.COM * clean up after any outstanding work has completed.
834011601SDavid.Hollister@Sun.COM */
834111601SDavid.Hollister@Sun.COM phyp->dead_next = pwp->dead_phys;
834211601SDavid.Hollister@Sun.COM pwp->dead_phys = phyp;
834311601SDavid.Hollister@Sun.COM pmcs_unlock_phy(phyp);
834411601SDavid.Hollister@Sun.COM phyp = nxt;
834511601SDavid.Hollister@Sun.COM }
834611601SDavid.Hollister@Sun.COM mutex_exit(&pwp->dead_phylist_lock);
834711601SDavid.Hollister@Sun.COM }
834811980SDavid.Hollister@Sun.COM
834911980SDavid.Hollister@Sun.COM static void
pmcs_get_fw_version(pmcs_hw_t * pwp)835011980SDavid.Hollister@Sun.COM pmcs_get_fw_version(pmcs_hw_t *pwp)
835111980SDavid.Hollister@Sun.COM {
835211980SDavid.Hollister@Sun.COM uint32_t ila_len, ver_hi, ver_lo;
835311980SDavid.Hollister@Sun.COM uint8_t ila_ver_string[9], img_flag;
835411980SDavid.Hollister@Sun.COM char uc, *ucp = &uc;
835511980SDavid.Hollister@Sun.COM unsigned long ila_ver;
835611980SDavid.Hollister@Sun.COM uint64_t ver_hilo;
835711980SDavid.Hollister@Sun.COM
835811980SDavid.Hollister@Sun.COM /* Firmware version is easy. */
835911980SDavid.Hollister@Sun.COM pwp->fw = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_FW);
836011980SDavid.Hollister@Sun.COM
836111980SDavid.Hollister@Sun.COM /*
836211980SDavid.Hollister@Sun.COM * Get the image size (2nd to last dword)
836311980SDavid.Hollister@Sun.COM * NOTE: The GSM registers are mapped little-endian, but the data
836411980SDavid.Hollister@Sun.COM * on the flash is actually big-endian, so we need to swap these values
836511980SDavid.Hollister@Sun.COM * regardless of which platform we're on.
836611980SDavid.Hollister@Sun.COM */
836711980SDavid.Hollister@Sun.COM ila_len = BSWAP_32(pmcs_rd_gsm_reg(pwp, GSM_FLASH_BASE_UPPER,
836811980SDavid.Hollister@Sun.COM GSM_FLASH_BASE + GSM_SM_BLKSZ - (2 << 2)));
836911980SDavid.Hollister@Sun.COM if (ila_len > 65535) {
837011980SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
837111980SDavid.Hollister@Sun.COM "%s: Invalid ILA image size (0x%x)?", __func__, ila_len);
837211980SDavid.Hollister@Sun.COM return;
837311980SDavid.Hollister@Sun.COM }
837411980SDavid.Hollister@Sun.COM
837511980SDavid.Hollister@Sun.COM /*
837611980SDavid.Hollister@Sun.COM * The numeric version is at ila_len - PMCS_ILA_VER_OFFSET
837711980SDavid.Hollister@Sun.COM */
837811980SDavid.Hollister@Sun.COM ver_hi = BSWAP_32(pmcs_rd_gsm_reg(pwp, GSM_FLASH_BASE_UPPER,
837911980SDavid.Hollister@Sun.COM GSM_FLASH_BASE + ila_len - PMCS_ILA_VER_OFFSET));
838011980SDavid.Hollister@Sun.COM ver_lo = BSWAP_32(pmcs_rd_gsm_reg(pwp, GSM_FLASH_BASE_UPPER,
838111980SDavid.Hollister@Sun.COM GSM_FLASH_BASE + ila_len - PMCS_ILA_VER_OFFSET + 4));
838211980SDavid.Hollister@Sun.COM ver_hilo = BE_64(((uint64_t)ver_hi << 32) | ver_lo);
838311980SDavid.Hollister@Sun.COM bcopy((const void *)&ver_hilo, &ila_ver_string[0], 8);
838411980SDavid.Hollister@Sun.COM ila_ver_string[8] = '\0';
838511980SDavid.Hollister@Sun.COM
838611980SDavid.Hollister@Sun.COM (void) ddi_strtoul((const char *)ila_ver_string, &ucp, 16, &ila_ver);
838711980SDavid.Hollister@Sun.COM pwp->ila_ver = (int)(ila_ver & 0xffffffff);
838811980SDavid.Hollister@Sun.COM
838911980SDavid.Hollister@Sun.COM img_flag = (BSWAP_32(pmcs_rd_gsm_reg(pwp, GSM_FLASH_BASE_UPPER,
839011980SDavid.Hollister@Sun.COM GSM_FLASH_IMG_FLAGS)) & 0xff000000) >> 24;
839111980SDavid.Hollister@Sun.COM if (img_flag & PMCS_IMG_FLAG_A) {
839211980SDavid.Hollister@Sun.COM pwp->fw_active_img = 1;
839311980SDavid.Hollister@Sun.COM } else {
839411980SDavid.Hollister@Sun.COM pwp->fw_active_img = 0;
839511980SDavid.Hollister@Sun.COM }
839611980SDavid.Hollister@Sun.COM }
8397