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
2012078SJesse.Butler@Sun.COM */
2112078SJesse.Butler@Sun.COM /*
2212078SJesse.Butler@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 functions that are called via interrupts.
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 #ifdef DEBUG
3210696SDavid.Hollister@Sun.COM #define VALID_IOMB_CHECK(p, w, m, b, c) \
3310696SDavid.Hollister@Sun.COM if (!(w & PMCS_IOMB_VALID)) { \
3410696SDavid.Hollister@Sun.COM char l[64]; \
3510696SDavid.Hollister@Sun.COM (void) snprintf(l, sizeof (l), \
3610696SDavid.Hollister@Sun.COM "%s: INVALID IOMB (oq_ci=%u oq_pi=%u)", __func__, b, c); \
3710696SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG, l, m); \
3810696SDavid.Hollister@Sun.COM STEP_OQ_ENTRY(pwp, PMCS_OQ_EVENTS, b, 1); \
3910696SDavid.Hollister@Sun.COM continue; \
4010696SDavid.Hollister@Sun.COM }
4110696SDavid.Hollister@Sun.COM #define WRONG_OBID_CHECK(pwp, w, q) \
4210696SDavid.Hollister@Sun.COM if (((w & PMCS_IOMB_OBID_MASK) >> PMCS_IOMB_OBID_SHIFT) != q) { \
4311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, \
4410696SDavid.Hollister@Sun.COM "%s: COMPLETION WITH WRONG OBID (0x%x)", __func__, \
4510696SDavid.Hollister@Sun.COM (w & PMCS_IOMB_OBID_MASK) >> PMCS_IOMB_OBID_SHIFT); \
4610696SDavid.Hollister@Sun.COM }
4710696SDavid.Hollister@Sun.COM #else
4810696SDavid.Hollister@Sun.COM #define VALID_IOMB_CHECK(a, b, c, d, e)
4910696SDavid.Hollister@Sun.COM #define WRONG_OBID_CHECK(a, b, c)
5010696SDavid.Hollister@Sun.COM #endif
5110696SDavid.Hollister@Sun.COM
5210696SDavid.Hollister@Sun.COM #define OQLIM_CHECK(p, l) \
5310696SDavid.Hollister@Sun.COM if (++l == (p)->ioq_depth) { \
5411048SDavid.Hollister@Sun.COM pmcs_prt(p, PMCS_PRT_DEBUG, NULL, NULL, \
5510696SDavid.Hollister@Sun.COM "%s: possible ob queue overflow", \
5610696SDavid.Hollister@Sun.COM __func__); \
5710696SDavid.Hollister@Sun.COM break; \
5810696SDavid.Hollister@Sun.COM }
5910696SDavid.Hollister@Sun.COM
6010696SDavid.Hollister@Sun.COM #define COPY_OUTBOUND(p, w, l, n, a, x, q, c) \
6110696SDavid.Hollister@Sun.COM n = ((w & PMCS_IOMB_BC_MASK) >> PMCS_IOMB_BC_SHIFT); \
6210696SDavid.Hollister@Sun.COM a = PMCS_QENTRY_SIZE; \
6310696SDavid.Hollister@Sun.COM (void) memcpy(l, x, PMCS_QENTRY_SIZE); \
6410696SDavid.Hollister@Sun.COM if (n > 1) { \
6510696SDavid.Hollister@Sun.COM a <<= 1; \
6610696SDavid.Hollister@Sun.COM (void) memcpy(&l[PMCS_QENTRY_SIZE], \
6710696SDavid.Hollister@Sun.COM GET_OQ_ENTRY(p, q, c, 1), PMCS_QENTRY_SIZE); \
6810696SDavid.Hollister@Sun.COM } \
6911048SDavid.Hollister@Sun.COM pmcs_prt(p, PMCS_PRT_DEBUG3, NULL, NULL, \
7010696SDavid.Hollister@Sun.COM "%s: ptr %p ci %d w0 %x nbuf %d", \
7110696SDavid.Hollister@Sun.COM __func__, (void *)x, ci, w0, n)
7210696SDavid.Hollister@Sun.COM
7310696SDavid.Hollister@Sun.COM #define EVT_PRT(hwp, msg, phy) \
7411501SDavid.Hollister@Sun.COM pmcs_prt(hwp, PMCS_PRT_DEBUG, NULL, NULL, "Phy 0x%x: %s", phy, # msg)
7510696SDavid.Hollister@Sun.COM
7610696SDavid.Hollister@Sun.COM
7710696SDavid.Hollister@Sun.COM /*
7810696SDavid.Hollister@Sun.COM * Map the link rate reported in the event to the SAS link rate value
7910696SDavid.Hollister@Sun.COM */
8010696SDavid.Hollister@Sun.COM static uint8_t
pmcs_link_rate(uint32_t event_link_rate)8110696SDavid.Hollister@Sun.COM pmcs_link_rate(uint32_t event_link_rate)
8210696SDavid.Hollister@Sun.COM {
8310696SDavid.Hollister@Sun.COM uint8_t sas_link_rate = 0;
8410696SDavid.Hollister@Sun.COM
8510696SDavid.Hollister@Sun.COM switch (event_link_rate) {
8610696SDavid.Hollister@Sun.COM case 1:
8710696SDavid.Hollister@Sun.COM sas_link_rate = SAS_LINK_RATE_1_5GBIT;
8810696SDavid.Hollister@Sun.COM break;
8910696SDavid.Hollister@Sun.COM case 2:
9010696SDavid.Hollister@Sun.COM sas_link_rate = SAS_LINK_RATE_3GBIT;
9110696SDavid.Hollister@Sun.COM break;
9210696SDavid.Hollister@Sun.COM case 4:
9310696SDavid.Hollister@Sun.COM sas_link_rate = SAS_LINK_RATE_6GBIT;
9410696SDavid.Hollister@Sun.COM break;
9510696SDavid.Hollister@Sun.COM }
9610696SDavid.Hollister@Sun.COM
9710696SDavid.Hollister@Sun.COM return (sas_link_rate);
9810696SDavid.Hollister@Sun.COM }
9910696SDavid.Hollister@Sun.COM
10010696SDavid.Hollister@Sun.COM /*
10110696SDavid.Hollister@Sun.COM * Called with pwrk lock
10210696SDavid.Hollister@Sun.COM */
10310696SDavid.Hollister@Sun.COM static void
pmcs_complete_work(pmcs_hw_t * pwp,pmcwork_t * pwrk,uint32_t * iomb,size_t amt)10410696SDavid.Hollister@Sun.COM pmcs_complete_work(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *iomb, size_t amt)
10510696SDavid.Hollister@Sun.COM {
10610696SDavid.Hollister@Sun.COM #ifdef DEBUG
10710696SDavid.Hollister@Sun.COM pwp->ltime[pwp->lti] = gethrtime();
10810696SDavid.Hollister@Sun.COM pwp->ltags[pwp->lti++] = pwrk->htag;
10910696SDavid.Hollister@Sun.COM #endif
11010696SDavid.Hollister@Sun.COM pwrk->htag |= PMCS_TAG_DONE;
11110696SDavid.Hollister@Sun.COM
11210696SDavid.Hollister@Sun.COM /*
11310696SDavid.Hollister@Sun.COM * If the command has timed out, leave it in that state.
11410696SDavid.Hollister@Sun.COM */
11510696SDavid.Hollister@Sun.COM if (pwrk->state != PMCS_WORK_STATE_TIMED_OUT) {
11610696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_INTR;
117*12807Ssrikanth.suravajhala@oracle.com pwrk->onwire = 0;
11810696SDavid.Hollister@Sun.COM }
11910696SDavid.Hollister@Sun.COM
12010696SDavid.Hollister@Sun.COM pmcs_complete_work_impl(pwp, pwrk, iomb, amt);
12110696SDavid.Hollister@Sun.COM }
12210696SDavid.Hollister@Sun.COM
12310696SDavid.Hollister@Sun.COM static void
pmcs_work_not_found(pmcs_hw_t * pwp,uint32_t htag,uint32_t * iomb)12410696SDavid.Hollister@Sun.COM pmcs_work_not_found(pmcs_hw_t *pwp, uint32_t htag, uint32_t *iomb)
12510696SDavid.Hollister@Sun.COM {
12610696SDavid.Hollister@Sun.COM #ifdef DEBUG
12710696SDavid.Hollister@Sun.COM int i;
12810696SDavid.Hollister@Sun.COM hrtime_t now;
12910696SDavid.Hollister@Sun.COM char buf[64];
13010696SDavid.Hollister@Sun.COM
13110696SDavid.Hollister@Sun.COM (void) snprintf(buf, sizeof (buf),
13210696SDavid.Hollister@Sun.COM "unable to find work structure for tag 0x%x", htag);
13310696SDavid.Hollister@Sun.COM
13411847SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG1, buf, iomb);
13510696SDavid.Hollister@Sun.COM if (htag == 0) {
13610696SDavid.Hollister@Sun.COM return;
13710696SDavid.Hollister@Sun.COM }
13810696SDavid.Hollister@Sun.COM now = gethrtime();
13910696SDavid.Hollister@Sun.COM for (i = 0; i < 256; i++) {
14010696SDavid.Hollister@Sun.COM mutex_enter(&pwp->dbglock);
14110696SDavid.Hollister@Sun.COM if (pwp->ltags[i] == htag) {
14211847SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
14310696SDavid.Hollister@Sun.COM "same tag already completed (%llu us ago)",
14410696SDavid.Hollister@Sun.COM (unsigned long long) (now - pwp->ltime[i]));
14510696SDavid.Hollister@Sun.COM }
14610696SDavid.Hollister@Sun.COM if (pwp->ftags[i] == htag) {
14711847SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
14810696SDavid.Hollister@Sun.COM "same tag started (line %d) (%llu ns ago)",
14910696SDavid.Hollister@Sun.COM pwp->ftag_lines[i], (unsigned long long)
15010696SDavid.Hollister@Sun.COM (now - pwp->ftime[i]));
15110696SDavid.Hollister@Sun.COM }
15210696SDavid.Hollister@Sun.COM mutex_exit(&pwp->dbglock);
15310696SDavid.Hollister@Sun.COM }
15410696SDavid.Hollister@Sun.COM #else
15510696SDavid.Hollister@Sun.COM char buf[64];
15610696SDavid.Hollister@Sun.COM (void) snprintf(buf, sizeof (buf),
15710696SDavid.Hollister@Sun.COM "unable to find work structure for tag 0x%x", htag);
15811847SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG1, buf, iomb);
15910696SDavid.Hollister@Sun.COM #endif
16010696SDavid.Hollister@Sun.COM }
16110696SDavid.Hollister@Sun.COM
16210696SDavid.Hollister@Sun.COM
16310696SDavid.Hollister@Sun.COM static void
pmcs_process_io_completion(pmcs_hw_t * pwp,pmcs_iocomp_cb_t * ioccb,size_t amt)16410696SDavid.Hollister@Sun.COM pmcs_process_io_completion(pmcs_hw_t *pwp, pmcs_iocomp_cb_t *ioccb, size_t amt)
16510696SDavid.Hollister@Sun.COM {
16610696SDavid.Hollister@Sun.COM pmcwork_t *pwrk;
16710696SDavid.Hollister@Sun.COM uint32_t tag_type;
16810696SDavid.Hollister@Sun.COM uint32_t htag = LE_32(((uint32_t *)((void *)ioccb->iomb))[1]);
16910696SDavid.Hollister@Sun.COM
17012258Ssrikanth.suravajhala@oracle.com pwrk = pmcs_tag2wp(pwp, htag, B_FALSE);
17110696SDavid.Hollister@Sun.COM if (pwrk == NULL) {
17210696SDavid.Hollister@Sun.COM pmcs_work_not_found(pwp, htag, (void *)&ioccb->iomb);
17310696SDavid.Hollister@Sun.COM kmem_cache_free(pwp->iocomp_cb_cache, ioccb);
17410696SDavid.Hollister@Sun.COM return;
17510696SDavid.Hollister@Sun.COM }
17610696SDavid.Hollister@Sun.COM
17710696SDavid.Hollister@Sun.COM pwrk->htag |= PMCS_TAG_DONE;
17810696SDavid.Hollister@Sun.COM
17910696SDavid.Hollister@Sun.COM /*
18010696SDavid.Hollister@Sun.COM * If the command has timed out, leave it in that state.
18110696SDavid.Hollister@Sun.COM */
18210696SDavid.Hollister@Sun.COM if (pwrk->state != PMCS_WORK_STATE_TIMED_OUT) {
18310696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_INTR;
184*12807Ssrikanth.suravajhala@oracle.com pwrk->onwire = 0;
18510696SDavid.Hollister@Sun.COM }
18610696SDavid.Hollister@Sun.COM
18710696SDavid.Hollister@Sun.COM /*
18810696SDavid.Hollister@Sun.COM * Some SATA and SAS commands are run in "WAIT" mode.
18910696SDavid.Hollister@Sun.COM * We can tell this from the tag type. In this case,
19010696SDavid.Hollister@Sun.COM * we just do a wakeup (not a callback).
19110696SDavid.Hollister@Sun.COM */
19210696SDavid.Hollister@Sun.COM tag_type = PMCS_TAG_TYPE(pwrk->htag);
19310696SDavid.Hollister@Sun.COM if (tag_type == PMCS_TAG_TYPE_WAIT) {
19410696SDavid.Hollister@Sun.COM ASSERT(PMCS_TAG_TYPE(pwrk->htag) == PMCS_TAG_TYPE_WAIT);
19510696SDavid.Hollister@Sun.COM if (pwrk->arg && amt) {
19610696SDavid.Hollister@Sun.COM (void) memcpy(pwrk->arg, ioccb->iomb, amt);
19710696SDavid.Hollister@Sun.COM }
19810696SDavid.Hollister@Sun.COM cv_signal(&pwrk->sleep_cv);
19910696SDavid.Hollister@Sun.COM mutex_exit(&pwrk->lock);
20010696SDavid.Hollister@Sun.COM kmem_cache_free(pwp->iocomp_cb_cache, ioccb);
20110696SDavid.Hollister@Sun.COM return;
20210696SDavid.Hollister@Sun.COM }
20310696SDavid.Hollister@Sun.COM ASSERT(tag_type == PMCS_TAG_TYPE_CBACK);
20410696SDavid.Hollister@Sun.COM
20510696SDavid.Hollister@Sun.COM #ifdef DEBUG
20610696SDavid.Hollister@Sun.COM pwp->ltime[pwp->lti] = gethrtime();
20710696SDavid.Hollister@Sun.COM pwp->ltags[pwp->lti++] = pwrk->htag;
20810696SDavid.Hollister@Sun.COM #endif
20910696SDavid.Hollister@Sun.COM
21010696SDavid.Hollister@Sun.COM ioccb->pwrk = pwrk;
21110696SDavid.Hollister@Sun.COM
21210696SDavid.Hollister@Sun.COM /*
21310696SDavid.Hollister@Sun.COM * Only update state to IOCOMPQ if we were in the INTR state.
21410696SDavid.Hollister@Sun.COM * Any other state (e.g. TIMED_OUT, ABORTED) needs to remain.
21510696SDavid.Hollister@Sun.COM */
21610696SDavid.Hollister@Sun.COM if (pwrk->state == PMCS_WORK_STATE_INTR) {
21710696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_IOCOMPQ;
21810696SDavid.Hollister@Sun.COM }
21910696SDavid.Hollister@Sun.COM
22010696SDavid.Hollister@Sun.COM mutex_enter(&pwp->cq_lock);
22110696SDavid.Hollister@Sun.COM if (pwp->iocomp_cb_tail) {
22210696SDavid.Hollister@Sun.COM pwp->iocomp_cb_tail->next = ioccb;
22310696SDavid.Hollister@Sun.COM pwp->iocomp_cb_tail = ioccb;
22410696SDavid.Hollister@Sun.COM } else {
22510696SDavid.Hollister@Sun.COM pwp->iocomp_cb_head = ioccb;
22610696SDavid.Hollister@Sun.COM pwp->iocomp_cb_tail = ioccb;
22710696SDavid.Hollister@Sun.COM }
22810696SDavid.Hollister@Sun.COM ioccb->next = NULL;
22910696SDavid.Hollister@Sun.COM mutex_exit(&pwp->cq_lock);
23010696SDavid.Hollister@Sun.COM
23110696SDavid.Hollister@Sun.COM mutex_exit(&pwrk->lock);
23210696SDavid.Hollister@Sun.COM /* Completion queue will be run at end of pmcs_iodone_intr */
23310696SDavid.Hollister@Sun.COM }
23410696SDavid.Hollister@Sun.COM
23510696SDavid.Hollister@Sun.COM
23610696SDavid.Hollister@Sun.COM static void
pmcs_process_completion(pmcs_hw_t * pwp,void * iomb,size_t amt)23710696SDavid.Hollister@Sun.COM pmcs_process_completion(pmcs_hw_t *pwp, void *iomb, size_t amt)
23810696SDavid.Hollister@Sun.COM {
23910696SDavid.Hollister@Sun.COM pmcwork_t *pwrk;
24010696SDavid.Hollister@Sun.COM uint32_t htag = LE_32(((uint32_t *)iomb)[1]);
24110696SDavid.Hollister@Sun.COM
24212258Ssrikanth.suravajhala@oracle.com pwrk = pmcs_tag2wp(pwp, htag, B_FALSE);
24310696SDavid.Hollister@Sun.COM if (pwrk == NULL) {
24410696SDavid.Hollister@Sun.COM pmcs_work_not_found(pwp, htag, iomb);
24510696SDavid.Hollister@Sun.COM return;
24610696SDavid.Hollister@Sun.COM }
24710696SDavid.Hollister@Sun.COM
24810696SDavid.Hollister@Sun.COM pmcs_complete_work(pwp, pwrk, iomb, amt);
24910696SDavid.Hollister@Sun.COM /*
25010696SDavid.Hollister@Sun.COM * The pwrk lock is now released
25110696SDavid.Hollister@Sun.COM */
25210696SDavid.Hollister@Sun.COM }
25310696SDavid.Hollister@Sun.COM
25410696SDavid.Hollister@Sun.COM static void
pmcs_kill_port(pmcs_hw_t * pwp,int portid)25510696SDavid.Hollister@Sun.COM pmcs_kill_port(pmcs_hw_t *pwp, int portid)
25610696SDavid.Hollister@Sun.COM {
25710696SDavid.Hollister@Sun.COM pmcs_phy_t *pptr = pwp->ports[portid];
25810696SDavid.Hollister@Sun.COM
25910696SDavid.Hollister@Sun.COM if (pptr == NULL) {
26010696SDavid.Hollister@Sun.COM return;
26110696SDavid.Hollister@Sun.COM }
26210696SDavid.Hollister@Sun.COM
26310696SDavid.Hollister@Sun.COM /*
26410696SDavid.Hollister@Sun.COM * Clear any subsidiary phys
26510696SDavid.Hollister@Sun.COM */
26610696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
26710696SDavid.Hollister@Sun.COM
26810696SDavid.Hollister@Sun.COM for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
26910696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
27010696SDavid.Hollister@Sun.COM if (pptr->link_rate && pptr->portid == portid &&
27110696SDavid.Hollister@Sun.COM pptr->subsidiary) {
27210696SDavid.Hollister@Sun.COM pmcs_clear_phy(pwp, pptr);
27310696SDavid.Hollister@Sun.COM }
27410696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
27510696SDavid.Hollister@Sun.COM }
27610696SDavid.Hollister@Sun.COM
27710696SDavid.Hollister@Sun.COM pptr = pwp->ports[portid];
27810696SDavid.Hollister@Sun.COM pwp->ports[portid] = NULL;
27910696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
28010696SDavid.Hollister@Sun.COM
28110696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
28210696SDavid.Hollister@Sun.COM pmcs_kill_changed(pwp, pptr, 0);
28310696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
28410696SDavid.Hollister@Sun.COM
28510696SDavid.Hollister@Sun.COM RESTART_DISCOVERY(pwp);
28611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "PortID 0x%x Cleared", portid);
28710696SDavid.Hollister@Sun.COM }
28810696SDavid.Hollister@Sun.COM
28910696SDavid.Hollister@Sun.COM void
pmcs_process_sas_hw_event(pmcs_hw_t * pwp,void * iomb,size_t amt)29010696SDavid.Hollister@Sun.COM pmcs_process_sas_hw_event(pmcs_hw_t *pwp, void *iomb, size_t amt)
29110696SDavid.Hollister@Sun.COM {
29210696SDavid.Hollister@Sun.COM uint32_t w1 = LE_32(((uint32_t *)iomb)[1]);
29310696SDavid.Hollister@Sun.COM uint32_t w3 = LE_32(((uint32_t *)iomb)[3]);
29410696SDavid.Hollister@Sun.COM char buf[32];
29510696SDavid.Hollister@Sun.COM uint8_t phynum = IOP_EVENT_PHYNUM(w1);
29610696SDavid.Hollister@Sun.COM uint8_t portid = IOP_EVENT_PORTID(w1);
29710696SDavid.Hollister@Sun.COM pmcs_iport_t *iport;
29810696SDavid.Hollister@Sun.COM pmcs_phy_t *pptr, *subphy, *tphyp;
29910696SDavid.Hollister@Sun.COM int need_ack = 0;
30010696SDavid.Hollister@Sun.COM int primary;
30110696SDavid.Hollister@Sun.COM
30210696SDavid.Hollister@Sun.COM switch (IOP_EVENT_EVENT(w1)) {
30310696SDavid.Hollister@Sun.COM case IOP_EVENT_PHY_STOP_STATUS:
30410696SDavid.Hollister@Sun.COM if (IOP_EVENT_STATUS(w1)) {
30511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
30610696SDavid.Hollister@Sun.COM "PORT %d failed to stop (0x%x)",
30710696SDavid.Hollister@Sun.COM phynum, IOP_EVENT_STATUS(w1));
30810696SDavid.Hollister@Sun.COM } else {
30911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
31011048SDavid.Hollister@Sun.COM "PHY 0x%x Stopped", phynum);
31110696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
31210696SDavid.Hollister@Sun.COM pptr = pwp->root_phys + phynum;
31310696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
31410696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
31510696SDavid.Hollister@Sun.COM if (pptr->configured) {
31610696SDavid.Hollister@Sun.COM pmcs_kill_changed(pwp, pptr, 0);
31710696SDavid.Hollister@Sun.COM } else {
31810696SDavid.Hollister@Sun.COM pmcs_set_changed(pwp, pptr, B_TRUE, 0);
31910696SDavid.Hollister@Sun.COM }
32010696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
32110696SDavid.Hollister@Sun.COM RESTART_DISCOVERY(pwp);
32210696SDavid.Hollister@Sun.COM }
32310696SDavid.Hollister@Sun.COM /* Reposition htag to the 'expected' position. */
32410696SDavid.Hollister@Sun.COM ((uint32_t *)iomb)[1] = ((uint32_t *)iomb)[2];
32510696SDavid.Hollister@Sun.COM pmcs_process_completion(pwp, iomb, amt);
32610696SDavid.Hollister@Sun.COM break;
32710696SDavid.Hollister@Sun.COM case IOP_EVENT_SAS_PHY_UP:
32810696SDavid.Hollister@Sun.COM {
32910696SDavid.Hollister@Sun.COM static const uint8_t sas_identify_af_endian_xfvec[] = {
33010696SDavid.Hollister@Sun.COM 0x5c, 0x5a, 0x56, 0x00
33110696SDavid.Hollister@Sun.COM };
33210696SDavid.Hollister@Sun.COM pmcs_phy_t *rp;
33310696SDavid.Hollister@Sun.COM sas_identify_af_t af;
33411601SDavid.Hollister@Sun.COM uint64_t phy_id, wwn;
33510696SDavid.Hollister@Sun.COM
33610696SDavid.Hollister@Sun.COM /*
33710696SDavid.Hollister@Sun.COM * If we're not at running state, don't do anything
33810696SDavid.Hollister@Sun.COM */
33910696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
34010696SDavid.Hollister@Sun.COM if (pwp->state != STATE_RUNNING) {
34110696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
34210696SDavid.Hollister@Sun.COM break;
34310696SDavid.Hollister@Sun.COM }
34410696SDavid.Hollister@Sun.COM pptr = pwp->root_phys + phynum;
34510696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
34610696SDavid.Hollister@Sun.COM
34711090SDavid.Hollister@Sun.COM /*
34811090SDavid.Hollister@Sun.COM * No need to lock the primary root PHY. It can never go
34911090SDavid.Hollister@Sun.COM * away, and we're only concerned with the port width and
35011090SDavid.Hollister@Sun.COM * the portid, both of which only ever change in this function.
35111090SDavid.Hollister@Sun.COM */
35210696SDavid.Hollister@Sun.COM rp = pwp->ports[portid];
35310696SDavid.Hollister@Sun.COM
35410696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
35510696SDavid.Hollister@Sun.COM
35610696SDavid.Hollister@Sun.COM pmcs_endian_transform(pwp, &af, &((uint32_t *)iomb)[4],
35710696SDavid.Hollister@Sun.COM sas_identify_af_endian_xfvec);
35810696SDavid.Hollister@Sun.COM
35910696SDavid.Hollister@Sun.COM /* Copy the remote address into our phy handle */
36010696SDavid.Hollister@Sun.COM (void) memcpy(pptr->sas_address, af.sas_address, 8);
36111601SDavid.Hollister@Sun.COM wwn = pmcs_barray2wwn(pptr->sas_address);
36211307SDavid.Hollister@Sun.COM phy_id = (uint64_t)af.phy_identifier;
36310696SDavid.Hollister@Sun.COM
36410696SDavid.Hollister@Sun.COM /*
36510696SDavid.Hollister@Sun.COM * Check to see if there is a PortID already active.
36610696SDavid.Hollister@Sun.COM */
36710696SDavid.Hollister@Sun.COM if (rp) {
36810696SDavid.Hollister@Sun.COM if (rp->portid != portid) {
36910696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
37011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
37111048SDavid.Hollister@Sun.COM "PortID 0x%x: PHY 0x%x SAS LINK UP IS FOR "
37211048SDavid.Hollister@Sun.COM "A DIFFERENT PORTID 0x%x", rp->portid,
37310696SDavid.Hollister@Sun.COM phynum, portid);
37410696SDavid.Hollister@Sun.COM break;
37510696SDavid.Hollister@Sun.COM }
37610696SDavid.Hollister@Sun.COM
37710696SDavid.Hollister@Sun.COM /*
37810696SDavid.Hollister@Sun.COM * If the dtype isn't NOTHING, then this is actually
37910696SDavid.Hollister@Sun.COM * the primary PHY for this port. It probably went
38010696SDavid.Hollister@Sun.COM * down and came back up, so be sure not to mark it
38110696SDavid.Hollister@Sun.COM * as a subsidiary.
38210696SDavid.Hollister@Sun.COM */
38310696SDavid.Hollister@Sun.COM if (pptr->dtype == NOTHING) {
38410696SDavid.Hollister@Sun.COM pptr->subsidiary = 1;
38510696SDavid.Hollister@Sun.COM }
38610696SDavid.Hollister@Sun.COM pptr->link_rate =
38710696SDavid.Hollister@Sun.COM pmcs_link_rate(IOP_EVENT_LINK_RATE(w1));
38810696SDavid.Hollister@Sun.COM pptr->portid = portid;
38910696SDavid.Hollister@Sun.COM pptr->dead = 0;
39011090SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
39110696SDavid.Hollister@Sun.COM
39210696SDavid.Hollister@Sun.COM rp->width = IOP_EVENT_NPIP(w3);
39310696SDavid.Hollister@Sun.COM
39410696SDavid.Hollister@Sun.COM /* Add this PHY to the phymap */
39510696SDavid.Hollister@Sun.COM if (sas_phymap_phy_add(pwp->hss_phymap, phynum,
39611601SDavid.Hollister@Sun.COM pwp->sas_wwns[0], wwn) != DDI_SUCCESS) {
39711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
39810696SDavid.Hollister@Sun.COM "Unable to add phy %u for 0x%" PRIx64 ".0x%"
39910696SDavid.Hollister@Sun.COM PRIx64, phynum, pwp->sas_wwns[rp->phynum],
40011601SDavid.Hollister@Sun.COM wwn);
40110696SDavid.Hollister@Sun.COM }
40210696SDavid.Hollister@Sun.COM
40311307SDavid.Hollister@Sun.COM /*
40411307SDavid.Hollister@Sun.COM * Get our iport, if attached, and set it up. Update
40511307SDavid.Hollister@Sun.COM * the PHY's phymask props while we're locked.
40611307SDavid.Hollister@Sun.COM */
40711090SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
40811442SDavid.Hollister@Sun.COM pmcs_update_phy_pm_props(pptr, (1ULL << phynum),
40911442SDavid.Hollister@Sun.COM (1ULL << phy_id), B_TRUE);
41011090SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
41111601SDavid.Hollister@Sun.COM iport = pmcs_get_iport_by_wwn(pwp, wwn);
41210696SDavid.Hollister@Sun.COM if (iport) {
41310696SDavid.Hollister@Sun.COM primary = !pptr->subsidiary;
41410696SDavid.Hollister@Sun.COM
41510696SDavid.Hollister@Sun.COM mutex_enter(&iport->lock);
41610696SDavid.Hollister@Sun.COM if (primary) {
41710696SDavid.Hollister@Sun.COM iport->pptr = pptr;
41810696SDavid.Hollister@Sun.COM }
41910696SDavid.Hollister@Sun.COM if (iport->ua_state == UA_ACTIVE) {
42010696SDavid.Hollister@Sun.COM pmcs_add_phy_to_iport(iport, pptr);
42111267SJesse.Butler@Sun.COM pptr->iport = iport;
42210696SDavid.Hollister@Sun.COM }
42310696SDavid.Hollister@Sun.COM mutex_exit(&iport->lock);
42410696SDavid.Hollister@Sun.COM pmcs_rele_iport(iport);
42510696SDavid.Hollister@Sun.COM }
42610696SDavid.Hollister@Sun.COM
42711442SDavid.Hollister@Sun.COM pmcs_update_phy_pm_props(rp, (1ULL << phynum),
42811442SDavid.Hollister@Sun.COM (1ULL << phy_id), B_TRUE);
42911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
43011048SDavid.Hollister@Sun.COM "PortID 0x%x: PHY 0x%x SAS LINK UP WIDENS PORT "
43111048SDavid.Hollister@Sun.COM "TO %d PHYS", portid, phynum, rp->width);
43210696SDavid.Hollister@Sun.COM
43310696SDavid.Hollister@Sun.COM break;
43410696SDavid.Hollister@Sun.COM }
43510696SDavid.Hollister@Sun.COM
43610696SDavid.Hollister@Sun.COM /*
43710696SDavid.Hollister@Sun.COM * Check to see if anything is here already
43810696SDavid.Hollister@Sun.COM */
43910696SDavid.Hollister@Sun.COM if (pptr->dtype != NOTHING && pptr->configured) {
44010696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
44111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
44211048SDavid.Hollister@Sun.COM "PortID 0x%x: SAS PHY 0x%x UP HITS EXISTING "
44311048SDavid.Hollister@Sun.COM "CONFIGURED TREE", portid, phynum);
44410696SDavid.Hollister@Sun.COM break;
44510696SDavid.Hollister@Sun.COM }
44610696SDavid.Hollister@Sun.COM
44710696SDavid.Hollister@Sun.COM if (af.address_frame_type != SAS_AF_IDENTIFY) {
44810696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
44911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
45010696SDavid.Hollister@Sun.COM "SAS link up on phy 0x%x, "
45110696SDavid.Hollister@Sun.COM "but unexpected frame type 0x%x found", phynum,
45210696SDavid.Hollister@Sun.COM af.address_frame_type);
45310696SDavid.Hollister@Sun.COM break;
45410696SDavid.Hollister@Sun.COM }
45510696SDavid.Hollister@Sun.COM pptr->width = IOP_EVENT_NPIP(w3);
45610696SDavid.Hollister@Sun.COM pptr->portid = portid;
45710696SDavid.Hollister@Sun.COM pptr->dead = 0;
45810696SDavid.Hollister@Sun.COM pptr->link_rate = pmcs_link_rate(IOP_EVENT_LINK_RATE(w1));
45910696SDavid.Hollister@Sun.COM
46010696SDavid.Hollister@Sun.COM /*
46110696SDavid.Hollister@Sun.COM * Check to see whether this is an expander or an endpoint
46210696SDavid.Hollister@Sun.COM */
46310696SDavid.Hollister@Sun.COM switch (af.device_type) {
46410696SDavid.Hollister@Sun.COM case SAS_IF_DTYPE_ENDPOINT:
46510696SDavid.Hollister@Sun.COM pptr->pend_dtype = SAS;
46610696SDavid.Hollister@Sun.COM pptr->dtype = SAS;
46710696SDavid.Hollister@Sun.COM break;
46810696SDavid.Hollister@Sun.COM case SAS_IF_DTYPE_EDGE:
46910696SDavid.Hollister@Sun.COM case SAS_IF_DTYPE_FANOUT:
47010696SDavid.Hollister@Sun.COM pptr->pend_dtype = EXPANDER;
47110696SDavid.Hollister@Sun.COM pptr->dtype = EXPANDER;
47210696SDavid.Hollister@Sun.COM break;
47310696SDavid.Hollister@Sun.COM default:
47411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
47510696SDavid.Hollister@Sun.COM "unknown device type 0x%x", af.device_type);
47610696SDavid.Hollister@Sun.COM pptr->pend_dtype = NOTHING;
47710696SDavid.Hollister@Sun.COM pptr->dtype = NOTHING;
47810696SDavid.Hollister@Sun.COM break;
47910696SDavid.Hollister@Sun.COM }
48010696SDavid.Hollister@Sun.COM
48110696SDavid.Hollister@Sun.COM /*
48210696SDavid.Hollister@Sun.COM * If this is a direct-attached SAS drive, do the spinup
48310696SDavid.Hollister@Sun.COM * release now.
48410696SDavid.Hollister@Sun.COM */
48510696SDavid.Hollister@Sun.COM if (pptr->dtype == SAS) {
48610696SDavid.Hollister@Sun.COM pptr->spinup_hold = 1;
48710696SDavid.Hollister@Sun.COM pmcs_spinup_release(pwp, pptr);
48811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL,
48910696SDavid.Hollister@Sun.COM "Release spinup hold on PHY 0x%x", phynum);
49010696SDavid.Hollister@Sun.COM }
49110696SDavid.Hollister@Sun.COM
49210696SDavid.Hollister@Sun.COM pmcs_set_changed(pwp, pptr, B_TRUE, 0);
49310696SDavid.Hollister@Sun.COM if (pptr->width > 1) {
49411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, pptr, NULL,
49511048SDavid.Hollister@Sun.COM "PortID 0x%x: PHY 0x%x SAS"
49610696SDavid.Hollister@Sun.COM " LINK UP @ %s Gb with %d phys/s", portid, phynum,
49710696SDavid.Hollister@Sun.COM pmcs_get_rate(pptr->link_rate), pptr->width);
49810696SDavid.Hollister@Sun.COM } else {
49911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, pptr, NULL,
50011048SDavid.Hollister@Sun.COM "PortID 0x%x: PHY 0x%x SAS"
50110696SDavid.Hollister@Sun.COM " LINK UP @ %s Gb/s", portid, phynum,
50210696SDavid.Hollister@Sun.COM pmcs_get_rate(pptr->link_rate));
50310696SDavid.Hollister@Sun.COM }
50410696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
50510696SDavid.Hollister@Sun.COM
50610696SDavid.Hollister@Sun.COM /* Add this PHY to the phymap */
50710696SDavid.Hollister@Sun.COM if (sas_phymap_phy_add(pwp->hss_phymap, phynum,
50811601SDavid.Hollister@Sun.COM pwp->sas_wwns[0], wwn) != DDI_SUCCESS) {
50911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
51010696SDavid.Hollister@Sun.COM "Unable to add phy %u for 0x%" PRIx64 ".0x%"
51111601SDavid.Hollister@Sun.COM PRIx64, phynum, pwp->sas_wwns[pptr->phynum], wwn);
51210696SDavid.Hollister@Sun.COM }
51310696SDavid.Hollister@Sun.COM
51410696SDavid.Hollister@Sun.COM /* Get a pointer to our iport and set it up if attached */
51511601SDavid.Hollister@Sun.COM iport = pmcs_get_iport_by_wwn(pwp, wwn);
51610696SDavid.Hollister@Sun.COM if (iport) {
51710696SDavid.Hollister@Sun.COM primary = !pptr->subsidiary;
51810696SDavid.Hollister@Sun.COM
51910696SDavid.Hollister@Sun.COM mutex_enter(&iport->lock);
52010696SDavid.Hollister@Sun.COM if (primary) {
52110696SDavid.Hollister@Sun.COM iport->pptr = pptr;
52210696SDavid.Hollister@Sun.COM }
52310696SDavid.Hollister@Sun.COM if (iport->ua_state == UA_ACTIVE) {
52410696SDavid.Hollister@Sun.COM pmcs_add_phy_to_iport(iport, pptr);
52511267SJesse.Butler@Sun.COM pptr->iport = iport;
52610696SDavid.Hollister@Sun.COM }
52710696SDavid.Hollister@Sun.COM mutex_exit(&iport->lock);
52810696SDavid.Hollister@Sun.COM pmcs_rele_iport(iport);
52910696SDavid.Hollister@Sun.COM }
53010696SDavid.Hollister@Sun.COM
53111090SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
53211442SDavid.Hollister@Sun.COM pmcs_update_phy_pm_props(pptr, (1ULL << phynum),
53311442SDavid.Hollister@Sun.COM (1ULL << phy_id), B_TRUE);
53410696SDavid.Hollister@Sun.COM pmcs_smhba_log_sysevent(pwp, ESC_SAS_PHY_EVENT,
53510696SDavid.Hollister@Sun.COM SAS_PHY_ONLINE, pptr);
53610696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
53710696SDavid.Hollister@Sun.COM
53810696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
53910696SDavid.Hollister@Sun.COM pwp->ports[portid] = pptr;
54010696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
54110696SDavid.Hollister@Sun.COM RESTART_DISCOVERY(pwp);
54210696SDavid.Hollister@Sun.COM
54310696SDavid.Hollister@Sun.COM break;
54410696SDavid.Hollister@Sun.COM }
54511601SDavid.Hollister@Sun.COM case IOP_EVENT_SATA_PHY_UP: {
54611601SDavid.Hollister@Sun.COM uint64_t wwn;
54710696SDavid.Hollister@Sun.COM /*
54810696SDavid.Hollister@Sun.COM * If we're not at running state, don't do anything
54910696SDavid.Hollister@Sun.COM */
55010696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
55110696SDavid.Hollister@Sun.COM if (pwp->state != STATE_RUNNING) {
55210696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
55310696SDavid.Hollister@Sun.COM break;
55410696SDavid.Hollister@Sun.COM }
55510696SDavid.Hollister@Sun.COM
55610696SDavid.Hollister@Sun.COM /*
55710696SDavid.Hollister@Sun.COM * Check to see if anything is here already
55810696SDavid.Hollister@Sun.COM */
55910696SDavid.Hollister@Sun.COM pmcs_lock_phy(pwp->root_phys + phynum);
56010696SDavid.Hollister@Sun.COM pptr = pwp->root_phys + phynum;
56110696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
56210696SDavid.Hollister@Sun.COM
56310696SDavid.Hollister@Sun.COM if (pptr->dtype != NOTHING && pptr->configured) {
56410696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
56511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
56610696SDavid.Hollister@Sun.COM "PortID 0x%x: SATA PHY 0x%x"
56710696SDavid.Hollister@Sun.COM " UP HITS EXISTING CONFIGURED TREE",
56810696SDavid.Hollister@Sun.COM portid, phynum);
56910696SDavid.Hollister@Sun.COM break;
57010696SDavid.Hollister@Sun.COM }
57110696SDavid.Hollister@Sun.COM
57210696SDavid.Hollister@Sun.COM pptr->width = 1;
57310696SDavid.Hollister@Sun.COM pptr->dead = 0;
57410696SDavid.Hollister@Sun.COM
57510696SDavid.Hollister@Sun.COM /*
57610696SDavid.Hollister@Sun.COM * Install the PHY number in the least significant byte
57710696SDavid.Hollister@Sun.COM * with a NAA=3 (locally assigned address) in the most
57810696SDavid.Hollister@Sun.COM * significant nubble.
57910696SDavid.Hollister@Sun.COM *
58010696SDavid.Hollister@Sun.COM * Later, we'll either use that or dig a
58110696SDavid.Hollister@Sun.COM * WWN out of words 108..111.
58210696SDavid.Hollister@Sun.COM */
58310696SDavid.Hollister@Sun.COM pptr->sas_address[0] = 0x30;
58410696SDavid.Hollister@Sun.COM pptr->sas_address[1] = 0;
58510696SDavid.Hollister@Sun.COM pptr->sas_address[2] = 0;
58610696SDavid.Hollister@Sun.COM pptr->sas_address[3] = 0;
58710696SDavid.Hollister@Sun.COM pptr->sas_address[4] = 0;
58810696SDavid.Hollister@Sun.COM pptr->sas_address[5] = 0;
58910696SDavid.Hollister@Sun.COM pptr->sas_address[6] = 0;
59010696SDavid.Hollister@Sun.COM pptr->sas_address[7] = phynum;
59110696SDavid.Hollister@Sun.COM pptr->portid = portid;
59210696SDavid.Hollister@Sun.COM pptr->link_rate = pmcs_link_rate(IOP_EVENT_LINK_RATE(w1));
59310696SDavid.Hollister@Sun.COM pptr->dtype = SATA;
59410696SDavid.Hollister@Sun.COM pmcs_set_changed(pwp, pptr, B_TRUE, 0);
59511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, pptr, NULL,
59610696SDavid.Hollister@Sun.COM "PortID 0x%x: PHY 0x%x SATA LINK UP @ %s Gb/s",
59710696SDavid.Hollister@Sun.COM pptr->portid, phynum, pmcs_get_rate(pptr->link_rate));
59811601SDavid.Hollister@Sun.COM wwn = pmcs_barray2wwn(pptr->sas_address);
59910696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
60010696SDavid.Hollister@Sun.COM
60110696SDavid.Hollister@Sun.COM /* Add this PHY to the phymap */
60210696SDavid.Hollister@Sun.COM if (sas_phymap_phy_add(pwp->hss_phymap, phynum,
60311601SDavid.Hollister@Sun.COM pwp->sas_wwns[0], wwn) != DDI_SUCCESS) {
60411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
60510696SDavid.Hollister@Sun.COM "Unable to add phy %u for 0x%" PRIx64 ".0x%"
60610696SDavid.Hollister@Sun.COM PRIx64, phynum, pwp->sas_wwns[pptr->phynum],
60711601SDavid.Hollister@Sun.COM wwn);
60810696SDavid.Hollister@Sun.COM }
60910696SDavid.Hollister@Sun.COM
61010696SDavid.Hollister@Sun.COM /* Get our iport, if attached, and set it up */
61111601SDavid.Hollister@Sun.COM iport = pmcs_get_iport_by_wwn(pwp, wwn);
61210696SDavid.Hollister@Sun.COM if (iport) {
61310696SDavid.Hollister@Sun.COM mutex_enter(&iport->lock);
61410696SDavid.Hollister@Sun.COM iport->pptr = pptr;
61510696SDavid.Hollister@Sun.COM if (iport->ua_state == UA_ACTIVE) {
61610696SDavid.Hollister@Sun.COM pmcs_add_phy_to_iport(iport, pptr);
61711267SJesse.Butler@Sun.COM pptr->iport = iport;
61810696SDavid.Hollister@Sun.COM ASSERT(iport->nphy == 1);
61910696SDavid.Hollister@Sun.COM iport->nphy = 1;
62010696SDavid.Hollister@Sun.COM }
62110696SDavid.Hollister@Sun.COM mutex_exit(&iport->lock);
62210696SDavid.Hollister@Sun.COM pmcs_rele_iport(iport);
62310696SDavid.Hollister@Sun.COM }
62410696SDavid.Hollister@Sun.COM
62511090SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
62611442SDavid.Hollister@Sun.COM pmcs_update_phy_pm_props(pptr, (1ULL << phynum), 1ULL, B_TRUE);
62710696SDavid.Hollister@Sun.COM pmcs_smhba_log_sysevent(pwp, ESC_SAS_PHY_EVENT,
62810696SDavid.Hollister@Sun.COM SAS_PHY_ONLINE, pptr);
62910696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
63010696SDavid.Hollister@Sun.COM
63110696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
63210696SDavid.Hollister@Sun.COM pwp->ports[pptr->portid] = pptr;
63310696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
63410696SDavid.Hollister@Sun.COM RESTART_DISCOVERY(pwp);
63510696SDavid.Hollister@Sun.COM break;
63611601SDavid.Hollister@Sun.COM }
63710696SDavid.Hollister@Sun.COM
63810696SDavid.Hollister@Sun.COM case IOP_EVENT_SATA_SPINUP_HOLD:
63910696SDavid.Hollister@Sun.COM tphyp = (pmcs_phy_t *)(pwp->root_phys + phynum);
64010696SDavid.Hollister@Sun.COM /*
64110696SDavid.Hollister@Sun.COM * No need to lock the entire tree for this
64210696SDavid.Hollister@Sun.COM */
64310696SDavid.Hollister@Sun.COM mutex_enter(&tphyp->phy_lock);
64410696SDavid.Hollister@Sun.COM tphyp->spinup_hold = 1;
64510696SDavid.Hollister@Sun.COM pmcs_spinup_release(pwp, tphyp);
64610696SDavid.Hollister@Sun.COM mutex_exit(&tphyp->phy_lock);
64710696SDavid.Hollister@Sun.COM break;
64811601SDavid.Hollister@Sun.COM case IOP_EVENT_PHY_DOWN: {
64911601SDavid.Hollister@Sun.COM uint64_t wwn;
65011601SDavid.Hollister@Sun.COM
65110696SDavid.Hollister@Sun.COM /*
65210696SDavid.Hollister@Sun.COM * If we're not at running state, don't do anything
65310696SDavid.Hollister@Sun.COM */
65410696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
65510696SDavid.Hollister@Sun.COM if (pwp->state != STATE_RUNNING) {
65610696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
65710696SDavid.Hollister@Sun.COM break;
65810696SDavid.Hollister@Sun.COM }
65910696SDavid.Hollister@Sun.COM pptr = pwp->ports[portid];
66010696SDavid.Hollister@Sun.COM
66110696SDavid.Hollister@Sun.COM subphy = pwp->root_phys + phynum;
66210696SDavid.Hollister@Sun.COM /*
66310696SDavid.Hollister@Sun.COM * subphy is a pointer to the PHY corresponding to the incoming
66410696SDavid.Hollister@Sun.COM * event. pptr points to the primary PHY for the corresponding
66510696SDavid.Hollister@Sun.COM * port. So, subphy and pptr may or may not be the same PHY,
66610696SDavid.Hollister@Sun.COM * but that doesn't change what we need to do with each.
66710696SDavid.Hollister@Sun.COM */
66810696SDavid.Hollister@Sun.COM ASSERT(subphy);
66910696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
67010696SDavid.Hollister@Sun.COM
67110696SDavid.Hollister@Sun.COM if (pptr == NULL) {
67211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
67311048SDavid.Hollister@Sun.COM "PortID 0x%x: PHY 0x%x LINK DOWN- no portid ptr",
67411048SDavid.Hollister@Sun.COM portid, phynum);
67510696SDavid.Hollister@Sun.COM break;
67610696SDavid.Hollister@Sun.COM }
67710696SDavid.Hollister@Sun.COM if (IOP_EVENT_PORT_STATE(w3) == IOP_EVENT_PS_NIL) {
67811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
67910696SDavid.Hollister@Sun.COM "PortID 0x%x: PHY 0x%x NOT VALID YET",
68010696SDavid.Hollister@Sun.COM portid, phynum);
68110696SDavid.Hollister@Sun.COM need_ack = 1;
68210696SDavid.Hollister@Sun.COM break;
68310696SDavid.Hollister@Sun.COM }
68410696SDavid.Hollister@Sun.COM if (IOP_EVENT_PORT_STATE(w3) == IOP_EVENT_PS_IN_RESET) {
68511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
68610696SDavid.Hollister@Sun.COM "PortID 0x%x: PHY 0x%x IN RESET",
68710696SDavid.Hollister@Sun.COM portid, phynum);
68811133SJesse.Butler@Sun.COM /* Entire port is down due to a host-initiated reset */
68911133SJesse.Butler@Sun.COM mutex_enter(&pptr->phy_lock);
69011307SDavid.Hollister@Sun.COM /* Clear the phymask props in pptr */
69111307SDavid.Hollister@Sun.COM pmcs_update_phy_pm_props(pptr, pptr->att_port_pm_tmp,
69211307SDavid.Hollister@Sun.COM pptr->tgt_port_pm_tmp, B_FALSE);
69311133SJesse.Butler@Sun.COM iport = pptr->iport;
69411133SJesse.Butler@Sun.COM mutex_exit(&pptr->phy_lock);
69511133SJesse.Butler@Sun.COM if (iport) {
69611133SJesse.Butler@Sun.COM mutex_enter(&iport->lock);
69711133SJesse.Butler@Sun.COM pmcs_iport_teardown_phys(iport);
69811133SJesse.Butler@Sun.COM mutex_exit(&iport->lock);
69911133SJesse.Butler@Sun.COM }
70011133SJesse.Butler@Sun.COM
70111347SRamana.Srikanth@Sun.COM /* Clear down all PHYs in the port */
70211347SRamana.Srikanth@Sun.COM for (pptr = pwp->root_phys; pptr;
70311347SRamana.Srikanth@Sun.COM pptr = pptr->sibling) {
70411347SRamana.Srikanth@Sun.COM pmcs_lock_phy(pptr);
70511347SRamana.Srikanth@Sun.COM if (pptr->portid == portid) {
70612462Sjesse.butler@oracle.com pptr->pend_dtype = NOTHING;
70711347SRamana.Srikanth@Sun.COM pptr->dtype = NOTHING;
70811347SRamana.Srikanth@Sun.COM pptr->portid =
70911347SRamana.Srikanth@Sun.COM PMCS_IPORT_INVALID_PORT_ID;
71011347SRamana.Srikanth@Sun.COM if (pptr->valid_device_id) {
71111347SRamana.Srikanth@Sun.COM pptr->deregister_wait = 1;
71211347SRamana.Srikanth@Sun.COM }
71311347SRamana.Srikanth@Sun.COM }
71411347SRamana.Srikanth@Sun.COM pmcs_unlock_phy(pptr);
71511347SRamana.Srikanth@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_DEREGISTER_DEV);
71611347SRamana.Srikanth@Sun.COM (void) ddi_taskq_dispatch(pwp->tq, pmcs_worker,
71711347SRamana.Srikanth@Sun.COM pwp, DDI_NOSLEEP);
71811347SRamana.Srikanth@Sun.COM }
71911347SRamana.Srikanth@Sun.COM
72010696SDavid.Hollister@Sun.COM break;
72110696SDavid.Hollister@Sun.COM }
72211347SRamana.Srikanth@Sun.COM
72310696SDavid.Hollister@Sun.COM if (IOP_EVENT_PORT_STATE(w3) == IOP_EVENT_PS_LOSTCOMM) {
72411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
72510696SDavid.Hollister@Sun.COM "PortID 0x%x: PHY 0x%x TEMPORARILY DOWN",
72610696SDavid.Hollister@Sun.COM portid, phynum);
72710696SDavid.Hollister@Sun.COM need_ack = 1;
72810696SDavid.Hollister@Sun.COM break;
72910696SDavid.Hollister@Sun.COM }
73010696SDavid.Hollister@Sun.COM
73110696SDavid.Hollister@Sun.COM if (IOP_EVENT_PORT_STATE(w3) == IOP_EVENT_PS_VALID) {
73211601SDavid.Hollister@Sun.COM
73310696SDavid.Hollister@Sun.COM /*
73411133SJesse.Butler@Sun.COM * This is not the last phy in the port, so if this
73511133SJesse.Butler@Sun.COM * is the primary PHY, promote another PHY to primary.
73611133SJesse.Butler@Sun.COM */
73711133SJesse.Butler@Sun.COM if (pptr == subphy) {
73811133SJesse.Butler@Sun.COM primary = !subphy->subsidiary;
73911133SJesse.Butler@Sun.COM ASSERT(primary);
74011133SJesse.Butler@Sun.COM
74111133SJesse.Butler@Sun.COM tphyp = pptr;
74211133SJesse.Butler@Sun.COM pptr = pmcs_promote_next_phy(tphyp);
74311133SJesse.Butler@Sun.COM
74411133SJesse.Butler@Sun.COM if (pptr) {
74511133SJesse.Butler@Sun.COM /* Update primary pptr in ports */
74611133SJesse.Butler@Sun.COM pwp->ports[portid] = pptr;
74711133SJesse.Butler@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr,
74811133SJesse.Butler@Sun.COM NULL, "PortID 0x%x: PHY 0x%x "
74911133SJesse.Butler@Sun.COM "promoted to primary", portid,
75011133SJesse.Butler@Sun.COM pptr->phynum);
75111133SJesse.Butler@Sun.COM } else {
75211133SJesse.Butler@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr,
75311133SJesse.Butler@Sun.COM NULL, "PortID 0x%x: PHY 0x%x: "
75411133SJesse.Butler@Sun.COM "unable to promote phy", portid,
75511133SJesse.Butler@Sun.COM phynum);
75611133SJesse.Butler@Sun.COM }
75711133SJesse.Butler@Sun.COM }
75811133SJesse.Butler@Sun.COM
75911133SJesse.Butler@Sun.COM /*
76010696SDavid.Hollister@Sun.COM * Drop port width on the primary phy handle
76110696SDavid.Hollister@Sun.COM * No need to lock the entire tree for this
76210696SDavid.Hollister@Sun.COM */
76310696SDavid.Hollister@Sun.COM mutex_enter(&pptr->phy_lock);
76411307SDavid.Hollister@Sun.COM pmcs_update_phy_pm_props(pptr, subphy->att_port_pm_tmp,
76511307SDavid.Hollister@Sun.COM subphy->tgt_port_pm_tmp, B_FALSE);
76610696SDavid.Hollister@Sun.COM pptr->width = IOP_EVENT_NPIP(w3);
76710696SDavid.Hollister@Sun.COM mutex_exit(&pptr->phy_lock);
76810696SDavid.Hollister@Sun.COM
76911347SRamana.Srikanth@Sun.COM /* Clear the iport reference and portid on the subphy */
77010696SDavid.Hollister@Sun.COM mutex_enter(&subphy->phy_lock);
77110696SDavid.Hollister@Sun.COM iport = subphy->iport;
77210696SDavid.Hollister@Sun.COM subphy->iport = NULL;
77311347SRamana.Srikanth@Sun.COM subphy->portid = PMCS_PHY_INVALID_PORT_ID;
77412462Sjesse.butler@oracle.com subphy->pend_dtype = NOTHING;
77511347SRamana.Srikanth@Sun.COM subphy->dtype = NOTHING;
77610696SDavid.Hollister@Sun.COM mutex_exit(&subphy->phy_lock);
77710696SDavid.Hollister@Sun.COM
77810696SDavid.Hollister@Sun.COM /*
77910696SDavid.Hollister@Sun.COM * If the iport was set on this phy, decrement its
78010696SDavid.Hollister@Sun.COM * nphy count and remove this phy from the phys list.
78110696SDavid.Hollister@Sun.COM */
78210696SDavid.Hollister@Sun.COM if (iport) {
78310696SDavid.Hollister@Sun.COM mutex_enter(&iport->lock);
78411267SJesse.Butler@Sun.COM if (iport->ua_state == UA_ACTIVE) {
78511267SJesse.Butler@Sun.COM pmcs_remove_phy_from_iport(iport,
78611267SJesse.Butler@Sun.COM subphy);
78711267SJesse.Butler@Sun.COM }
78810696SDavid.Hollister@Sun.COM mutex_exit(&iport->lock);
78910696SDavid.Hollister@Sun.COM }
79010696SDavid.Hollister@Sun.COM
79110696SDavid.Hollister@Sun.COM pmcs_lock_phy(subphy);
79211601SDavid.Hollister@Sun.COM wwn = pmcs_barray2wwn(pptr->sas_address);
79310696SDavid.Hollister@Sun.COM if (subphy->subsidiary)
79410696SDavid.Hollister@Sun.COM pmcs_clear_phy(pwp, subphy);
79510696SDavid.Hollister@Sun.COM pmcs_unlock_phy(subphy);
79610696SDavid.Hollister@Sun.COM
79710696SDavid.Hollister@Sun.COM /* Remove this PHY from the phymap */
79810696SDavid.Hollister@Sun.COM if (sas_phymap_phy_rem(pwp->hss_phymap, phynum) !=
79910696SDavid.Hollister@Sun.COM DDI_SUCCESS) {
80011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
80110696SDavid.Hollister@Sun.COM "Unable to remove phy %u for 0x%" PRIx64
80210696SDavid.Hollister@Sun.COM ".0x%" PRIx64, phynum,
80311601SDavid.Hollister@Sun.COM pwp->sas_wwns[pptr->phynum], wwn);
80410696SDavid.Hollister@Sun.COM }
80510696SDavid.Hollister@Sun.COM
80611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, pptr, NULL,
80711048SDavid.Hollister@Sun.COM "PortID 0x%x: PHY 0x%x LINK DOWN NARROWS PORT "
80811048SDavid.Hollister@Sun.COM "TO %d PHYS", portid, phynum, pptr->width);
80910696SDavid.Hollister@Sun.COM break;
81010696SDavid.Hollister@Sun.COM }
81110696SDavid.Hollister@Sun.COM if (IOP_EVENT_PORT_STATE(w3) != IOP_EVENT_PS_INVALID) {
81211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
81311048SDavid.Hollister@Sun.COM "PortID 0x%x: PHY 0x%x LINK DOWN NOT HANDLED "
81411048SDavid.Hollister@Sun.COM "(state 0x%x)", portid, phynum,
81511048SDavid.Hollister@Sun.COM IOP_EVENT_PORT_STATE(w3));
81610696SDavid.Hollister@Sun.COM need_ack = 1;
81710696SDavid.Hollister@Sun.COM break;
81810696SDavid.Hollister@Sun.COM }
81910696SDavid.Hollister@Sun.COM /* Remove this PHY from the phymap */
82010696SDavid.Hollister@Sun.COM if (sas_phymap_phy_rem(pwp->hss_phymap, phynum) !=
82110696SDavid.Hollister@Sun.COM DDI_SUCCESS) {
82211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
82310696SDavid.Hollister@Sun.COM "Unable to remove phy %u for 0x%" PRIx64
82410696SDavid.Hollister@Sun.COM ".0x%" PRIx64, phynum,
82511601SDavid.Hollister@Sun.COM pwp->sas_wwns[pptr->phynum], wwn);
82610696SDavid.Hollister@Sun.COM }
82710696SDavid.Hollister@Sun.COM
82811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
82910696SDavid.Hollister@Sun.COM "PortID 0x%x: PHY 0x%x LINK DOWN (port invalid)",
83010696SDavid.Hollister@Sun.COM portid, phynum);
83110696SDavid.Hollister@Sun.COM
83210696SDavid.Hollister@Sun.COM /*
83310696SDavid.Hollister@Sun.COM * Last PHY on the port.
83411307SDavid.Hollister@Sun.COM * Assumption: pptr and subphy are both "valid". In fact,
83511307SDavid.Hollister@Sun.COM * they should be one and the same.
83610696SDavid.Hollister@Sun.COM *
83710696SDavid.Hollister@Sun.COM * Drop port width on the primary phy handle
83811307SDavid.Hollister@Sun.COM * Report the event and clear its PHY pm props while we've
83911307SDavid.Hollister@Sun.COM * got the lock
84010696SDavid.Hollister@Sun.COM */
84111307SDavid.Hollister@Sun.COM ASSERT(pptr == subphy);
84210696SDavid.Hollister@Sun.COM mutex_enter(&pptr->phy_lock);
84310696SDavid.Hollister@Sun.COM pptr->width = 0;
84411307SDavid.Hollister@Sun.COM pmcs_update_phy_pm_props(pptr, pptr->att_port_pm_tmp,
84511307SDavid.Hollister@Sun.COM pptr->tgt_port_pm_tmp, B_FALSE);
84610696SDavid.Hollister@Sun.COM pmcs_smhba_log_sysevent(pwp, ESC_SAS_PHY_EVENT,
84710696SDavid.Hollister@Sun.COM SAS_PHY_OFFLINE, pptr);
84810696SDavid.Hollister@Sun.COM mutex_exit(&pptr->phy_lock);
84910696SDavid.Hollister@Sun.COM
85011347SRamana.Srikanth@Sun.COM /* Clear the iport reference and portid on the subphy */
85111347SRamana.Srikanth@Sun.COM pmcs_lock_phy(subphy);
85210696SDavid.Hollister@Sun.COM iport = subphy->iport;
85311347SRamana.Srikanth@Sun.COM subphy->deregister_wait = 1;
85410696SDavid.Hollister@Sun.COM subphy->iport = NULL;
85511347SRamana.Srikanth@Sun.COM subphy->portid = PMCS_PHY_INVALID_PORT_ID;
85612462Sjesse.butler@oracle.com subphy->pend_dtype = NOTHING;
85711347SRamana.Srikanth@Sun.COM subphy->dtype = NOTHING;
85811347SRamana.Srikanth@Sun.COM pmcs_unlock_phy(subphy);
85911347SRamana.Srikanth@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_DEREGISTER_DEV);
86011347SRamana.Srikanth@Sun.COM (void) ddi_taskq_dispatch(pwp->tq, pmcs_worker,
86111347SRamana.Srikanth@Sun.COM pwp, DDI_NOSLEEP);
86210696SDavid.Hollister@Sun.COM
86310696SDavid.Hollister@Sun.COM /*
86410696SDavid.Hollister@Sun.COM * If the iport was set on this phy, decrement its
86510696SDavid.Hollister@Sun.COM * nphy count and remove this phy from the phys list.
86610696SDavid.Hollister@Sun.COM * Also, clear the iport's pptr as this port is now
86710696SDavid.Hollister@Sun.COM * down.
86810696SDavid.Hollister@Sun.COM */
86910696SDavid.Hollister@Sun.COM if (iport) {
87010696SDavid.Hollister@Sun.COM mutex_enter(&iport->lock);
87111267SJesse.Butler@Sun.COM if (iport->ua_state == UA_ACTIVE) {
87211267SJesse.Butler@Sun.COM pmcs_remove_phy_from_iport(iport, subphy);
87311267SJesse.Butler@Sun.COM iport->pptr = NULL;
87411267SJesse.Butler@Sun.COM iport->ua_state = UA_PEND_DEACTIVATE;
87511267SJesse.Butler@Sun.COM }
87610696SDavid.Hollister@Sun.COM mutex_exit(&iport->lock);
87710696SDavid.Hollister@Sun.COM }
87810696SDavid.Hollister@Sun.COM
87910696SDavid.Hollister@Sun.COM pmcs_lock_phy(subphy);
88010696SDavid.Hollister@Sun.COM if (subphy->subsidiary)
88110696SDavid.Hollister@Sun.COM pmcs_clear_phy(pwp, subphy);
88210696SDavid.Hollister@Sun.COM pmcs_unlock_phy(subphy);
88310696SDavid.Hollister@Sun.COM
88410696SDavid.Hollister@Sun.COM /*
88510696SDavid.Hollister@Sun.COM * Since we're now really dead, it's time to clean up.
88610696SDavid.Hollister@Sun.COM */
88710696SDavid.Hollister@Sun.COM pmcs_kill_port(pwp, portid);
88810696SDavid.Hollister@Sun.COM need_ack = 1;
88910696SDavid.Hollister@Sun.COM
89010696SDavid.Hollister@Sun.COM break;
89111601SDavid.Hollister@Sun.COM }
89210696SDavid.Hollister@Sun.COM case IOP_EVENT_BROADCAST_CHANGE:
89311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
89410696SDavid.Hollister@Sun.COM "PortID 0x%x: PHY 0x%x Broadcast Change", portid, phynum);
89510696SDavid.Hollister@Sun.COM need_ack = 1;
89610696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
89710696SDavid.Hollister@Sun.COM pptr = pwp->ports[portid];
89810696SDavid.Hollister@Sun.COM if (pptr) {
89910696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
90010696SDavid.Hollister@Sun.COM if (pptr->phynum == phynum) {
90110696SDavid.Hollister@Sun.COM pmcs_set_changed(pwp, pptr, B_TRUE, 0);
90210696SDavid.Hollister@Sun.COM }
90310696SDavid.Hollister@Sun.COM pmcs_smhba_log_sysevent(pwp, ESC_SAS_HBA_PORT_BROADCAST,
90410696SDavid.Hollister@Sun.COM SAS_PORT_BROADCAST_CHANGE, pptr);
90510696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
90610696SDavid.Hollister@Sun.COM }
90710696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
90810696SDavid.Hollister@Sun.COM RESTART_DISCOVERY(pwp);
90910696SDavid.Hollister@Sun.COM break;
91010696SDavid.Hollister@Sun.COM case IOP_EVENT_BROADCAST_SES:
91110696SDavid.Hollister@Sun.COM EVT_PRT(pwp, IOP_EVENT_BROADCAST_SES, phynum);
91210696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
91310696SDavid.Hollister@Sun.COM pptr = pwp->ports[portid];
91410696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
91510696SDavid.Hollister@Sun.COM if (pptr) {
91610696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
91710696SDavid.Hollister@Sun.COM pmcs_smhba_log_sysevent(pwp, ESC_SAS_HBA_PORT_BROADCAST,
91810696SDavid.Hollister@Sun.COM SAS_PORT_BROADCAST_SES, pptr);
91910696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
92010696SDavid.Hollister@Sun.COM }
92110696SDavid.Hollister@Sun.COM break;
92210696SDavid.Hollister@Sun.COM case IOP_EVENT_PHY_ERR_INBOUND_CRC:
92310696SDavid.Hollister@Sun.COM {
92410696SDavid.Hollister@Sun.COM char buf[32];
92510696SDavid.Hollister@Sun.COM (void) snprintf(buf, sizeof (buf), "Inbound PHY CRC error");
92610696SDavid.Hollister@Sun.COM need_ack = 1;
92710696SDavid.Hollister@Sun.COM break;
92810696SDavid.Hollister@Sun.COM }
92910696SDavid.Hollister@Sun.COM case IOP_EVENT_HARD_RESET_RECEIVED:
93010696SDavid.Hollister@Sun.COM EVT_PRT(pwp, IOP_EVENT_HARD_RESET_RECEIVED, phynum);
93110696SDavid.Hollister@Sun.COM break;
93210696SDavid.Hollister@Sun.COM case IOP_EVENT_EVENT_ID_FRAME_TIMO:
93310696SDavid.Hollister@Sun.COM EVT_PRT(pwp, IOP_EVENT_EVENT_ID_FRAME_TIMO, phynum);
93410696SDavid.Hollister@Sun.COM break;
93510696SDavid.Hollister@Sun.COM case IOP_EVENT_BROADCAST_EXP:
93611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
93710696SDavid.Hollister@Sun.COM "PortID 0x%x: PHY 0x%x Broadcast Exp Change",
93810696SDavid.Hollister@Sun.COM portid, phynum);
93910696SDavid.Hollister@Sun.COM /*
94010696SDavid.Hollister@Sun.COM * Comparing Section 6.8.1.4 of SMHBA (rev 7) spec and Section
94110696SDavid.Hollister@Sun.COM * 7.2.3 of SAS2 (Rev 15) spec,
94210696SDavid.Hollister@Sun.COM * _BROADCAST_EXPANDER event corresponds to _D01_4 primitive
94310696SDavid.Hollister@Sun.COM */
94410696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
94510696SDavid.Hollister@Sun.COM pptr = pwp->ports[portid];
94610696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
94710696SDavid.Hollister@Sun.COM if (pptr) {
94810696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
94910696SDavid.Hollister@Sun.COM pmcs_smhba_log_sysevent(pwp, ESC_SAS_HBA_PORT_BROADCAST,
95010696SDavid.Hollister@Sun.COM SAS_PORT_BROADCAST_D01_4, pptr);
95110696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
95210696SDavid.Hollister@Sun.COM }
95310696SDavid.Hollister@Sun.COM break;
95410696SDavid.Hollister@Sun.COM case IOP_EVENT_PHY_START_STATUS:
95510696SDavid.Hollister@Sun.COM switch (IOP_EVENT_STATUS(w1)) {
95610696SDavid.Hollister@Sun.COM case IOP_PHY_START_OK:
95711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
95811048SDavid.Hollister@Sun.COM "PHY 0x%x Started", phynum);
95910696SDavid.Hollister@Sun.COM break;
96010696SDavid.Hollister@Sun.COM case IOP_PHY_START_ALREADY:
96111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
96210696SDavid.Hollister@Sun.COM "PHY 0x%x Started (Already)", phynum);
96310696SDavid.Hollister@Sun.COM break;
96410696SDavid.Hollister@Sun.COM case IOP_PHY_START_INVALID:
96511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
96610696SDavid.Hollister@Sun.COM "PHY 0x%x failed to start (invalid phy)", phynum);
96710696SDavid.Hollister@Sun.COM break;
96810696SDavid.Hollister@Sun.COM case IOP_PHY_START_ERROR:
96911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
97010696SDavid.Hollister@Sun.COM "PHY 0x%x Start Error", phynum);
97110696SDavid.Hollister@Sun.COM break;
97210696SDavid.Hollister@Sun.COM default:
97311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
97411048SDavid.Hollister@Sun.COM "PHY 0x%x failed to start (0x%x)", phynum,
97511048SDavid.Hollister@Sun.COM IOP_EVENT_STATUS(w1));
97610696SDavid.Hollister@Sun.COM break;
97710696SDavid.Hollister@Sun.COM }
97810696SDavid.Hollister@Sun.COM /* Reposition htag to the 'expected' position. */
97910696SDavid.Hollister@Sun.COM ((uint32_t *)iomb)[1] = ((uint32_t *)iomb)[2];
98010696SDavid.Hollister@Sun.COM pmcs_process_completion(pwp, iomb, amt);
98110696SDavid.Hollister@Sun.COM break;
98210696SDavid.Hollister@Sun.COM case IOP_EVENT_PHY_ERR_INVALID_DWORD:
98310696SDavid.Hollister@Sun.COM need_ack = 1;
98410696SDavid.Hollister@Sun.COM EVT_PRT(pwp, IOP_EVENT_PHY_ERR_INVALID_DWORD, phynum);
98510696SDavid.Hollister@Sun.COM break;
98610696SDavid.Hollister@Sun.COM case IOP_EVENT_PHY_ERR_DISPARITY_ERROR:
98710696SDavid.Hollister@Sun.COM need_ack = 1;
98810696SDavid.Hollister@Sun.COM EVT_PRT(pwp, IOP_EVENT_PHY_ERR_DISPARITY_ERROR, phynum);
98910696SDavid.Hollister@Sun.COM break;
99010696SDavid.Hollister@Sun.COM case IOP_EVENT_PHY_ERR_CODE_VIOLATION:
99110696SDavid.Hollister@Sun.COM need_ack = 1;
99210696SDavid.Hollister@Sun.COM EVT_PRT(pwp, IOP_EVENT_PHY_ERR_CODE_VIOLATION, phynum);
99310696SDavid.Hollister@Sun.COM break;
99410696SDavid.Hollister@Sun.COM case IOP_EVENT_PHY_ERR_LOSS_OF_DWORD_SYN:
99510696SDavid.Hollister@Sun.COM need_ack = 1;
99610696SDavid.Hollister@Sun.COM EVT_PRT(pwp, IOP_EVENT_PHY_ERR_LOSS_OF_DWORD_SYN, phynum);
99710696SDavid.Hollister@Sun.COM break;
99810696SDavid.Hollister@Sun.COM case IOP_EVENT_PHY_ERR_PHY_RESET_FAILD:
99910696SDavid.Hollister@Sun.COM need_ack = 1;
100010696SDavid.Hollister@Sun.COM EVT_PRT(pwp, IOP_EVENT_PHY_ERR_PHY_RESET_FAILD, phynum);
100110696SDavid.Hollister@Sun.COM break;
100210696SDavid.Hollister@Sun.COM case IOP_EVENT_PORT_RECOVERY_TIMER_TMO:
100310696SDavid.Hollister@Sun.COM EVT_PRT(pwp, IOP_EVENT_PORT_RECOVERY_TIMER_TMO, phynum);
100410696SDavid.Hollister@Sun.COM break;
100510696SDavid.Hollister@Sun.COM case IOP_EVENT_PORT_RECOVER:
100610696SDavid.Hollister@Sun.COM EVT_PRT(pwp, IOP_EVENT_PORT_RECOVER, phynum);
100710696SDavid.Hollister@Sun.COM break;
100810696SDavid.Hollister@Sun.COM case IOP_EVENT_PORT_INVALID:
100910696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
101010696SDavid.Hollister@Sun.COM if (pwp->state != STATE_RUNNING) {
101110696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
101210696SDavid.Hollister@Sun.COM break;
101310696SDavid.Hollister@Sun.COM }
101410696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
101510696SDavid.Hollister@Sun.COM pmcs_kill_port(pwp, portid);
101611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
101711048SDavid.Hollister@Sun.COM "PortID 0x%x: PORT Now Invalid", portid);
101810696SDavid.Hollister@Sun.COM break;
101910696SDavid.Hollister@Sun.COM case IOP_EVENT_PORT_RESET_TIMER_TMO:
102010696SDavid.Hollister@Sun.COM EVT_PRT(pwp, IOP_EVENT_PORT_RESET_TIMER_TMO, phynum);
102110696SDavid.Hollister@Sun.COM break;
102210696SDavid.Hollister@Sun.COM case IOP_EVENT_PORT_RESET_COMPLETE:
102310696SDavid.Hollister@Sun.COM EVT_PRT(pwp, IOP_EVENT_PORT_RESET_COMPLETE, phynum);
102410696SDavid.Hollister@Sun.COM break;
102510696SDavid.Hollister@Sun.COM case IOP_EVENT_BROADCAST_ASYNC_EVENT:
102610696SDavid.Hollister@Sun.COM EVT_PRT(pwp, IOP_EVENT_BROADCAST_ASYNC_EVENT, phynum);
102710696SDavid.Hollister@Sun.COM /*
102810696SDavid.Hollister@Sun.COM * Comparing Section 6.8.1.4 of SMHBA (rev 7) spec and Section
102910696SDavid.Hollister@Sun.COM * 7.2.3 of SAS2 (Rev 15) spec,
103010696SDavid.Hollister@Sun.COM * _BROADCAST_ASYNC event corresponds to _D04_7 primitive
103110696SDavid.Hollister@Sun.COM */
103210696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
103310696SDavid.Hollister@Sun.COM pptr = pwp->ports[portid];
103410696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
103510696SDavid.Hollister@Sun.COM if (pptr) {
103610696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
103710696SDavid.Hollister@Sun.COM pmcs_smhba_log_sysevent(pwp, ESC_SAS_HBA_PORT_BROADCAST,
103810696SDavid.Hollister@Sun.COM SAS_PORT_BROADCAST_D04_7, pptr);
103910696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
104010696SDavid.Hollister@Sun.COM }
104110696SDavid.Hollister@Sun.COM break;
104210696SDavid.Hollister@Sun.COM default:
104310696SDavid.Hollister@Sun.COM (void) snprintf(buf, sizeof (buf),
104410696SDavid.Hollister@Sun.COM "unknown SAS H/W Event PHY 0x%x", phynum);
104510696SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG, buf, iomb);
104610696SDavid.Hollister@Sun.COM break;
104710696SDavid.Hollister@Sun.COM }
104810696SDavid.Hollister@Sun.COM if (need_ack) {
104910696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
105010696SDavid.Hollister@Sun.COM /*
105110696SDavid.Hollister@Sun.COM * Don't lock the entire tree for this. Just grab the mutex
105210696SDavid.Hollister@Sun.COM * on the root PHY.
105310696SDavid.Hollister@Sun.COM */
105410696SDavid.Hollister@Sun.COM tphyp = pwp->root_phys + phynum;
105510696SDavid.Hollister@Sun.COM mutex_enter(&tphyp->phy_lock);
105610696SDavid.Hollister@Sun.COM tphyp->hw_event_ack = w1;
105710696SDavid.Hollister@Sun.COM mutex_exit(&tphyp->phy_lock);
105810696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
105910696SDavid.Hollister@Sun.COM pmcs_ack_events(pwp);
106010696SDavid.Hollister@Sun.COM }
106110696SDavid.Hollister@Sun.COM }
106210696SDavid.Hollister@Sun.COM
106310696SDavid.Hollister@Sun.COM static void
pmcs_process_echo_completion(pmcs_hw_t * pwp,void * iomb,size_t amt)106410696SDavid.Hollister@Sun.COM pmcs_process_echo_completion(pmcs_hw_t *pwp, void *iomb, size_t amt)
106510696SDavid.Hollister@Sun.COM {
106610696SDavid.Hollister@Sun.COM echo_test_t fred;
106710696SDavid.Hollister@Sun.COM pmcwork_t *pwrk;
106810696SDavid.Hollister@Sun.COM uint32_t *msg = iomb, htag = LE_32(msg[1]);
106912258Ssrikanth.suravajhala@oracle.com pwrk = pmcs_tag2wp(pwp, htag, B_FALSE);
107010696SDavid.Hollister@Sun.COM if (pwrk) {
107110696SDavid.Hollister@Sun.COM (void) memcpy(&fred, &((uint32_t *)iomb)[2], sizeof (fred));
107210696SDavid.Hollister@Sun.COM fred.ptr[0]++;
107310696SDavid.Hollister@Sun.COM msg[2] = LE_32(PMCOUT_STATUS_OK);
107410696SDavid.Hollister@Sun.COM pmcs_complete_work(pwp, pwrk, msg, amt);
107510696SDavid.Hollister@Sun.COM } else {
107610696SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
107710696SDavid.Hollister@Sun.COM "ECHO completion with no work structure", iomb);
107810696SDavid.Hollister@Sun.COM }
107910696SDavid.Hollister@Sun.COM }
108010696SDavid.Hollister@Sun.COM
108110696SDavid.Hollister@Sun.COM static void
pmcs_process_ssp_event(pmcs_hw_t * pwp,void * iomb,size_t amt)108210696SDavid.Hollister@Sun.COM pmcs_process_ssp_event(pmcs_hw_t *pwp, void *iomb, size_t amt)
108310696SDavid.Hollister@Sun.COM {
108410696SDavid.Hollister@Sun.COM _NOTE(ARGUNUSED(amt));
108510696SDavid.Hollister@Sun.COM uint32_t status, htag, *w;
108610696SDavid.Hollister@Sun.COM pmcwork_t *pwrk;
108711048SDavid.Hollister@Sun.COM pmcs_phy_t *phyp = NULL;
108810696SDavid.Hollister@Sun.COM char *path;
108910696SDavid.Hollister@Sun.COM
109010696SDavid.Hollister@Sun.COM w = iomb;
109110696SDavid.Hollister@Sun.COM htag = LE_32(w[1]);
109210696SDavid.Hollister@Sun.COM status = LE_32(w[2]);
109310696SDavid.Hollister@Sun.COM
109410696SDavid.Hollister@Sun.COM
109512258Ssrikanth.suravajhala@oracle.com pwrk = pmcs_tag2wp(pwp, htag, B_FALSE);
109610696SDavid.Hollister@Sun.COM if (pwrk == NULL) {
109710696SDavid.Hollister@Sun.COM path = "????";
109810696SDavid.Hollister@Sun.COM } else {
109911048SDavid.Hollister@Sun.COM phyp = pwrk->phy;
110010696SDavid.Hollister@Sun.COM path = pwrk->phy->path;
110110696SDavid.Hollister@Sun.COM }
110210696SDavid.Hollister@Sun.COM
110310696SDavid.Hollister@Sun.COM if (status != PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED) {
110410696SDavid.Hollister@Sun.COM char buf[20];
110510696SDavid.Hollister@Sun.COM const char *emsg = pmcs_status_str(status);
110610696SDavid.Hollister@Sun.COM
110710696SDavid.Hollister@Sun.COM if (emsg == NULL) {
110810696SDavid.Hollister@Sun.COM (void) snprintf(buf, sizeof (buf), "Status 0x%x",
110910696SDavid.Hollister@Sun.COM status);
111010696SDavid.Hollister@Sun.COM emsg = buf;
111110696SDavid.Hollister@Sun.COM }
111211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL, "%s: Bad SAS Status "
111311048SDavid.Hollister@Sun.COM "(tag 0x%x) %s on %s", __func__, htag, emsg, path);
111410696SDavid.Hollister@Sun.COM if (pwrk != NULL) {
111510696SDavid.Hollister@Sun.COM /*
111610696SDavid.Hollister@Sun.COM * There may be pending command on a target device.
111710696SDavid.Hollister@Sun.COM * Or, it may be a double fault.
111810696SDavid.Hollister@Sun.COM */
111910696SDavid.Hollister@Sun.COM pmcs_start_ssp_event_recovery(pwp, pwrk, iomb, amt);
112010696SDavid.Hollister@Sun.COM }
112110696SDavid.Hollister@Sun.COM } else {
112211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, phyp, NULL,
112310696SDavid.Hollister@Sun.COM "%s: tag %x put onto the wire for %s",
112410696SDavid.Hollister@Sun.COM __func__, htag, path);
112510696SDavid.Hollister@Sun.COM if (pwrk) {
112610696SDavid.Hollister@Sun.COM pwrk->onwire = 1;
112710696SDavid.Hollister@Sun.COM mutex_exit(&pwrk->lock);
112810696SDavid.Hollister@Sun.COM }
112910696SDavid.Hollister@Sun.COM }
113010696SDavid.Hollister@Sun.COM }
113110696SDavid.Hollister@Sun.COM
113210696SDavid.Hollister@Sun.COM static void
pmcs_process_sata_event(pmcs_hw_t * pwp,void * iomb,size_t amt)113310696SDavid.Hollister@Sun.COM pmcs_process_sata_event(pmcs_hw_t *pwp, void *iomb, size_t amt)
113410696SDavid.Hollister@Sun.COM {
113510696SDavid.Hollister@Sun.COM _NOTE(ARGUNUSED(amt));
113610696SDavid.Hollister@Sun.COM pmcwork_t *pwrk = NULL;
113712385Ssrikanth.suravajhala@oracle.com pmcs_phy_t *pptr = NULL;
113810696SDavid.Hollister@Sun.COM uint32_t status, htag, *w;
113912385Ssrikanth.suravajhala@oracle.com char *path = NULL;
114010696SDavid.Hollister@Sun.COM
114110696SDavid.Hollister@Sun.COM w = iomb;
114210696SDavid.Hollister@Sun.COM htag = LE_32(w[1]);
114310696SDavid.Hollister@Sun.COM status = LE_32(w[2]);
114410696SDavid.Hollister@Sun.COM
114510696SDavid.Hollister@Sun.COM /*
114610696SDavid.Hollister@Sun.COM * If the status is PMCOUT_STATUS_XFER_ERROR_ABORTED_NCQ_MODE,
114710696SDavid.Hollister@Sun.COM * we have to issue a READ LOG EXT ATA (page 0x10) command
114810696SDavid.Hollister@Sun.COM * to the device. In this case, htag is not valid.
114910696SDavid.Hollister@Sun.COM *
115010696SDavid.Hollister@Sun.COM * If the status is PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED, we're
115110696SDavid.Hollister@Sun.COM * just noting that an I/O got put onto the wire.
115210696SDavid.Hollister@Sun.COM *
115310696SDavid.Hollister@Sun.COM * Othewise, other errors are indicative that things need to
115410696SDavid.Hollister@Sun.COM * be aborted.
115510696SDavid.Hollister@Sun.COM */
115610696SDavid.Hollister@Sun.COM if (htag) {
115712258Ssrikanth.suravajhala@oracle.com pwrk = pmcs_tag2wp(pwp, htag, B_TRUE);
115810696SDavid.Hollister@Sun.COM }
115912385Ssrikanth.suravajhala@oracle.com
116012385Ssrikanth.suravajhala@oracle.com if (pwrk) {
116112385Ssrikanth.suravajhala@oracle.com pptr = pwrk->phy;
116212385Ssrikanth.suravajhala@oracle.com path = pptr->path;
116312385Ssrikanth.suravajhala@oracle.com } else {
116412385Ssrikanth.suravajhala@oracle.com pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: "
116512385Ssrikanth.suravajhala@oracle.com "cannot find work structure for SATA completion", __func__);
116612385Ssrikanth.suravajhala@oracle.com return;
116712385Ssrikanth.suravajhala@oracle.com }
116812385Ssrikanth.suravajhala@oracle.com
116910696SDavid.Hollister@Sun.COM if (status != PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED) {
117010696SDavid.Hollister@Sun.COM char buf[20];
117110696SDavid.Hollister@Sun.COM const char *emsg = pmcs_status_str(status);
117210696SDavid.Hollister@Sun.COM
117310696SDavid.Hollister@Sun.COM if (emsg == NULL) {
117410696SDavid.Hollister@Sun.COM (void) snprintf(buf, sizeof (buf), "Status 0x%x",
117510696SDavid.Hollister@Sun.COM status);
117610696SDavid.Hollister@Sun.COM emsg = buf;
117710696SDavid.Hollister@Sun.COM }
117810696SDavid.Hollister@Sun.COM if (status == PMCOUT_STATUS_XFER_ERROR_ABORTED_NCQ_MODE) {
117910696SDavid.Hollister@Sun.COM pptr->need_rl_ext = 1;
118010696SDavid.Hollister@Sun.COM htag = 0;
118110696SDavid.Hollister@Sun.COM } else {
118210696SDavid.Hollister@Sun.COM pptr->abort_pending = 1;
118310696SDavid.Hollister@Sun.COM }
118411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
118511048SDavid.Hollister@Sun.COM "%s: Bad SATA Status (tag 0x%x) %s on %s",
118611048SDavid.Hollister@Sun.COM __func__, htag, emsg, path);
118710696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
118810696SDavid.Hollister@Sun.COM /*
118910696SDavid.Hollister@Sun.COM * Unlike SSP devices, we let the abort we
119010696SDavid.Hollister@Sun.COM * schedule above force the completion of
119110696SDavid.Hollister@Sun.COM * problem commands.
119210696SDavid.Hollister@Sun.COM */
119312385Ssrikanth.suravajhala@oracle.com mutex_exit(&pwrk->lock);
1194*12807Ssrikanth.suravajhala@oracle.com } else {
119511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, NULL,
119610696SDavid.Hollister@Sun.COM "%s: tag %x put onto the wire for %s",
119710696SDavid.Hollister@Sun.COM __func__, htag, path);
119812385Ssrikanth.suravajhala@oracle.com pwrk->onwire = 1;
119912385Ssrikanth.suravajhala@oracle.com mutex_exit(&pwrk->lock);
120010696SDavid.Hollister@Sun.COM }
120110696SDavid.Hollister@Sun.COM
120212385Ssrikanth.suravajhala@oracle.com mutex_exit(&pptr->phy_lock);
120310696SDavid.Hollister@Sun.COM }
120410696SDavid.Hollister@Sun.COM
120510696SDavid.Hollister@Sun.COM static void
pmcs_process_abort_completion(pmcs_hw_t * pwp,void * iomb,size_t amt)120610696SDavid.Hollister@Sun.COM pmcs_process_abort_completion(pmcs_hw_t *pwp, void *iomb, size_t amt)
120710696SDavid.Hollister@Sun.COM {
120810696SDavid.Hollister@Sun.COM pmcs_phy_t *pptr;
120910696SDavid.Hollister@Sun.COM struct pmcwork *pwrk;
121010696SDavid.Hollister@Sun.COM uint32_t htag = LE_32(((uint32_t *)iomb)[1]);
121110696SDavid.Hollister@Sun.COM uint32_t status = LE_32(((uint32_t *)iomb)[2]);
121212462Sjesse.butler@oracle.com uint32_t scope = LE_32(((uint32_t *)iomb)[3]) & 0x1;
121310696SDavid.Hollister@Sun.COM char *path;
121410696SDavid.Hollister@Sun.COM
121512258Ssrikanth.suravajhala@oracle.com pwrk = pmcs_tag2wp(pwp, htag, B_TRUE);
121610696SDavid.Hollister@Sun.COM if (pwrk == NULL) {
121711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
121810696SDavid.Hollister@Sun.COM "%s: cannot find work structure for ABORT", __func__);
121910696SDavid.Hollister@Sun.COM return;
122010696SDavid.Hollister@Sun.COM }
122110696SDavid.Hollister@Sun.COM
122210696SDavid.Hollister@Sun.COM pptr = pwrk->phy;
122310696SDavid.Hollister@Sun.COM if (pptr) {
122410696SDavid.Hollister@Sun.COM pptr->abort_pending = 0;
122510696SDavid.Hollister@Sun.COM pptr->abort_sent = 0;
122610696SDavid.Hollister@Sun.COM
122710696SDavid.Hollister@Sun.COM /*
122810696SDavid.Hollister@Sun.COM * Don't do this if the status was ABORT_IN_PROGRESS and
122910696SDavid.Hollister@Sun.COM * the scope bit was set
123010696SDavid.Hollister@Sun.COM */
123112462Sjesse.butler@oracle.com if ((status != PMCOUT_STATUS_IO_ABORT_IN_PROGRESS) || !scope) {
123210696SDavid.Hollister@Sun.COM pptr->abort_all_start = 0;
123310696SDavid.Hollister@Sun.COM cv_signal(&pptr->abort_all_cv);
123410696SDavid.Hollister@Sun.COM }
123510696SDavid.Hollister@Sun.COM path = pptr->path;
123611847SDavid.Hollister@Sun.COM mutex_exit(&pptr->phy_lock);
123710696SDavid.Hollister@Sun.COM } else {
123810696SDavid.Hollister@Sun.COM path = "(no phy)";
123910696SDavid.Hollister@Sun.COM }
124010696SDavid.Hollister@Sun.COM
124110696SDavid.Hollister@Sun.COM switch (status) {
124210696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OK:
124312462Sjesse.butler@oracle.com if (scope) {
124411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
124510696SDavid.Hollister@Sun.COM "%s: abort all succeeded for %s. (htag=0x%x)",
124610696SDavid.Hollister@Sun.COM __func__, path, htag);
124710696SDavid.Hollister@Sun.COM } else {
124811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
124910696SDavid.Hollister@Sun.COM "%s: abort tag 0x%x succeeded for %s. (htag=0x%x)",
125011048SDavid.Hollister@Sun.COM __func__, pwrk->abt_htag, path, htag);
125110696SDavid.Hollister@Sun.COM }
125210696SDavid.Hollister@Sun.COM break;
125310696SDavid.Hollister@Sun.COM
125410696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_NOT_VALID:
125512462Sjesse.butler@oracle.com if (scope) {
125611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
125710696SDavid.Hollister@Sun.COM "%s: ABORT %s failed (DEV NOT VALID) for %s. "
125812462Sjesse.butler@oracle.com "(htag=0x%x)", __func__, scope ? "all" : "tag",
125910696SDavid.Hollister@Sun.COM path, htag);
126010696SDavid.Hollister@Sun.COM } else {
126111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
126210696SDavid.Hollister@Sun.COM "%s: ABORT %s failed (I/O NOT VALID) for %s. "
126312462Sjesse.butler@oracle.com "(htag=0x%x)", __func__, scope ? "all" : "tag",
126410696SDavid.Hollister@Sun.COM path, htag);
126510696SDavid.Hollister@Sun.COM }
126610696SDavid.Hollister@Sun.COM break;
126710696SDavid.Hollister@Sun.COM
126810696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_ABORT_IN_PROGRESS:
126911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, "%s: ABORT %s failed "
127011048SDavid.Hollister@Sun.COM "for %s, htag 0x%x (ABORT IN PROGRESS)", __func__,
127112462Sjesse.butler@oracle.com scope ? "all" : "tag", path, htag);
127210696SDavid.Hollister@Sun.COM break;
127310696SDavid.Hollister@Sun.COM
127410696SDavid.Hollister@Sun.COM default:
127511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, "%s: Unknown status "
127611048SDavid.Hollister@Sun.COM "%d for ABORT %s, htag 0x%x, PHY %s", __func__, status,
127712462Sjesse.butler@oracle.com scope ? "all" : "tag", htag, path);
127810696SDavid.Hollister@Sun.COM break;
127910696SDavid.Hollister@Sun.COM }
128010696SDavid.Hollister@Sun.COM
128110696SDavid.Hollister@Sun.COM pmcs_complete_work(pwp, pwrk, iomb, amt);
128210696SDavid.Hollister@Sun.COM }
128310696SDavid.Hollister@Sun.COM
128410696SDavid.Hollister@Sun.COM static void
pmcs_process_general_event(pmcs_hw_t * pwp,uint32_t * iomb)128510696SDavid.Hollister@Sun.COM pmcs_process_general_event(pmcs_hw_t *pwp, uint32_t *iomb)
128610696SDavid.Hollister@Sun.COM {
128710696SDavid.Hollister@Sun.COM uint32_t htag;
128810696SDavid.Hollister@Sun.COM char local[60];
128910696SDavid.Hollister@Sun.COM struct pmcwork *pwrk;
129010696SDavid.Hollister@Sun.COM int i;
129110696SDavid.Hollister@Sun.COM
129210696SDavid.Hollister@Sun.COM if (LE_32(iomb[1]) == INBOUND_IOMB_V_BIT_NOT_SET) {
129310696SDavid.Hollister@Sun.COM (void) snprintf(local, sizeof (local),
129410696SDavid.Hollister@Sun.COM "VALID bit not set on INBOUND IOMB");
129510696SDavid.Hollister@Sun.COM } else if (LE_32(iomb[1]) ==
129610696SDavid.Hollister@Sun.COM INBOUND_IOMB_OPC_NOT_SUPPORTED) {
129710696SDavid.Hollister@Sun.COM (void) snprintf(local, sizeof (local),
129810696SDavid.Hollister@Sun.COM "opcode not set on inbound IOMB");
129910696SDavid.Hollister@Sun.COM } else {
130010696SDavid.Hollister@Sun.COM (void) snprintf(local, sizeof (local),
130110696SDavid.Hollister@Sun.COM "unknown GENERAL EVENT status (0x%x)",
130210696SDavid.Hollister@Sun.COM LE_32(iomb[1]));
130310696SDavid.Hollister@Sun.COM }
130410696SDavid.Hollister@Sun.COM /* Pull up bad IOMB into usual position */
130510696SDavid.Hollister@Sun.COM for (i = 0; i < PMCS_MSG_SIZE - 2; i++) {
130610696SDavid.Hollister@Sun.COM iomb[i] = iomb[i+2];
130710696SDavid.Hollister@Sun.COM }
130810696SDavid.Hollister@Sun.COM /* overwrite status with an error */
130910696SDavid.Hollister@Sun.COM iomb[2] = LE_32(PMCOUT_STATUS_PROG_ERROR);
131010696SDavid.Hollister@Sun.COM iomb[PMCS_MSG_SIZE - 2] = 0;
131110696SDavid.Hollister@Sun.COM iomb[PMCS_MSG_SIZE - 1] = 0;
131210696SDavid.Hollister@Sun.COM htag = LE_32(iomb[1]);
131310696SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG, local, iomb);
131412258Ssrikanth.suravajhala@oracle.com pwrk = pmcs_tag2wp(pwp, htag, B_FALSE);
131510696SDavid.Hollister@Sun.COM if (pwrk) {
131610696SDavid.Hollister@Sun.COM pmcs_complete_work(pwp, pwrk, iomb, PMCS_QENTRY_SIZE);
131710696SDavid.Hollister@Sun.COM }
131810696SDavid.Hollister@Sun.COM }
131910696SDavid.Hollister@Sun.COM
132010696SDavid.Hollister@Sun.COM void
pmcs_general_intr(pmcs_hw_t * pwp)132110696SDavid.Hollister@Sun.COM pmcs_general_intr(pmcs_hw_t *pwp)
132210696SDavid.Hollister@Sun.COM {
132310696SDavid.Hollister@Sun.COM char local[PMCS_QENTRY_SIZE << 1];
132410696SDavid.Hollister@Sun.COM uint32_t w0, pi, ci;
132510696SDavid.Hollister@Sun.COM uint32_t *ptr, nbuf, lim = 0;
132610696SDavid.Hollister@Sun.COM size_t amt;
132710696SDavid.Hollister@Sun.COM
132810696SDavid.Hollister@Sun.COM ci = pmcs_rd_oqci(pwp, PMCS_OQ_GENERAL);
132910696SDavid.Hollister@Sun.COM pi = pmcs_rd_oqpi(pwp, PMCS_OQ_GENERAL);
133010696SDavid.Hollister@Sun.COM
133110696SDavid.Hollister@Sun.COM while (ci != pi) {
133210696SDavid.Hollister@Sun.COM OQLIM_CHECK(pwp, lim);
133310696SDavid.Hollister@Sun.COM ptr = GET_OQ_ENTRY(pwp, PMCS_OQ_GENERAL, ci, 0);
133410696SDavid.Hollister@Sun.COM w0 = LE_32(ptr[0]);
133510696SDavid.Hollister@Sun.COM VALID_IOMB_CHECK(pwp, w0, ptr, ci, pi);
133610696SDavid.Hollister@Sun.COM WRONG_OBID_CHECK(pwp, w0, PMCS_OQ_GENERAL);
133710696SDavid.Hollister@Sun.COM COPY_OUTBOUND(pwp, w0, local, nbuf, amt, ptr,
133810696SDavid.Hollister@Sun.COM PMCS_OQ_GENERAL, ci);
133910696SDavid.Hollister@Sun.COM
134010696SDavid.Hollister@Sun.COM switch (w0 & PMCS_IOMB_OPCODE_MASK) {
134110696SDavid.Hollister@Sun.COM case PMCOUT_SSP_COMPLETION:
134210696SDavid.Hollister@Sun.COM /*
134310696SDavid.Hollister@Sun.COM * We only get SSP completion here for Task Management
134410696SDavid.Hollister@Sun.COM * completions.
134510696SDavid.Hollister@Sun.COM */
134610696SDavid.Hollister@Sun.COM case PMCOUT_SMP_COMPLETION:
134710696SDavid.Hollister@Sun.COM case PMCOUT_LOCAL_PHY_CONTROL:
134810696SDavid.Hollister@Sun.COM case PMCOUT_DEVICE_REGISTRATION:
134910696SDavid.Hollister@Sun.COM case PMCOUT_DEREGISTER_DEVICE_HANDLE:
135010696SDavid.Hollister@Sun.COM case PMCOUT_GET_NVMD_DATA:
135110696SDavid.Hollister@Sun.COM case PMCOUT_SET_NVMD_DATA:
135210696SDavid.Hollister@Sun.COM case PMCOUT_GET_DEVICE_STATE:
135310696SDavid.Hollister@Sun.COM case PMCOUT_SET_DEVICE_STATE:
135410696SDavid.Hollister@Sun.COM pmcs_process_completion(pwp, local, amt);
135510696SDavid.Hollister@Sun.COM break;
135610696SDavid.Hollister@Sun.COM case PMCOUT_SSP_ABORT:
135710696SDavid.Hollister@Sun.COM case PMCOUT_SATA_ABORT:
135810696SDavid.Hollister@Sun.COM case PMCOUT_SMP_ABORT:
135910696SDavid.Hollister@Sun.COM pmcs_process_abort_completion(pwp, local, amt);
136010696SDavid.Hollister@Sun.COM break;
136110696SDavid.Hollister@Sun.COM case PMCOUT_SSP_EVENT:
136210696SDavid.Hollister@Sun.COM pmcs_process_ssp_event(pwp, local, amt);
136310696SDavid.Hollister@Sun.COM break;
136410696SDavid.Hollister@Sun.COM case PMCOUT_ECHO:
136510696SDavid.Hollister@Sun.COM pmcs_process_echo_completion(pwp, local, amt);
136610696SDavid.Hollister@Sun.COM break;
136710696SDavid.Hollister@Sun.COM case PMCOUT_SAS_HW_EVENT_ACK_ACK:
136810696SDavid.Hollister@Sun.COM if (LE_32(ptr[2]) != SAS_HW_EVENT_ACK_OK) {
136911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
137010696SDavid.Hollister@Sun.COM "SAS H/W EVENT ACK/ACK Status=0x%b",
137110696SDavid.Hollister@Sun.COM LE_32(ptr[2]), "\020\4InvParm\3"
137210696SDavid.Hollister@Sun.COM "InvPort\2InvPhy\1InvSEA");
137310696SDavid.Hollister@Sun.COM }
137410696SDavid.Hollister@Sun.COM pmcs_process_completion(pwp, local, amt);
137510696SDavid.Hollister@Sun.COM break;
137610696SDavid.Hollister@Sun.COM case PMCOUT_SKIP_ENTRIES:
137711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL,
137811048SDavid.Hollister@Sun.COM "%s: skip %d entries", __func__, nbuf);
137910696SDavid.Hollister@Sun.COM break;
138010696SDavid.Hollister@Sun.COM default:
138110696SDavid.Hollister@Sun.COM (void) snprintf(local, sizeof (local),
138210696SDavid.Hollister@Sun.COM "%s: unhandled message", __func__);
138310696SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG, local, ptr);
138410696SDavid.Hollister@Sun.COM break;
138510696SDavid.Hollister@Sun.COM }
138610696SDavid.Hollister@Sun.COM STEP_OQ_ENTRY(pwp, PMCS_OQ_GENERAL, ci, nbuf);
138710696SDavid.Hollister@Sun.COM }
138810696SDavid.Hollister@Sun.COM if (lim) {
138910696SDavid.Hollister@Sun.COM SYNC_OQ_ENTRY(pwp, PMCS_OQ_GENERAL, ci, pi);
139010696SDavid.Hollister@Sun.COM }
139110696SDavid.Hollister@Sun.COM }
139210696SDavid.Hollister@Sun.COM
139310696SDavid.Hollister@Sun.COM /*
139410696SDavid.Hollister@Sun.COM * pmcs_check_intr_coal
139510696SDavid.Hollister@Sun.COM *
139610696SDavid.Hollister@Sun.COM * This function makes a determination on the dynamic value of the
139710696SDavid.Hollister@Sun.COM * interrupt coalescing timer register. We only use this for I/O
139810696SDavid.Hollister@Sun.COM * completions.
139910696SDavid.Hollister@Sun.COM *
140010696SDavid.Hollister@Sun.COM * The basic algorithm is as follows:
140110696SDavid.Hollister@Sun.COM *
140210696SDavid.Hollister@Sun.COM * PMCS_MAX_IO_COMPS_PER_INTR: The maximum number of I/O completions per
140310696SDavid.Hollister@Sun.COM * I/O completion interrupt. We won't increase the interrupt coalescing
140410696SDavid.Hollister@Sun.COM * timer if we're already processing this many completions per interrupt
140510696SDavid.Hollister@Sun.COM * beyond the threshold.
140610696SDavid.Hollister@Sun.COM *
140710696SDavid.Hollister@Sun.COM * Values in io_intr_coal structure:
140810696SDavid.Hollister@Sun.COM *
140910696SDavid.Hollister@Sun.COM * intr_latency: The average number of nsecs between interrupts during
141010696SDavid.Hollister@Sun.COM * the echo test. Used to help determine whether to increase the coalescing
141110696SDavid.Hollister@Sun.COM * timer.
141210696SDavid.Hollister@Sun.COM *
141310696SDavid.Hollister@Sun.COM * intr_threshold: Calculated number of interrupts beyond which we may
141410696SDavid.Hollister@Sun.COM * increase the timer. This value is calculated based on the calculated
141510696SDavid.Hollister@Sun.COM * interrupt latency during the ECHO test and the current value of the
141610696SDavid.Hollister@Sun.COM * coalescing timer.
141710696SDavid.Hollister@Sun.COM *
141810696SDavid.Hollister@Sun.COM * nsecs_between_intrs: Total number of nsecs between all the interrupts
141910696SDavid.Hollister@Sun.COM * in the current timeslice.
142010696SDavid.Hollister@Sun.COM *
142110696SDavid.Hollister@Sun.COM * last_io_comp: Time of the last I/O interrupt.
142210696SDavid.Hollister@Sun.COM *
142310696SDavid.Hollister@Sun.COM * num_io_completions: Number of I/O completions during the slice
142410696SDavid.Hollister@Sun.COM *
142510696SDavid.Hollister@Sun.COM * num_intrs: Number of I/O completion interrupts during the slice
142610696SDavid.Hollister@Sun.COM *
142710696SDavid.Hollister@Sun.COM * max_io_completions: Number of times we hit >= PMCS_MAX_IO_COMPS_PER_INTR
142810696SDavid.Hollister@Sun.COM * during interrupt processing.
142910696SDavid.Hollister@Sun.COM *
143010696SDavid.Hollister@Sun.COM * PMCS_MAX_IO_COMPS_LOWAT_SHIFT/HIWAT_SHIFT
143110696SDavid.Hollister@Sun.COM * Low and high marks used to determine whether we processed enough interrupts
143210696SDavid.Hollister@Sun.COM * that contained the maximum number of I/O completions to warrant increasing
143310696SDavid.Hollister@Sun.COM * the timer
143410696SDavid.Hollister@Sun.COM *
143510696SDavid.Hollister@Sun.COM * intr_coal_timer: The current value of the register (in usecs)
143610696SDavid.Hollister@Sun.COM *
143710696SDavid.Hollister@Sun.COM * timer_on: B_TRUE means we are using the timer
143810696SDavid.Hollister@Sun.COM *
143910696SDavid.Hollister@Sun.COM * The timer is increased if we processed more than intr_threshold interrupts
144010696SDavid.Hollister@Sun.COM * during the quantum and the number of interrupts containing the maximum
144110696SDavid.Hollister@Sun.COM * number of I/O completions is between PMCS_MAX_IO_COMPS_LOWAT_SHIFT and
144210696SDavid.Hollister@Sun.COM * _HIWAT_SHIFT
144310696SDavid.Hollister@Sun.COM *
144410696SDavid.Hollister@Sun.COM * If the average time between completions is greater than twice
144510696SDavid.Hollister@Sun.COM * the current timer value, the timer value is decreased.
144610696SDavid.Hollister@Sun.COM *
144710696SDavid.Hollister@Sun.COM * If we did not take any interrupts during a quantum, we turn the timer off.
144810696SDavid.Hollister@Sun.COM */
144910696SDavid.Hollister@Sun.COM void
pmcs_check_intr_coal(void * arg)145010696SDavid.Hollister@Sun.COM pmcs_check_intr_coal(void *arg)
145110696SDavid.Hollister@Sun.COM {
145210696SDavid.Hollister@Sun.COM pmcs_hw_t *pwp = (pmcs_hw_t *)arg;
145310696SDavid.Hollister@Sun.COM uint32_t avg_nsecs;
145412078SJesse.Butler@Sun.COM clock_t lbolt, ret;
145510696SDavid.Hollister@Sun.COM pmcs_io_intr_coal_t *ici;
145610696SDavid.Hollister@Sun.COM
145710696SDavid.Hollister@Sun.COM ici = &pwp->io_intr_coal;
145810696SDavid.Hollister@Sun.COM mutex_enter(&pwp->ict_lock);
145910696SDavid.Hollister@Sun.COM while (ici->stop_thread == B_FALSE) {
146010696SDavid.Hollister@Sun.COM /*
146110696SDavid.Hollister@Sun.COM * Wait for next time quantum... collect stats
146210696SDavid.Hollister@Sun.COM */
146312078SJesse.Butler@Sun.COM lbolt = ddi_get_lbolt();
146412078SJesse.Butler@Sun.COM while (ici->stop_thread == B_FALSE) {
146512078SJesse.Butler@Sun.COM ret = cv_timedwait(&pwp->ict_cv, &pwp->ict_lock,
146612078SJesse.Butler@Sun.COM lbolt + ici->quantum);
146712078SJesse.Butler@Sun.COM if (ret == -1) {
146812078SJesse.Butler@Sun.COM break;
146912078SJesse.Butler@Sun.COM }
147012078SJesse.Butler@Sun.COM }
147110696SDavid.Hollister@Sun.COM
147210696SDavid.Hollister@Sun.COM if (ici->stop_thread == B_TRUE) {
147310696SDavid.Hollister@Sun.COM continue;
147410696SDavid.Hollister@Sun.COM }
147510696SDavid.Hollister@Sun.COM
147610696SDavid.Hollister@Sun.COM DTRACE_PROBE1(pmcs__check__intr__coal, pmcs_io_intr_coal_t *,
147710696SDavid.Hollister@Sun.COM &pwp->io_intr_coal);
147810696SDavid.Hollister@Sun.COM
147910696SDavid.Hollister@Sun.COM /*
148010696SDavid.Hollister@Sun.COM * Determine whether to adjust timer
148110696SDavid.Hollister@Sun.COM */
148210696SDavid.Hollister@Sun.COM if (ici->num_intrs == 0) {
148310696SDavid.Hollister@Sun.COM /*
148410696SDavid.Hollister@Sun.COM * If timer is off, nothing more to do.
148510696SDavid.Hollister@Sun.COM */
148610696SDavid.Hollister@Sun.COM if (!pwp->io_intr_coal.timer_on) {
148710696SDavid.Hollister@Sun.COM continue;
148810696SDavid.Hollister@Sun.COM }
148910696SDavid.Hollister@Sun.COM
149010696SDavid.Hollister@Sun.COM /*
149110696SDavid.Hollister@Sun.COM * No interrupts. Turn off the timer.
149210696SDavid.Hollister@Sun.COM */
149310696SDavid.Hollister@Sun.COM pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_CONTROL, 0);
149410696SDavid.Hollister@Sun.COM
149510696SDavid.Hollister@Sun.COM if (pwp->odb_auto_clear & (1 << PMCS_MSIX_IODONE)) {
149610696SDavid.Hollister@Sun.COM pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR,
149710696SDavid.Hollister@Sun.COM pwp->odb_auto_clear);
149810696SDavid.Hollister@Sun.COM }
149910696SDavid.Hollister@Sun.COM
150010696SDavid.Hollister@Sun.COM ici->timer_on = B_FALSE;
150110696SDavid.Hollister@Sun.COM ici->max_io_completions = 0;
150210696SDavid.Hollister@Sun.COM ici->num_intrs = 0;
150310696SDavid.Hollister@Sun.COM ici->int_cleared = B_FALSE;
150410696SDavid.Hollister@Sun.COM ici->num_io_completions = 0;
150510696SDavid.Hollister@Sun.COM DTRACE_PROBE1(pmcs__intr__coalesce__timer__off,
150610696SDavid.Hollister@Sun.COM pmcs_io_intr_coal_t *, ici);
150710696SDavid.Hollister@Sun.COM continue;
150810696SDavid.Hollister@Sun.COM }
150910696SDavid.Hollister@Sun.COM
151010696SDavid.Hollister@Sun.COM avg_nsecs = ici->nsecs_between_intrs / ici->num_intrs;
151110696SDavid.Hollister@Sun.COM
151210696SDavid.Hollister@Sun.COM if ((ici->num_intrs > ici->intr_threshold) &&
151310696SDavid.Hollister@Sun.COM (ici->max_io_completions > (ici->num_intrs >>
151410696SDavid.Hollister@Sun.COM PMCS_MAX_IO_COMPS_LOWAT_SHIFT)) &&
151510696SDavid.Hollister@Sun.COM (ici->max_io_completions < (ici->num_intrs >>
151610696SDavid.Hollister@Sun.COM PMCS_MAX_IO_COMPS_HIWAT_SHIFT))) {
151710696SDavid.Hollister@Sun.COM pmcs_set_intr_coal_timer(pwp, INCREASE_TIMER);
151810696SDavid.Hollister@Sun.COM } else if (avg_nsecs >
151910696SDavid.Hollister@Sun.COM (ici->intr_coal_timer * 1000 * 2)) {
152010696SDavid.Hollister@Sun.COM pmcs_set_intr_coal_timer(pwp, DECREASE_TIMER);
152110696SDavid.Hollister@Sun.COM }
152210696SDavid.Hollister@Sun.COM
152310696SDavid.Hollister@Sun.COM /*
152410696SDavid.Hollister@Sun.COM * Reset values for new sampling period.
152510696SDavid.Hollister@Sun.COM */
152610696SDavid.Hollister@Sun.COM ici->max_io_completions = 0;
152710696SDavid.Hollister@Sun.COM ici->nsecs_between_intrs = 0;
152810696SDavid.Hollister@Sun.COM ici->num_intrs = 0;
152910696SDavid.Hollister@Sun.COM ici->num_io_completions = 0;
153011694SDavid.Hollister@Sun.COM
153111694SDavid.Hollister@Sun.COM /*
153211694SDavid.Hollister@Sun.COM * If a firmware event log file is configured, check to see
153311694SDavid.Hollister@Sun.COM * if it needs to be written to the file. We do this here
153411694SDavid.Hollister@Sun.COM * because writing to a file from a callout thread (i.e.
153511694SDavid.Hollister@Sun.COM * from the watchdog timer) can cause livelocks.
153611694SDavid.Hollister@Sun.COM */
153711694SDavid.Hollister@Sun.COM if (pwp->fwlog_file) {
153812085SDavid.Hollister@Sun.COM mutex_exit(&pwp->ict_lock);
153911694SDavid.Hollister@Sun.COM pmcs_gather_fwlog(pwp);
154012085SDavid.Hollister@Sun.COM mutex_enter(&pwp->ict_lock);
154111694SDavid.Hollister@Sun.COM }
154210696SDavid.Hollister@Sun.COM }
154310696SDavid.Hollister@Sun.COM
154410696SDavid.Hollister@Sun.COM mutex_exit(&pwp->ict_lock);
154510696SDavid.Hollister@Sun.COM thread_exit();
154610696SDavid.Hollister@Sun.COM }
154710696SDavid.Hollister@Sun.COM
154810696SDavid.Hollister@Sun.COM void
pmcs_iodone_intr(pmcs_hw_t * pwp)154910696SDavid.Hollister@Sun.COM pmcs_iodone_intr(pmcs_hw_t *pwp)
155010696SDavid.Hollister@Sun.COM {
155110696SDavid.Hollister@Sun.COM char local[PMCS_QENTRY_SIZE << 1];
155210696SDavid.Hollister@Sun.COM pmcs_iocomp_cb_t *ioccb;
155310696SDavid.Hollister@Sun.COM uint32_t w0, ci, pi, nbuf, lim = 0, niodone = 0, iomb_opcode;
155410696SDavid.Hollister@Sun.COM size_t amt;
155510696SDavid.Hollister@Sun.COM uint32_t *ptr;
155610696SDavid.Hollister@Sun.COM hrtime_t curtime = gethrtime();
155710696SDavid.Hollister@Sun.COM
155810696SDavid.Hollister@Sun.COM ci = pmcs_rd_oqci(pwp, PMCS_OQ_IODONE);
155910696SDavid.Hollister@Sun.COM pi = pmcs_rd_oqpi(pwp, PMCS_OQ_IODONE);
156010696SDavid.Hollister@Sun.COM
156110696SDavid.Hollister@Sun.COM while (ci != pi) {
156210696SDavid.Hollister@Sun.COM OQLIM_CHECK(pwp, lim);
156310696SDavid.Hollister@Sun.COM ptr = GET_OQ_ENTRY(pwp, PMCS_OQ_IODONE, ci, 0);
156410696SDavid.Hollister@Sun.COM w0 = LE_32(ptr[0]);
156510696SDavid.Hollister@Sun.COM VALID_IOMB_CHECK(pwp, w0, ptr, ci, pi);
156610696SDavid.Hollister@Sun.COM WRONG_OBID_CHECK(pwp, w0, PMCS_OQ_IODONE);
156710696SDavid.Hollister@Sun.COM iomb_opcode = (w0 & PMCS_IOMB_OPCODE_MASK);
156810696SDavid.Hollister@Sun.COM
156910696SDavid.Hollister@Sun.COM if ((iomb_opcode == PMCOUT_SSP_COMPLETION) ||
157010696SDavid.Hollister@Sun.COM (iomb_opcode == PMCOUT_SATA_COMPLETION)) {
157110696SDavid.Hollister@Sun.COM ioccb =
157210696SDavid.Hollister@Sun.COM kmem_cache_alloc(pwp->iocomp_cb_cache, KM_NOSLEEP);
157310696SDavid.Hollister@Sun.COM if (ioccb == NULL) {
157411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL,
157511048SDavid.Hollister@Sun.COM "%s: kmem_cache_alloc failed", __func__);
157610696SDavid.Hollister@Sun.COM break;
157710696SDavid.Hollister@Sun.COM }
157810696SDavid.Hollister@Sun.COM
157910696SDavid.Hollister@Sun.COM COPY_OUTBOUND(pwp, w0, ioccb->iomb, nbuf, amt, ptr,
158010696SDavid.Hollister@Sun.COM PMCS_OQ_IODONE, ci);
158110696SDavid.Hollister@Sun.COM
158210696SDavid.Hollister@Sun.COM niodone++;
158310696SDavid.Hollister@Sun.COM pmcs_process_io_completion(pwp, ioccb, amt);
158410696SDavid.Hollister@Sun.COM } else {
158510696SDavid.Hollister@Sun.COM COPY_OUTBOUND(pwp, w0, local, nbuf, amt, ptr,
158610696SDavid.Hollister@Sun.COM PMCS_OQ_IODONE, ci);
158710696SDavid.Hollister@Sun.COM
158810696SDavid.Hollister@Sun.COM switch (iomb_opcode) {
158910696SDavid.Hollister@Sun.COM case PMCOUT_ECHO:
159010696SDavid.Hollister@Sun.COM pmcs_process_echo_completion(pwp, local, amt);
159110696SDavid.Hollister@Sun.COM break;
159210696SDavid.Hollister@Sun.COM case PMCOUT_SATA_EVENT:
159310696SDavid.Hollister@Sun.COM pmcs_process_sata_event(pwp, local, amt);
159410696SDavid.Hollister@Sun.COM break;
159510696SDavid.Hollister@Sun.COM case PMCOUT_SSP_EVENT:
159610696SDavid.Hollister@Sun.COM pmcs_process_ssp_event(pwp, local, amt);
159710696SDavid.Hollister@Sun.COM break;
159810696SDavid.Hollister@Sun.COM case PMCOUT_SKIP_ENTRIES:
159911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL,
160010696SDavid.Hollister@Sun.COM "%s: skip %d entries", __func__, nbuf);
160110696SDavid.Hollister@Sun.COM break;
160210696SDavid.Hollister@Sun.COM default:
160310696SDavid.Hollister@Sun.COM (void) snprintf(local, sizeof (local),
160410696SDavid.Hollister@Sun.COM "%s: unhandled message", __func__);
160510696SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG, local,
160610696SDavid.Hollister@Sun.COM ptr);
160710696SDavid.Hollister@Sun.COM break;
160810696SDavid.Hollister@Sun.COM }
160910696SDavid.Hollister@Sun.COM }
161010696SDavid.Hollister@Sun.COM
161110696SDavid.Hollister@Sun.COM STEP_OQ_ENTRY(pwp, PMCS_OQ_IODONE, ci, nbuf);
161210696SDavid.Hollister@Sun.COM }
161310696SDavid.Hollister@Sun.COM
161410696SDavid.Hollister@Sun.COM if (lim != 0) {
161510696SDavid.Hollister@Sun.COM SYNC_OQ_ENTRY(pwp, PMCS_OQ_IODONE, ci, pi);
161610696SDavid.Hollister@Sun.COM }
161710696SDavid.Hollister@Sun.COM
161810696SDavid.Hollister@Sun.COM /*
161910696SDavid.Hollister@Sun.COM * Update the interrupt coalescing timer check stats and run
162010696SDavid.Hollister@Sun.COM * completions for queued up commands.
162110696SDavid.Hollister@Sun.COM */
162210696SDavid.Hollister@Sun.COM
162310696SDavid.Hollister@Sun.COM if (niodone > 0) {
162410696SDavid.Hollister@Sun.COM /*
162510696SDavid.Hollister@Sun.COM * If we can't get the lock, then completions are either
162610696SDavid.Hollister@Sun.COM * already running or will be scheduled to do so shortly.
162710696SDavid.Hollister@Sun.COM */
162810696SDavid.Hollister@Sun.COM if (mutex_tryenter(&pwp->cq_lock) != 0) {
162910696SDavid.Hollister@Sun.COM PMCS_CQ_RUN_LOCKED(pwp);
163010696SDavid.Hollister@Sun.COM mutex_exit(&pwp->cq_lock);
163110696SDavid.Hollister@Sun.COM }
163210696SDavid.Hollister@Sun.COM
163310696SDavid.Hollister@Sun.COM mutex_enter(&pwp->ict_lock);
163410696SDavid.Hollister@Sun.COM pwp->io_intr_coal.nsecs_between_intrs +=
163510696SDavid.Hollister@Sun.COM curtime - pwp->io_intr_coal.last_io_comp;
163610696SDavid.Hollister@Sun.COM pwp->io_intr_coal.num_intrs++;
163710696SDavid.Hollister@Sun.COM pwp->io_intr_coal.num_io_completions += niodone;
163810696SDavid.Hollister@Sun.COM if (niodone >= PMCS_MAX_IO_COMPS_PER_INTR) {
163910696SDavid.Hollister@Sun.COM pwp->io_intr_coal.max_io_completions++;
164010696SDavid.Hollister@Sun.COM }
164110696SDavid.Hollister@Sun.COM pwp->io_intr_coal.last_io_comp = gethrtime();
164210696SDavid.Hollister@Sun.COM mutex_exit(&pwp->ict_lock);
164310696SDavid.Hollister@Sun.COM }
164410696SDavid.Hollister@Sun.COM }
164510696SDavid.Hollister@Sun.COM
164610696SDavid.Hollister@Sun.COM void
pmcs_event_intr(pmcs_hw_t * pwp)164710696SDavid.Hollister@Sun.COM pmcs_event_intr(pmcs_hw_t *pwp)
164810696SDavid.Hollister@Sun.COM {
164910696SDavid.Hollister@Sun.COM char local[PMCS_QENTRY_SIZE << 1];
165010696SDavid.Hollister@Sun.COM uint32_t w0, ci, pi, nbuf, lim = 0;
165110696SDavid.Hollister@Sun.COM size_t amt;
165210696SDavid.Hollister@Sun.COM uint32_t *ptr;
165310696SDavid.Hollister@Sun.COM
165410696SDavid.Hollister@Sun.COM ci = pmcs_rd_oqci(pwp, PMCS_OQ_EVENTS);
165510696SDavid.Hollister@Sun.COM pi = pmcs_rd_oqpi(pwp, PMCS_OQ_EVENTS);
165610696SDavid.Hollister@Sun.COM
165710696SDavid.Hollister@Sun.COM while (ci != pi) {
165810696SDavid.Hollister@Sun.COM OQLIM_CHECK(pwp, lim);
165910696SDavid.Hollister@Sun.COM ptr = GET_OQ_ENTRY(pwp, PMCS_OQ_EVENTS, ci, 0);
166010696SDavid.Hollister@Sun.COM w0 = LE_32(ptr[0]);
166110696SDavid.Hollister@Sun.COM VALID_IOMB_CHECK(pwp, w0, ptr, ci, pi);
166210696SDavid.Hollister@Sun.COM WRONG_OBID_CHECK(pwp, w0, PMCS_OQ_EVENTS);
166310696SDavid.Hollister@Sun.COM COPY_OUTBOUND(pwp, w0, local, nbuf, amt, ptr,
166410696SDavid.Hollister@Sun.COM PMCS_OQ_EVENTS, ci);
166510696SDavid.Hollister@Sun.COM
166610696SDavid.Hollister@Sun.COM switch (w0 & PMCS_IOMB_OPCODE_MASK) {
166710696SDavid.Hollister@Sun.COM case PMCOUT_ECHO:
166810696SDavid.Hollister@Sun.COM pmcs_process_echo_completion(pwp, local, amt);
166910696SDavid.Hollister@Sun.COM break;
167010696SDavid.Hollister@Sun.COM case PMCOUT_SATA_EVENT:
167110696SDavid.Hollister@Sun.COM pmcs_process_sata_event(pwp, local, amt);
167210696SDavid.Hollister@Sun.COM break;
167310696SDavid.Hollister@Sun.COM case PMCOUT_SSP_EVENT:
167410696SDavid.Hollister@Sun.COM pmcs_process_ssp_event(pwp, local, amt);
167510696SDavid.Hollister@Sun.COM break;
167610696SDavid.Hollister@Sun.COM case PMCOUT_GENERAL_EVENT:
167710696SDavid.Hollister@Sun.COM pmcs_process_general_event(pwp, ptr);
167810696SDavid.Hollister@Sun.COM break;
167910696SDavid.Hollister@Sun.COM case PMCOUT_DEVICE_HANDLE_REMOVED:
168010696SDavid.Hollister@Sun.COM {
168110696SDavid.Hollister@Sun.COM uint32_t port = IOP_EVENT_PORTID(LE_32(ptr[1]));
168210696SDavid.Hollister@Sun.COM uint32_t did = LE_32(ptr[2]);
168311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
168410696SDavid.Hollister@Sun.COM "PortID 0x%x device_id 0x%x removed", port, did);
168510696SDavid.Hollister@Sun.COM break;
168610696SDavid.Hollister@Sun.COM }
168710696SDavid.Hollister@Sun.COM case PMCOUT_SAS_HW_EVENT:
168810696SDavid.Hollister@Sun.COM if (nbuf > 1) {
168911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL,
169010696SDavid.Hollister@Sun.COM "multiple SAS HW_EVENT (%d) responses "
169110696SDavid.Hollister@Sun.COM "in EVENT OQ", nbuf);
169210696SDavid.Hollister@Sun.COM }
169310696SDavid.Hollister@Sun.COM pmcs_process_sas_hw_event(pwp, local, PMCS_QENTRY_SIZE);
169410696SDavid.Hollister@Sun.COM break;
169510696SDavid.Hollister@Sun.COM case PMCOUT_FW_FLASH_UPDATE:
169610696SDavid.Hollister@Sun.COM case PMCOUT_GET_TIME_STAMP:
169710696SDavid.Hollister@Sun.COM case PMCOUT_GET_DEVICE_STATE:
169810696SDavid.Hollister@Sun.COM case PMCOUT_SET_DEVICE_STATE:
169910696SDavid.Hollister@Sun.COM case PMCOUT_SAS_DIAG_EXECUTE:
170010696SDavid.Hollister@Sun.COM pmcs_process_completion(pwp, local, amt);
170110696SDavid.Hollister@Sun.COM break;
170210696SDavid.Hollister@Sun.COM case PMCOUT_SKIP_ENTRIES:
170311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL,
170411048SDavid.Hollister@Sun.COM "%s: skip %d entries", __func__, nbuf);
170510696SDavid.Hollister@Sun.COM break;
170610696SDavid.Hollister@Sun.COM default:
170710696SDavid.Hollister@Sun.COM (void) snprintf(local, sizeof (local),
170810696SDavid.Hollister@Sun.COM "%s: unhandled message", __func__);
170910696SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG, local, ptr);
171010696SDavid.Hollister@Sun.COM break;
171110696SDavid.Hollister@Sun.COM }
171210696SDavid.Hollister@Sun.COM STEP_OQ_ENTRY(pwp, PMCS_OQ_EVENTS, ci, nbuf);
171310696SDavid.Hollister@Sun.COM }
171410696SDavid.Hollister@Sun.COM if (lim) {
171510696SDavid.Hollister@Sun.COM SYNC_OQ_ENTRY(pwp, PMCS_OQ_EVENTS, ci, pi);
171610696SDavid.Hollister@Sun.COM }
171710696SDavid.Hollister@Sun.COM }
171810696SDavid.Hollister@Sun.COM
171910696SDavid.Hollister@Sun.COM void
pmcs_timed_out(pmcs_hw_t * pwp,uint32_t htag,const char * func)172010696SDavid.Hollister@Sun.COM pmcs_timed_out(pmcs_hw_t *pwp, uint32_t htag, const char *func)
172110696SDavid.Hollister@Sun.COM {
172210696SDavid.Hollister@Sun.COM #ifdef DEBUG
172310696SDavid.Hollister@Sun.COM hrtime_t now = gethrtime();
172410696SDavid.Hollister@Sun.COM int i;
172510696SDavid.Hollister@Sun.COM
172610696SDavid.Hollister@Sun.COM for (i = 0; i < 256; i++) {
172710696SDavid.Hollister@Sun.COM if (pwp->ftags[i] == htag) {
172811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
172910696SDavid.Hollister@Sun.COM "Inbound msg (tag 0x%8x) timed out - "
173010696SDavid.Hollister@Sun.COM "was started %llu ns ago in %s:%d",
173110696SDavid.Hollister@Sun.COM htag, (unsigned long long) (now - pwp->ftime[i]),
173210696SDavid.Hollister@Sun.COM func, pwp->ftag_lines[i]);
173310696SDavid.Hollister@Sun.COM return;
173410696SDavid.Hollister@Sun.COM }
173510696SDavid.Hollister@Sun.COM }
173610696SDavid.Hollister@Sun.COM #endif
173711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
173810696SDavid.Hollister@Sun.COM "Inbound Message (tag 0x%08x) timed out- was started in %s",
173910696SDavid.Hollister@Sun.COM htag, func);
174010696SDavid.Hollister@Sun.COM }
1741